暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Mapreduce经典案例:倒排索引

小甜菜Pro 2023-03-08
533

倒排索引是用来存储某个单词或词组在一组文档中存储位置的映射,它提供了可以根据内容来查找文档的方式,而不是根据文档来确定内容,因此称为倒排索引。
在实际应用中,还需要给每个文档添加一个权值,用来指出每个文档与搜索内容的相关程度,最常用的是使用词频作为权重,用户在搜索相关文档时,就会把权重高的文档优先推荐给用户。
下面是两个文本文件file1.txt
file2.txt
,对它们进行加权倒排索引,整体思想流程如下图所示。

从以上图中可以看出,在Map和Reduce中间使用了一个combine
的过程,它可以理解为是在map阶段的reduce操作,是对单个map任务的输出结果数据进行合并的操作。
下面进行代码实现,以下代码所实现的功能均为以上图中所显示的步骤,若读者对于代码及讲解有所疑惑,可以多观察以上图示,加以理解。
在Map阶段,将传来的数据行使用一个空格进行切分(使用一个空格切分是由于每个数据行中的每个单词之间使用的是一个空格分隔),并将每个数据存放在名为words
的字符串数组中,同时利用FileSplit
类获取该数据行所在的文件名称,将这个文件名称存放在名为fileName
的字符串变量中,接下来遍历words
字符串数组,将数组中的每个元素与文件名称进行拼接(拼接时使用word :filename
的格式,例如:hadoop:file1.txt
),拼接后的数据为设置KEY
的值,最后将KEY
VAL
写入下一阶段(这里的VAL
是一个字符1,是为了在combine
阶段进行计数)。
以下是Map阶段详细代码。
public class InvertedIndexMapper extends Mapper<LongWritableTextTextText{
    private static final Text KEY = new Text();
    private static final Text VAL = new Text("1");
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String[] words = value.toString().split(" ");
        FileSplit fileSplit = (FileSplit) context.getInputSplit();
        String fileName = fileSplit.getPath().getName();
        for (String word : words) {
            KEY.set(word + ":" + fileName);
            context.write(KEY, VAL);
        }
    }
}

在Combine阶段,与Reduce阶段类似,同样继承Reducer
类。在该阶段中将Map阶段传来的每个key中的value进行计数,由于Map阶段最终的输出VAL
类型为Text
,因此要使用toString()
方法转为字符串类型,并同时使用Integer
类的parseInt()
方法继续转为int
类型,再进行累加,但是这样操作较为繁琐,由于这里只是为了实现计数功能,并且Map阶段最终的输出VAL
为字符串1,因此可以直接使用sum ++
语句进行计数。
接下来通过key提取出单词和文件名称(这是因为在Map阶段传来的key为word :filename
格式),最后将单词word
作为KEY
,文件名称filename
与计数sum
拼接后作为VAL
传入下一阶段。
到这里读者是否有些疑惑:既然在Combine阶段对value进行计数,那么为什么不将Map阶段中的输出VAL
设置成IntWritable
类型呢?这样是不是就可以不用在Combine阶段进行两次转变数据类型,而是直接使用get()
方法得到数字1呢?
对于以上问题,答案肯定是否定的!这里我们要清楚的知道Combine阶段只是Map阶段的Reduce操作,它发生在Map端,对数据进行局部聚合处理,Combine的输入是Map的输出,Combine的输出是Reduce的输入,而Map的输出和Reduce的输入是一致的,因此Combine的输入输出是一致的,并且为Map的输出类型。
若将该案例Map阶段的输出类型调整为<Text, IntWritable>
,那么Combine阶段的输入输出类型为<Text, IntWritable, Text, IntWritable>
,而Reduce阶段的输入则为<Text, IntWritable>
,但是在Combine阶段的输出类型必须为<Text, Text>
类型(输出的key为单词word
,value为文件名称和计数的拼接数据filename:sum
,因此只能使用Text
类型传递),所以Map阶段的输出类型也必须为<Text, Text>
类型,这样就解释清楚以上问题的答案了。
以下是Combine阶段详细代码。
public class InvertedIndexCombiner extends Reducer<TextTextTextText{
 private static final Text KEY = new Text();
 private static final Text VAL = new Text();
 protected void reduce(Text key, Iterable<Text> value, Context context) throws IOException, InterruptedException {
  int sum = 0;
  for (Text v : value) sum += Integer.parseInt(v.toString());
  String[] line = key.toString().split(":");
  String word = line[0];
  String filename = line[1];
  KEY.set(word);
  VAL.set(filename + ":" + sum);
  context.write(KEY, VAL);
 }
}

在Reduce阶段,实现的主要功能为将相同key的value进行追加操作,这里为了方便所以使用StringBuilder
类型,将拼接后的数据作为VAL
,而KEY
依然为上一阶段的输出KEY
,最后写入下一阶段。
以下是Reduce阶段详细代码。
public class InvertedIndexReducer extends Reducer<TextTextTextText{
    private static final Text KEY = new Text();
    private static final Text VAL = new Text();
    protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
        StringBuilder fileIndex = new StringBuilder();
        for (Text value : values) fileIndex.append(value.toString()).append(";");
        KEY.set(key);
        VAL.set(fileIndex.toString());
        context.write(KEY, VAL);
    }
}

接下来是启动类,主要用来设置Mapper类、Combiner类和Reducer类以及输入输出类型和文件的输入输出路径,这里不再详细说明,具体实现过程请在公众号后台回复关键词20230308
获取详细代码。
最后将代码进行打包(具体如何打包请参考本公众号中如何使用IDEA将Maven项目进行打包?
文章,或在本公众号后台回复关键词20230117
获取该图文),并上传至服务器中/usr/local/hadoop/jar
路径下,同时开启HDFS和Yarn集群,将本文开头的file1.txt
文件和file2.txt
文件上传至服务器中/root
路径下。
在服务器中使用$HADOOP_HOME/bin/hadoop fs -mkdir -p /Mapreduce/InvertedIndex/input
命令在HDFS中创建数据上传路径,使用$HADOOP_HOME/bin/hadoop fs -put /root/file* /Mapreduce/InvertedIndex/input
命令将两个示例文件上传至HDFS的/Mapreduce/InvertedIndex/input
路径下。
使用$HADOOP_HOME/bin/hadoop jar /usr/local/hadoop/jar/InvertedIndex.jar /Mapreduce/InvertedIndex/input /Mapreduce/InvertedIndex/output
命令启动MapReduce程序并提交到Yarn集群。

等待运行结束后使用$HADOOP_HOME/bin/hadoop fs -cat /Mapreduce/InvertedIndex/output/part-r-00000
命令查看运行结果,此案例运行结果如下所示。

本文详细讲解了MapReduce倒排索引案例,以上就是本期分享的全部内容,想要获取本文中的全部代码及数据,请在公众号后台回复关键词20230308
,若各位小伙伴有什么不懂的问题也可以直接在公众号后台留言,感谢观看!


参考文献
[1] 黑马程序员. Hadoop大数据技术原理与应用 [M]. 北京:清华大学出版社, 2019.

文章转载自小甜菜Pro,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论