文档章节

nutch源代码剖析——索引去重 (SolrDeleteDuplicates )

开拓者-2017
 开拓者-2017
发布于 2014/12/02 22:59
字数 1030
阅读 52
收藏 0

索引去重MapReduce任务描述

 

作者:旱魃斗天  oatt123@live.com

 

获取 去重 写入索引位置


一、主程序调用


SolrDeleteDuplicates dedup = new SolrDeleteDuplicates();

dedup.setConf(getConf());

dedup.dedup(solrUrl);



二、job任务配置


JobConf job = new NutchJob(getConf());


job.setInputFormat(SolrInputFormat.class);

job.setMapperClass(IdentityMapper.class);

job.setMapOutputKeyClass(Text.class);

job.setMapOutputValueClass(SolrRecord.class);


job.setReducerClass(SolrDeleteDuplicates.class);

job.setOutputFormat(NullOutputFormat.class);


JobClient.runJob(job);



三、Map、reduce任务的输入和输出

Map任务输入、输出

public void map(

K key, V val,

      OutputCollector<K, V> output


reduce任务输入、输出

输入:Text/Iterator<SolrRecord>

输出:Text/SolrRecord


public void reduce(

Text key, Iterator<SolrRecord> values,

      OutputCollector<Text, SolrRecord> output




四、job任务输入类SolrInputFormat


getSplits方法将所有的文档按照数量平均分片

getRecordReader方法中利用solrserver查询了当前分片包含的所有doc记录,solrrecord返回了的当前的RecordReader<Text, SolrRecord>记录(RecordReader是一个全局的变量),并且有获取下一个方法。


(1)、SolrInputFormat的getSplits方法


1、根据job对象的参数,获取solrserver对象。

2、构建并执行查询(查询参数:[*:*、id、setRow(1)] ),获取响应对象

3、根据响应对象获取索引总数,除以分片数,得到每一片分配多少个索引

4、根据分片数创建 SolrInputSplit数组对象,

5、根据solr输入分片的开始和结束位置,实例化SolrInputSplit对象




    public InputSplit[] getSplits(JobConf job, int numSplits) throws IOException {

      SolrServer solr = SolrUtils.getCommonsHttpSolrServer(job);


      final SolrQuery solrQuery = new SolrQuery(SOLR_GET_ALL_QUERY);

      solrQuery.setFields(SolrConstants.ID_FIELD);

      solrQuery.setRows(1);


      QueryResponse response;

      try {

        response = solr.query(solrQuery);

      } catch (final SolrServerException e) {

        throw new IOException(e);

      }


      int numResults = (int)response.getResults().getNumFound();

      int numDocsPerSplit = (numResults / numSplits); 

      int currentDoc = 0;

      SolrInputSplit[] splits = new SolrInputSplit[numSplits];

      for (int i = 0; i < numSplits - 1; i++) {

        splits[i] = new SolrInputSplit(currentDoc, numDocsPerSplit);

        currentDoc += numDocsPerSplit;

      }

      splits[splits.length - 1] = new SolrInputSplit(currentDoc, numResults - currentDoc);


      return splits;

    }



(2)、SolrInputFormat的getRecordReader()方法


1、获取solrserver对象

2、将传入的split参数,强转成SolrInputSplit对象,并获取这个分片的文档总数

3、构建查询对象,执行查询(参数[*:*,id,boost,tstamp,digest, SolrInputSplit中的开始位置,文档总数 ])。

4、根据响应对象,获取结果集

5、对匿名内部内RecordReader做了实现,并且返回


    public RecordReader<Text, SolrRecord> getRecordReader(final InputSplit split,

        final JobConf job, 

        Reporter reporter)

        throws IOException {

//1、获取solrserver对象

      SolrServer solr = SolrUtils.getCommonsHttpSolrServer(job);


//2、将传入的split参数,强转成SolrInputSplit对象,并获取这个分片的文档总数

SolrInputSplit solrSplit = (SolrInputSplit) split;

      final int numDocs = solrSplit.getNumDocs();

      

//3、构建查询对象,执行查询(参数[*:*,id,boost,tstamp,digest,

SolrInputSplit中的开始位置,文档总数

])

      SolrQuery solrQuery = new SolrQuery(SOLR_GET_ALL_QUERY);

      solrQuery.setFields(SolrConstants.ID_FIELD, SolrConstants.BOOST_FIELD,

                          SolrConstants.TIMESTAMP_FIELD,

                          SolrConstants.DIGEST_FIELD);

      solrQuery.setStart(solrSplit.getDocBegin());

      solrQuery.setRows(numDocs);

      QueryResponse response;

      


try {

        response = solr.query(solrQuery);

      } catch (final SolrServerException e) {

        throw new IOException(e);

      }


//4、根据响应对象,获取结果集

      final SolrDocumentList solrDocs = response.getResults();

      return new RecordReader<Text, SolrRecord>() {

//当前的文档

        private int currentDoc = 0;

        public void close() throws IOException { }

        public Text createKey() {

          return new Text();

        }

        public SolrRecord createValue() {

          return new SolrRecord();

        }

//获取当前的指针

        public long getPos() throws IOException {

          return currentDoc;

        }

//获取进度

        public float getProgress() throws IOException {

          return currentDoc / (float) numDocs;

        }

//获取下一个

        public boolean next(Text key, SolrRecord value) throws IOException {

          if (currentDoc >= numDocs) {

            return false;

          }

//

          SolrDocument doc = solrDocs.get(currentDoc);

//获取摘要

          String digest = (String) doc.getFieldValue(SolrConstants.DIGEST_FIELD);

//把摘要作为key

          key.set(digest);

//value(SolrRecord)

//赋值:通过doc给solrrecord的id,tstamp,boost 3个字段赋值

          value.readSolrDocument(doc);

//指针加自增1

          currentDoc++;

          return true;

        }    

      };

    }


  


