Hadoop编程学习4--倒排索引

倒排索引介绍:

  倒排索引是被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射,是目前几乎所有支持全文索引的搜索引擎都需要依赖的一个数据结构。

倒排索引的设计思路:

Map过程:

  Map过程首先必须分析输入的key,value对,得到倒排索引中需要的三个信息:单词、文档名和词频,将单词和URL组成key值(如”MapReduce:test1.txt”),将词频作为value,这样做可以利用MapReduce框架自带的Map排序,将同一文档的相同单词的词频组成列表,传递给Combine过程

Combine过程:

  经过map方法处理后,Combine过程将key值相同的value值累加,得到一个单词在文档在文档中的词频。在Combine过程中将单词作为key值,文档名和词频组成value值(如”test1.txt:1”)。将相同单词的所有记录发送给同一个Reducer进行处理。

Reduce过程:

  经过上述两个过程后,Reduce过程只需将相同key值的value值组合成倒排索引文件所需的格式即可,其中还需要写一个相加的方法来求取一个单词在所有文档中的频次total。

package org.apache.hadoop.examples;

import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

public class InverseIndex {


    public static class Map extends Mapper<Object,Text,Text,Text>
    {
        private URI[] remoteFiles;  // 存放停用词txt文档统一资源标识符
        private Set<String> stopwords;  //存放停用词

        //重写setup函数
        @Override
        public void setup(Context context) throws IOException
        {
            Configuration conf = context.getConfiguration();
            remoteFiles = Job.getInstance(conf).getCacheFiles();  //获取共享资源里的stop_words.txt
            stopwords = new TreeSet<String>();

            //对于URI列表里的每一个停用词表(假设有多个  停用词表.txt)
            for (int i = 0; i < remoteFiles.length; i++)
            {
                FileInputStream in =new FileInputStream(new Path(remoteFiles[i].getPath()).getName().toString());  //获取文件

                //读取文件的每一行
                Scanner sc =new Scanner(in);  
                while (sc.hasNextLine()) 
                {
                    String line = sc.nextLine();
                    String[] split = line.trim().split(" ");

                    for (int j = 0; j < split.length; j++) 
                    {
                        stopwords.add(split[j]);  //加到set集合中
                    }
                }
            }
        }

        public void map(Object key, Text value, Context context) throws IOException, InterruptedException 
        {
            FileSplit inputSplit = (FileSplit) context.getInputSplit();

            String s=inputSplit.getPath().getName();  //获取文件名
            //System.out.println(s);
            String t[]=value.toString().split("\\W");  //按照非数字、单词进行划分

            int num=0;
            while(num<t.length-1)
            {
                String tt=t[num].toLowerCase();
                num++;
                //如果该词不是停用词
                if(!stopwords.contains(tt))
                    context.write(new Text(tt+","+s), new Text("1"));  // context中写入((token,xxx.txt),1)
            }
        }
    }

    public static class Combine extends Reducer<Text,Text,Text,Text>
    {
         public void reduce(Text key, Iterable<Text> values, Context context)throws IOException, InterruptedException 
         {
             //Map结束后传进来的key为   word,test.txt   value为1
             Text temp=new Text();

             int sum=0;
             for(Text val:values)
             {
                 sum+=1;  //将相同key的value相加
             }

             //然后将key为   abc,test1.txt; sum  -->转变为 abc;  test1.txt,sum  以便于reduce
             String word[]=key.toString().split(",");
             temp.set(word[1]+","+sum);
             context.write(new Text(word[0]), temp);
         }

    }

    public static class Reduce extends Reducer<Text,Text,Text,Text>
    {
        public void reduce(Text key, Iterable<Text> values, Context context)throws IOException, InterruptedException 
         {
             Text temp=new Text();
             int num=0;  //该单词在所有文档中出现的总的次数

             String fileindex=new String();
             for(Text val:values)
             {
                 fileindex+="<"+val.toString()+">"+";";   //所有的<文档,频次>对

                 String word[]=val.toString().split(",");
                 num+=Integer.parseInt(word[1]);   //将每个文档的次数相加
             }

             fileindex+="<total,"+String.valueOf(num)+">.";  //在value串的最后加上total的值

             temp.set(fileindex);
             context.write(key, temp);  //输出结果
         }
    }


    //main方法
    public static void main(String[] args) throws Exception {

        //在学校集群上跑的
        final String OUTPUT_PATH = "/user/201500xx/output";     
        Configuration conf = new Configuration();
        conf.set("fs.defaultFS", "hdfs://xx.xxx.x.xxx:9000");

        Path path = new Path(OUTPUT_PATH);  

        //加载配置文件
        FileSystem fileSystem = path.getFileSystem(conf);

        //输出目录若存在则删除
        if (fileSystem.exists(new Path(OUTPUT_PATH))) 
        {  
           fileSystem.delete(new Path(OUTPUT_PATH),true);  
        }  

        //指定输入输出目录
        String[] otherArgs = new String[]{"/txt_input","/user/201500xx/output"};
        if (otherArgs.length != 2) 
        {
            System.err.println("路径出错");
            System.exit(2);
        }


        //一些初始化
        Job job = Job.getInstance(conf);
        job.addCacheFile(new Path("hdfs://xx.xxx.x.xxx:9000/stop_words/stop_words_eng.txt").toUri());
        job.setJarByClass(InverseIndex.class);
        job.setMapperClass(Map.class);  //初始化为自定义Map类
        job.setCombinerClass(Combine.class); 
        job.setReducerClass(Reduce.class);  //初始化为自定义Reduce类
        job.setOutputKeyClass(Text.class);  //指定输出的key的类型,Text相当于String类
        job.setOutputValueClass(Text.class);  //指定输出的Value的类型,Text相当于String类

        FileInputFormat.addInputPath(job, new Path(otherArgs[0]));  //FileInputFormat指将输入的文件(若大于64M)进行切片划分,每个split切片对应一个Mapper任务
        FileOutputFormat.setOutputPath(job, new Path(otherArgs[1]));
        System.exit(job.waitForCompletion(true) ? 0 : 1);

    }

}

代码有很清晰的注释,看不懂的话可以评论给我,input目录文件及运行结果output目录如下:

DFS文件目录:

DFS文件目录

/txt_input/Dune.txt 部分截图

Dune.txt

/output/part-r-00000

/output2/part-r-00000

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值