【Hadoop基础教程】9、Hadoop之倒排索引

开发环境

硬件环境:Centos 6.5 服务器4台(一台为Master节点,三台为Slave节点) 软件环境:Java 1.7.0_45、hadoop-1.2.1

1、倒排索引

倒排索引是文档检索系统中最常用的数据结构,被广泛用于全文搜索引擎。它主要是用来存储某个单词(或词组)在一个文档或一组文档的存储位置的映射,即提供了一种根据内容来查找文档的方式。由于不是根据文档来确定文档所包含的内容,而是进行了相反的操作(根据关键字来查找文档),因而称为倒排索引(Inverted Index)。通常情况下,倒排索引由一个单词(词组)以及相关的文档列表(标示文档的ID号,,或者是指定文档所在位置的URI)组成,如下图所示:

从上图可以看出,单词1出现在{文档1、文档4、文档13、……}中,单词2出现在{文档3、文档5、文档15、…..}中,而单词3出现在{文档1、文档8、文档20、….}中,还需要给每个文档添加一个权值,用来指出每个文档与搜素内容相关的相关度,如下图所示:

最常用的是使用词频作为权重,即记录单词在文档中出现的次数了。以英文为例,如下图所示,索引文件中的“MapReduce”一行表示:“MapReduce”这个单词在文本T0中出现过1次,T1中出现过1次,T2中出现过2次。当搜索条件为“MapReduce”、“is”、“simple”时,对应的集合为:{T0,T1,T2}∩{ T0,T1}∩{ T0,T1}={ T0,T1},即文本T0和T1包含所要索引的单词,而且只有T0是连续的。

2、Map过程

首先使用默认的TextInputFormat类对输入文件进行处理,得到文本中每行的偏移量及其内容。显然,Map过程首先必须分析输入的< key,value>对,得到倒排索引中需要的三个信息:单词、文档URI和词频,如下图所示。这里存在两个问题:第一,< key,value>对只能有两个值,在不使用Hadoop自定义数据类型的情况下,需要根据情况将其中两个值合并成一个值,作为key或value值;第二,通过一个Reduce过程无法同时完成词频统计和文档列表,所以必须增加一个Combine过程完成词频统计。

这里将单词和URI组成Key值(如“MapReduce :1.txt”),将词频作为value,这样做的好处是可以利用MapReduce框架自带的Map端排序,将同一文档的相同单词的词频组成列表,传递给Combine过程,实现类似于WordCount的功能。 Map过程核心代码实现如下,详细源码请参考:InvertedIndex\src\com\zonesion\hdfs\InvertedIndex.java。

public static class InvertedIndexMapper extendsMapper<Object,Text,Object,Text>{private Text keyInfo = new Text();//存储单词和URI的组合private Text valueInfo = new Text();//存储词频private FileSplit split;//存储Split对象@Overridepublic void map(Object key, Text value, Context context)throws IOException, InterruptedException {split = (FileSplit)context.getInputSplit();StringTokenizer itr = new StringTokenizer(value.toString());while(itr.hasMoreTokens()){//key值由单词和URI组成keyInfo.set(itr.nextToken()+”:”+split.getPath().toString());valueInfo.set(“1”);context.write(keyInfo, valueInfo);//输出:<key,value>—<“MapReduce:1.txt”,1>}}}3、Combine过程

经过map方法处理之后,Combine过程将key值相同的value值累加,得到一个单词在文档中的词频,如下图所示。如果直接将Map的输出结果作为Reduce过程的输入,在Shuffle过程时将面临一个问题:所有具有相同单词的记录(由单词、URI和词频组成)应该交由同一个Reduce处理,但当前key值无法保证这一点,所以必须修改key值和value值。这次将单词作为key值,URI和词频作为value值。这样做的好处是可以利用MapReduce框架默认的HashPartitioner类完成Shuffle过程,将相同单词的所有记录发送给同一个Reducer处理。

Combine过程核心代码实现如下,详细源码请参考:InvertedIndex\src\com\zonesion\hdfs\InvertedIndex.java。

public static class InvertedIndexCombinerextends Reducer<Text, Text, Text, Text>{private Text info = new Text();@Overrideprotected void reduce(Text key, Iterable<Text> values,Context context)throws IOException, InterruptedException {//输入:<key,value>—<“MapReduce:1.txt”,list(1,1,1,1)>int sum = 0;for(Text value : values){sum += Integer.parseInt(value.toString());}int splitIndex = key.toString().indexOf(“:”);info.set(key.toString().substring(splitIndex+1)+”:”+sum);key.set(key.toString().substring(0,splitIndex));context.write(key, info);//输出:<key,value>—-<“Mapreduce”,”0.txt:2″>}}4、Reduce过程

经过上述两个过程后,Reduce过程只需要将相同key值的value值组合成倒排索引文件所需的格式即可,剩下的事情就可以直接交给MapReduce框架处理了,如下图所示。

Reduce过程核心代码实现如下,详细源码请参考:InvertedIndex\src\com\zonesion\hdfs\InvertedIndex.java。

public static class InvertedIndexReducerextends Reducer<Text, Text, Text, Text>{private Text result = new Text();@Overrideprotected void reduce(Text key, Iterable<Text> values,Context context)throws IOException, InterruptedException {//输入:<“MapReduce”,list(“0.txt:1″,”1.txt:1″,”2.txt:1″)>String fileList = new String();for(Text value : values){//value=”0.txt:1″fileList += value.toString()+”;”;}result.set(fileList);context.write(key, result);//输出:<“MapReduce”,”0.txt:1,1.txt:1,2.txt:1″>}}5、驱动实现当我要取的时候,你淘气的躲开了,

【Hadoop基础教程】9、Hadoop之倒排索引

相关文章:

你感兴趣的文章:

标签云: