MapReduce经典案例——倒排索引
MapReduce经典案例————倒排索引
一、案例分析
1、倒排索引介绍:
- 倒排索引是文档检索系统中最常用的数据结构,被广泛应用于全文搜索引擎。
- 倒排索引主要用来存储某个单词(或词组)在一组文档中的存储位置的映射,提供了可以根据内容来查找文档的方式,而不是根据文档来确定内容,因此称为倒排索引(Inverted Index)。
- 带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(Inverted File)。
2、案例需求及分析
- 现假设有三个源文件file1.txt、file2.txt和file3.txt,需要使用倒排索引的方式对这三个源文件内容实现倒排索引,并将最后的倒排索引文件输出。
3、测试文档:
-
file1.txt:
MapReduce is simple
-
file2.txt:
MapReduce is powerful is simple
-
file3.txt:
Hello MapReduce bye MapReduce
二、案例实现
1、Map阶段实现:
-
使用Eclipse开发工具打开之前创建的Maven项目HadoopDemo,并且新创建cn.itcast.mr.invertedIndex包,在该路径下编写自定义Mapper类InvertedIndexMapper
-
主要用于将文本中的单词按照空格进行切割,并以冒号拼接,“单词:文档名称”作为key,单词次数作为value,都以文本方式输出至Combine阶段。
-
InvertedIndexMapper.java:
package cn.itcast.mr.invertedIndex; //Map阶段实现 import java.io.IOException; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.lib.input.FileSplit; public class InvertedIndexMapper extends Mapper<LongWritable, Text, Text, Text> { private static Text keyInfo = new Text();// 存储单词和 URL 组合 private static final Text valueInfo = new Text("1");// 存储词频,初始化为1 @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString(); String[] fields = StringUtils.split(line, " ");// 得到字段数组 FileSplit fileSplit = (FileSplit) context.getInputSplit();// 得到这行数据所在的文件切片 String fileName = fileSplit.getPath().getName();// 根据文件切片得到文件名 for (String field : fields) { // key值由单词和URL组成,如“MapReduce:file1” keyInfo.set(field + ":" + fileName); context.write(keyInfo, valueInfo); } } }
2、Combine阶段实现
-
根据Map阶段的输出结果形式,在cn.itcast.mr.InvertedIndex包下,自定义实现Combine阶段的类InvertedIndexCombiner,对每个文档的单词进行词频统计。
-
InvertedIndexCombiner.java:
package cn.itcast.mr.invertedIndex; //Combine阶段实现 import java.io.IOException; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Reducer; public class InvertedIndexCombiner extends Reducer<Text, Text, Text, Text> { private static Text info = new Text(); // 输入: <MapReduce:file3 {1,1,...}> // 输出:<MapReduce file3:2> @Override protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException { int sum = 0;// 统计词频 for (Text value : values) { sum += Integer.parseInt(value.toString()); } int splitIndex = key.toString().indexOf(":"); // 重新设置 value 值由 URL 和词频组成 info.set(key.toString().substring(splitIndex + 1) + ":" + sum); // 重新设置 key 值为单词 key.set(key.toString().substring(0, splitIndex)); context.write(key, info); } }
3、Reduce阶段实现
-
根据Combine阶段的输出结果形式,同样在cn.itcast.mr.InvertedIndex包下,自定义Reducer类InvertedIndexMapper
-
主要用于接收Combine阶段输出的数据,并最终案例倒排索引文件需求的样式,将单词作为key,多个文档名称和词频连接作为value,输出到目标目录。
-
InvertedIndexMapper.java:
package cn.itcast.mr.invertedIndex; //Map阶段实现 import java.io.IOException; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.lib.input.FileSplit; public class InvertedIndexMapper extends Mapper<LongWritable, Text, Text, Text> { private static Text keyInfo = new Text();// 存储单词和 URL 组合 private static final Text valueInfo = new Text("1");// 存储词频,初始化为1 @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString(); String[] fields = StringUtils.split(line, " ");// 得到字段数组 FileSplit fileSplit = (FileSplit) context.getInputSplit();// 得到这行数据所在的文件切片 String fileName = fileSplit.getPath().getName();// 根据文件切片得到文件名 for (String field : fields) { // key值由单词和URL组成,如“MapReduce:file1” keyInfo.set(field + ":" + fileName); context.write(keyInfo, valueInfo); } } }
4、Driver程序主类实现
-
编写MapReduce程序运行主类InvertedIndexDriver,主要用于设置MapReduce工作任务的相关参数,由于本次演示的数据量较小,为了方便、快速进行案例演示,本案例采用了本地运行模式
-
指定的本地E:\hadoop\fileStorage\InvertedIndexes\input目录下的源文件(需要提前准备)实现倒排索引,并将结果输入到本地E:\hadoop\fileStorage\InvertedIndexes\output\index目录下。
-
InvertedIndexDriver.java:
package cn.itcast.mr.invertedIndex; //Driver程序主类实现 import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class InvertedIndexDriver { public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { Configuration conf = new Configuration(); Job job = Job.getInstance(conf); job.setJarByClass(InvertedIndexDriver.class); job.setMapperClass(InvertedIndexMapper.class); job.setCombinerClass(InvertedIndexCombiner.class); job.setReducerClass(InvertedIndexReducer.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(Text.class); FileInputFormat.setInputPaths(job, new Path("E:\\hadoop\\fileStorage\\InvertedIndexes\\input")); // 指定处理完成之后的结果所保存的位置 FileOutputFormat.setOutputPath(job, new Path("E:\\hadoop\\fileStorage\\InvertedIndexes\\output\\index")); // 向 yarn 集群提交这个 job boolean res = job.waitForCompletion(true); System.exit(res ? 0 : 1); } }
三、效果展示
1、查看本地文件:
2、打开part-r-00000:
FINISH