五、map()方法和reduce()方法中的实现


(1)、map任务

(2)、reduce任务


去重逻辑:


reduce任务会遍历每一个record,并执行reduce()方法中的代码

reduce()方法中,会遍历处于当前文档之后的所有文档,如果分值和时间都比当前的小,会调用solrj删除这个文档,如果比当前的大,会删除当前的,并把当前的替换成这个大的。



  public void reduce(Text key, Iterator<SolrRecord> values,

      OutputCollector<Text, SolrRecord> output, Reporter reporter)

  throws IOException {

     //1、下一个SolrRecord对象

    SolrRecord recordToKeep = new SolrRecord(values.next());

//2、遍历了SolrRecord

    while (values.hasNext()) {

//

      SolrRecord solrRecord = values.next();

//boost、tstamp参与比较

//如果当前的分值, 比保持的分支高,并且时间比保持的新,就根据id删除这条索引,

      if (solrRecord.getBoost() > recordToKeep.getBoost() ||

          (solrRecord.getBoost() == recordToKeep.getBoost() && 

              solrRecord.getTstamp() > recordToKeep.getTstamp())) {

        updateRequest.deleteById(recordToKeep.id);

        recordToKeep = new SolrRecord(solrRecord);

      } else {

        updateRequest.deleteById(solrRecord.id);

      }

      numDeletes++;

      reporter.incrCounter("SolrDedupStatus", "Deleted documents", 1);

      if (numDeletes >= NUM_MAX_DELETE_REQUEST) {

        try {

          LOG.info("SolrDeleteDuplicates: deleting " + numDeletes + " duplicates");

          updateRequest.process(solr);

        } catch (SolrServerException e) {

          throw new IOException(e);

        }

        updateRequest = new UpdateRequest();

        numDeletes = 0;

      }

    }

  }



六、关于digest


