基于hadoop搜索引擎实践——生成倒排表文件(四)

来源:互联网 发布:云计算学习视频 编辑:程序博客网 时间:2024/05/22 06:54
2.3 建立倒排表文件(下面原理引用刘鹏hadoop实战)
    在分析完分词,Rank值得计算等问题的解决方案之后,就可以设计相应的MapReduce算法,来建立倒排表,计算,保存Rank和Position等附属信息。
    首先定义倒排表存储信息格式,这是算法的输出目标,也是查询程序从倒排表中获取信息的接口。本系统倒排表的存储格式定义如下:
    (1)倒排表文件(INVERTED_INDEX_FILE)有若干索引词记录组成,每个索引词记录(TERM_RECORD)是有一个索引词(TERM)和包含该词的所有帖子的信息(MULTI_INFO)组成,其中TERM和MULTI_INFO之间用空格隔开。索引词记录按照顺序追加的方式存放,之间用换行符分隔,例如:
    TERM+'\t'+MULTI_INFO+'\r\n'
    (2)在每条索引词记录中,TERM是用分词软件切分出来的一个词。而MULTI_INFO则由多个单条帖子信息(SINGLE_INFO)组成。其中SINGLE_INFO和SINGLE_INFO之间用分号隔开。表示如下:
    MULTI_INFO=SINGLE_INFO1;SINGLE_INFO2;……;SINGLE_INFONn
    (3)单条帖子信息SINGLE_INFO,由帖子ID(DID),索引词语该帖子的Rank值(Rank),索引词在该帖子中出现的位置(POSITIONS)组成,其间用冒号隔开。表示格式如下:
    SINGLE_INFO=DID:RANK:POSITIONS
    (4)SINGLE_INFO中的DID是唯一指定某个帖子的值。本系统选择原文件中帖子行首在原文件中的偏移量(offset)作为帖子ID(DID)。每个帖子的源文件中有唯一的偏移量,且给定一个偏移量offset后,可以通过在源文件中定位offset,并执行readline操作(帖子和帖子之间用换行符间隔的),即可读出这条帖子的信息。
    (5)SINGLE_INFO中的RANK用一个浮点类型的数值表示。
    (6)SINGLE_INFO中的POSITIONS由多个单个位置信息(POSITIONS)组成,之间用百分号隔开。表示格式如下:
    POSITIONS=POSITIONS1%POSITIONS2%……%POSITIONSn
    (7)对于单个位置信息(POSITION),其有标题记标识(ISTITLE),起始位置(START),结束为止(END)组成。之间用竖线隔开,表示格式如下:
    POSITIONS=ISTITLE|START|END
    下面给出一个索引词记录的存储实例
    黑莓    4852292:162.6:1|2|4%0|804|806;42910773:106.26:0|456|458%0|560|562
    该实例说明关键词“黑莓”在ID号为“48522292”和“42910773”的两个帖子中出现。在"48522292"中出现了两次,第一次的位置是在标题中,具体出现在第2-4位;第二次出现在正文中,具体出现在第804-806;该词相对于这个帖子的Rank值为162.6
    由于倒排表的信息来自于每条帖子,这些帖子可以并行地被处理,因此设计了基于MapReduce的并行算法来建立倒排表。算法描述如下

图1-1 MapReduce建立倒排表的流程图
    
建立倒排表的算法流程如下:
    (1)采用Hadoop默认的文件分给方式,将源文件分成若干个小文件,每个Mapper节点一次只处理一个小文件。
    (2)在Map阶段,对于每个小文件采用按行切分的方法输入。每个map函数的输入<key,value>分别是offset和line.其中offset作为key是指输出的这一行的行首相对于整个源文件的偏移量,也就是帖子的DID;line作为value是指输入的一行,在本系统也就是一条帖子,可以从中切分出帖子的TITLE和CONTENT。
    (3)对于输入的每条line(一条帖子),在map函数中切分出TITLE和CONTENT中的每个词(TERM)。对于每个词,根据其出现的情况计算出RANK和POSITIONS,将这些信息封装成一个SINGLE_INFO.输出阶段要发送k次(k是切分出来的TERM的个数),每次发射的key和value是TERM及其对应的SINGLE_INFO。
    (4)经过分区(Partion)阶段,从Map发射出来具有相同的key(TERM)的<key,value>对会分别发到同一个Reducer端,每个reduce函数会处理具有相同TERM的<key,value>对。
    (5)输入到每个reduce函数的相同TERM对应的SINGLE_INFO的数目就是包含这个TERM的帖子的数目,记为num.根据前面介绍的IT-IDF算法,在reduce函数里更新每个SINGLE_INFO里面的Rank值,更新的的公式为RANK=RANK/num.更新后的RANK值就是TERM相对于某个帖子的最终RANK值。
    核心代码如下:
