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

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值。
    核心代码如下:
 public static class InvertedIndexerMapper extends
            Mapper<LongWritable, Text, Text, RecordWritable> {
        public final static Text WORD = new Text();
        public final static RecordWritable RECORD = new RecordWritable();
        public final static Gson gson = new Gson();
        @Override
        protected void map(LongWritable key, Text value, Context context)
                throws IOException, InterruptedException {
            BBS bbs = gson.fromJson(value.toString(), BBS.class);
            HashMap<String, RankPosition> map = new HashMap<String, RankPosition>();
            StringReader title = new StringReader(bbs.getTitle());
            IKSegmenter ik_title = new IKSegmenter(title, true);
            Lexeme lex_title = new Lexeme(0, 0, 0, 0);
            while ((lex_title = (ik_title.next())) != null) {
                if (lex_title.getLength() >= 2) {
                    String token = lex_title.getLexemeText();
                    int start = lex_title.getBeginPosition();
                    int end = lex_title.getEndPosition();
                    if (!map.containsKey(token)) {
                        map.put(token, new RankPosition(5, true, start, end));
                    } else {
                        map.put(token, map.get(token).add(5, true, start, end));
                    }
                }
            }
            StringReader content = new StringReader(bbs.getContent());
            IKSegmenter ik_content = new IKSegmenter(content, true);
            Lexeme lex_content = new Lexeme(0, 0, 0, 0);
            while ((lex_content = ik_content.next()) != null) {
                if (lex_content.getLength() >= 2) {
                    String token = lex_content.getLexemeText();
                    int start = lex_content.getBeginPosition();
                    int end = lex_content.getEndPosition();
                    if (!map.containsKey(token)) {
                        map.put(token, new RankPosition(1, false, start, end));
                    } else {
                        map.put(token, map.get(token).add(1, false, start, end));
                    }
                }
            }
            EmitMapValue(map, key.get(), context);
            map.clear();
        }
        public void EmitMapValue(HashMap<String, RankPosition> map,
                long DID, Context context) throws IOException,
                InterruptedException {
            for (Map.Entry<String, RankPosition> entry : map.entrySet()) {
                WORD.set(entry.getKey());
                RECORD.set(DID, entry.getValue());
                context.write(WORD, RECORD);
            }
        }
    }
    public static class InvertedIndexerReducer extends
            Reducer<Text, RecordWritable, Text, Text> {
        private final static Text OUT=new Text();
        @Override
        protected void reduce(Text key, Iterable<RecordWritable> values,
                Context context) throws IOException, InterruptedException {
            StringBuilder info = new StringBuilder();
            List<RecordWritable> list=new ArrayList<RecordWritable>();
           
            while(values.iterator().hasNext()){
                RecordWritable record=values.iterator().next();
                RecordWritable recordWritable=new RecordWritable(record.getDID(), record.getRank(), record.getPositions());
                list.add(recordWritable);
            }
            int sum=list.size();
            for(int i=0;i<list.size();i++){
                info.append(resetFinalRank(list.get(i), sum)+";");
            }
            OUT.set(info.toString());
            context.write(key,OUT);
        }
        public String resetFinalRank(RecordWritable value, long num) {
            value.setRank((float) (value.getRank().get() / num));
            return value.toString();
        }
    }


具体实现代码可以查看:
参考文献:
1.刘鹏,hadoop实战,电子工业出版社,2011.9
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值