doc中的digest字段,是在IndexerMapReduce类中的reduce方法中加入的

// add digest, used by dedup

doc.add("digest", metadata.get(Nutch.SIGNATURE_KEY));


Metadata中包含了一个HashMap

final Metadata metadata = parseData.getContentMeta();


© 著作权归作者所有

开拓者-2017
粉丝 38
博文 151
码字总数 301625
作品 0
大兴
程序员
私信 提问
nutch与起点R3集成之笔记(四)

通过“nutch与起点R3集成之笔记(一、二、三)”中的步骤,我们可以建立起一个行业内部网的搜索引擎,但搜索引擎一个最重要的功能,就是必须能搜索到网络中最新的内容。这就要求nutch能及时采...

夜游神
2011/08/04
1K
2
nutch-1.3使用solrindex出现 Invalid UTF-8 character ...

用nutch-1.3抓取了大量的页面后,最后用solrindex索引到solr中,发现出现了如下错误: LinkDb: finished at 2011-08-10 06:14:32, elapsed: 03:13:16 SolrIndexer: starting at 2011-08-10 0......

夜游神
2011/08/16
1K
2
Apache Nutch 1.2 Released

Nutch 是一个开源Java 实现的搜索引擎。它提供了我们运行自己的搜索引擎所需的全部工具。包括全文搜索和Web爬虫。 Apache Nutch 1.2 包含了不少的改进和bug修复,详情请看 CHANGES 文件。 你...

红薯
2010/09/25
2.5K
4
将nutch2.3的bin/crawl脚本改写为java类

将nutch2.3的bin/crawl脚本改写为java类 标签: nutch [TOC] nutch1.8以后,以前的主控代码类没了,只剩下对应的控制脚本,感觉在IDEA里面调试不方便,所以我了解了下shell脚本,根据nutch2....

brianway
2016/01/19
1K
0
ntuch solrindex 能索引出anchor数据的方法

网页的标题在检索排名中占有很重的权重,可是,一些网站管理系统常常用网站名或栏目名做标题,所以,为了保证文章的检索的命中率,nutch在生成lucene的数据时,把anchor也作为排名因素,给予...

夜游神
2013/03/14
181
0

没有更多内容

加载失败,请刷新页面

加载更多

Jenkins World 贡献者峰会及专家答疑展位

本文首发于:Jenkins 中文社区 原文链接 作者:Marky Jackson 译者:shunw Jenkins World 贡献者峰会及专家答疑展位 本文为 Jenkins World 贡献者峰会活动期间的记录 Jenkins 15周岁啦!Jen...

Jenkins中文社区
25分钟前
8
0
杂谈:面向微服务的体系结构评审中需要问的三个问题

面向微服务的体系结构如今风靡全球。这是因为更快的部署节奏和更低的成本是面向微服务的体系结构的基本承诺。 然而,对于大多数试水的公司来说,开发活动更多的是将现有的单块应用程序转换为...

liululee
40分钟前
7
0
OSChina 周二乱弹 —— 我等饭呢,你是不是来错食堂了?

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @ 自行车丢了:给主编推荐首歌 《クリスマスの夜》- 岡村孝子 手机党少年们想听歌,请使劲儿戳(这里) @烽火燎原 :国庆快来,我需要长假! ...

小小编辑
今天
418
9
玩转 Springboot 2 之热部署(DevTools)

Devtools 介绍 SpringBoot 提供了热部署的功能,那啥是热部署累?SpringBoot官方是这样说的:只要类路径上的文件发生更改,就会自动重新启动应用程序。在IDE中工作时,这可能是一个有用的功能...

桌前明月
今天
6
0
CSS--列表

一、列表标识项 list-style-type none:去掉标识项 disc:默认实心圆 circle:空心圆 squire:矩形 二、列表项图片 list-style-img: 取值:url(路径) 三、列表项位置 list-style-position:...

wytao1995
今天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部