[java] view plain copy
  1. public static class InvertedIndexerMapper extends  
  2.            Mapper<LongWritable, Text, Text, RecordWritable> {  
  3.        public final static Text WORD = new Text();  
  4.        public final static RecordWritable RECORD = new RecordWritable();  
  5.        public final static Gson gson = new Gson();  
  6.        @Override  
  7.        protected void map(LongWritable key, Text value, Context context)  
  8.                throws IOException, InterruptedException {  
  9.            BBS bbs = gson.fromJson(value.toString(), BBS.class);  
  10.            HashMap<String, RankPosition> map = new HashMap<String, RankPosition>();  
  11.            StringReader title = new StringReader(bbs.getTitle());  
  12.            IKSegmenter ik_title = new IKSegmenter(title, true);  
  13.            Lexeme lex_title = new Lexeme(0000);  
  14.            while ((lex_title = (ik_title.next())) != null) {  
  15.                if (lex_title.getLength() >= 2) {  
  16.                    String token = lex_title.getLexemeText();  
  17.                    int start = lex_title.getBeginPosition();  
  18.                    int end = lex_title.getEndPosition();  
  19.                    if (!map.containsKey(token)) {  
  20.                        map.put(token, new RankPosition(5true, start, end));  
  21.                    } else {  
  22.                        map.put(token, map.get(token).add(5true, start, end));  
  23.                    }  
  24.                }  
  25.            }  
  26.            StringReader content = new StringReader(bbs.getContent());  
  27.            IKSegmenter ik_content = new IKSegmenter(content, true);  
  28.            Lexeme lex_content = new Lexeme(0000);  
  29.            while ((lex_content = ik_content.next()) != null) {  
  30.                if (lex_content.getLength() >= 2) {  
  31.                    String token = lex_content.getLexemeText();  
  32.                    int start = lex_content.getBeginPosition();  
  33.                    int end = lex_content.getEndPosition();  
  34.                    if (!map.containsKey(token)) {  
  35.                        map.put(token, new RankPosition(1false, start, end));  
  36.                    } else {  
  37.                        map.put(token, map.get(token).add(1false, start, end));  
  38.                    }  
  39.                }  
  40.            }  
  41.            EmitMapValue(map, key.get(), context);  
  42.            map.clear();  
  43.        }  
  44.        public void EmitMapValue(HashMap<String, RankPosition> map,  
  45.                long DID, Context context) throws IOException,  
  46.                InterruptedException {  
  47.            for (Map.Entry<String, RankPosition> entry : map.entrySet()) {  
  48.                WORD.set(entry.getKey());  
  49.                RECORD.set(DID, entry.getValue());  
  50.                context.write(WORD, RECORD);  
  51.            }  
  52.        }  
  53.    }  
  54.    public static class InvertedIndexerReducer extends  
  55.            Reducer<Text, RecordWritable, Text, Text> {  
  56.        private final static Text OUT=new Text();  
  57.        @Override  
  58.        protected void reduce(Text key, Iterable<RecordWritable> values,  
  59.                Context context) throws IOException, InterruptedException {  
  60.            StringBuilder info = new StringBuilder();  
  61.            List<RecordWritable> list=new ArrayList<RecordWritable>();  
  62.             
  63.            while(values.iterator().hasNext()){  
  64.                RecordWritable record=values.iterator().next();  
  65.                RecordWritable recordWritable=new RecordWritable(record.getDID(), record.getRank(), record.getPositions());  
  66.                list.add(recordWritable);  
  67.            }  
  68.            int sum=list.size();  
  69.            for(int i=0;i<list.size();i++){  
  70.                info.append(resetFinalRank(list.get(i), sum)+";");  
  71.            }  
  72.            OUT.set(info.toString());  
  73.            context.write(key,OUT);  
  74.        }  
  75.        public String resetFinalRank(RecordWritable value, long num) {  
  76.            value.setRank((float) (value.getRank().get() / num));  
  77.            return value.toString();  
  78.        }  
  79.    }  


具体实现代码可以查看:
    离线处理程序:http://download.csdn.net/detail/long1657/8059593
    在线处理程序:http://download.csdn.net/detail/long1657/8059567
阅读全文
0 0