`

Lucene/Solr(5.0) 源码初探- Lucene Facet SortedSetDocValues (二)

阅读更多
//SortedSetDocValuesWriter
 public void addValue(int docID, BytesRef value) {
    if (value == null) {
      throw new IllegalArgumentException("field \"" + fieldInfo.name + "\": null value not allowed");
    }
    if (value.length > (BYTE_BLOCK_SIZE - 2)) {
      throw new IllegalArgumentException("DocValuesField \"" + fieldInfo.name + "\" is too large, must be <= " + (BYTE_BLOCK_SIZE - 2));
    }
    
    /**由于所有的文档是按照顺序index,如果docid变化,表示前面文档index结束要做一些后期操作*
    if (docID != currentDoc) {
      finishCurrentDoc();
    }

    // Fill in any holes:
    while(currentDoc < docID) {
      pendingCounts.add(0); // no values
      currentDoc++;
    }
   // 这里讲field 值加入内存
    addOneValue(value);
    updateBytesUsed();
  }


 private void addOneValue(BytesRef value) {

    /** 这里维护了一个hash表用来生成term id, 同时可以判定此term是够已经存在(去重)*/
    int termID = hash.add(value);
    if (termID < 0) {
      termID = -termID-1;
    } else {
      // reserve additional space for each unique value:
      // 1. when indexing, when hash is 50% full, rehash() suddenly needs 2*size ints.
      //    TODO: can this same OOM happen in THPF?
      // 2. when flushing, we need 1 int per value (slot in the ordMap).
      iwBytesUsed.addAndGet(2 * RamUsageEstimator.NUM_BYTES_INT);
    }
    
    if (currentUpto == currentValues.length) {
      currentValues = ArrayUtil.grow(currentValues, currentValues.length+1);
      // reserve additional space for max # values per-doc
      // when flushing, we need an int[] to sort the mapped-ords within the doc
      iwBytesUsed.addAndGet((currentValues.length - currentUpto) * 2 * RamUsageEstimator.NUM_BYTES_INT);
    }
   /** currentValues 是一个writer的全局数组,用来记录每个文档所包含的term Id
currentUpto 表示现在数组占用边界,对每个文档具体的统计在finishCurrentDoc 方法操作*/
    currentValues[currentUpto] = termID;
    currentUpto++;
  }

// finalize currentDoc: this deduplicates the current term ids
  private void finishCurrentDoc() {
    Arrays.sort(currentValues, 0, currentUpto);
    int lastValue = -1;
    int count = 0;
   /** currentValues termid 排序后,用pending 来做统计,pendingCounts 表示当前文档包含几个term,pending 维护一个termid数组,而且对于其中每个文档其包含的termid 必定唯一。*/
    for (int i = 0; i < currentUpto; i++) {
      int termID = currentValues[i];
      // if it's not a duplicate
      if (termID != lastValue) {
        pending.add(termID); // record the term id
        count++;
      }
      lastValue = termID;
    }
    // record the number of unique term ids for this doc
    pendingCounts.add(count);
    maxCount = Math.max(maxCount, count);
    currentUpto = 0;
    currentDoc++;
  }


做好每个文档的term统计以后,在最后flush操作中,生成几个关键数组,完成DocValues的存储。


//SortedSetDocValuesWriter
 public void flush(SegmentWriteState state, DocValuesConsumer dvConsumer) throws IOException {
    final int maxDoc = state.segmentInfo.getDocCount();
    final int maxCountPerDoc = maxCount;
    assert pendingCounts.size() == maxDoc;
    //由于hash是全局,并且去重,那么valuecount就是真正存储的term的数量
    final int valueCount = hash.size();
    //pending 数组表示文档包含哪些具体term id,每个文档之间已经按照termid 排序
    final PackedLongValues ords = pending.build();
    // 每个文档包含几个term
    final PackedLongValues ordCounts = pendingCounts.build();
    
    //sortedValues 是hash所有存储的term排序后term id 排列组合
    final int[] sortedValues = hash.sort(BytesRef.getUTF8SortedAsUnicodeComparator());
    final int[] ordMap = new int[valueCount];
   
    /**ordMap是数据索引表,可以根据termid 查找在sortedValues 中具体位置,挺tricky的行列转换代码*/
    for(int ord=0;ord<valueCount;ord++) {
      ordMap[sortedValues[ord]] = ord;
    }
    
    // 具体将各个数据字典写入segment,dvd,dvm文件中。
    dvConsumer.addSortedSetField(fieldInfo,

                              // ord -> value
                              new Iterable<BytesRef>() {
                                @Override
                                public Iterator<BytesRef> iterator() {
                                  return new ValuesIterator(sortedValues, valueCount, hash);
                                }
                              },
                              
                              // doc -> ordCount
                              new Iterable<Number>() {
                                @Override
                                public Iterator<Number> iterator() {
                                  return new OrdCountIterator(maxDoc, ordCounts);
                                }
                              },

                              // ords
                              new Iterable<Number>() {
                                @Override
                                public Iterator<Number> iterator() {
                                  return new OrdsIterator(ordMap, maxCountPerDoc, ords, ordCounts);
                                }
                              });
  }



由于看代码抽象,用上章的例子做为解释对象,上章例子中有如下SortedSetDocValuesFacetField:

Doc0:
  doc.add(new SortedSetDocValuesFacetField("Author", "Bob"));
  doc.add(new SortedSetDocValuesFacetField("Publish Year", "2010"));
Doc1:
  doc.add(new SortedSetDocValuesFacetField("Author", "Lisa"));
  doc.add(new SortedSetDocValuesFacetField("Publish Year", "2010"));
Doc2:
  doc.add(new SortedSetDocValuesFacetField("Author", "Lisa"));
  doc.add(new SortedSetDocValuesFacetField("Publish Year", "2012"));
Doc3:
  doc.add(new SortedSetDocValuesFacetField("Author", "Susan"));
  doc.add(new SortedSetDocValuesFacetField("Publish Year", "2012"));
Doc4:
  doc.add(new SortedSetDocValuesFacetField("Author", "Frank"));
  doc.add(new SortedSetDocValuesFacetField("Publish Year", "1999"));

根据上章分析所有的dim,label 将会拼接在一起,而且生成termid, 其term id 与term对应关系如下:
(注lucene存贮字符串是用utf8存储为了便于理解这里还是用字符串显示但是中间分隔符是1f)
0 ----- "Author1fBob"
1 ----- "Publish Year1f2010"
2 ----- "Author1fLisa"
3 ----- "Publish Year1f2012"
4 ----- "Author1fSusan"
5 ----- "Author1fFrank"
6 ----- "Publish Year1f1999"

pendingCounts(ordCounts) 在flush时候:
[2, 2, 2, 2, 2]
每个数组下标表示docid,这个数组表示0-4号文档,每个文档包括2个term

pending ords 在flush时候:
[0, 1, 1, 2, 2, 3, 3, 4, 5, 6]

结合ordCounts再看,对于文档1,查找pendingCounts 知道其包含2个term,其前一个文档也是包含2个term,那么查找文档1包含的term是从pending 数组下标2开始,到4 结束(不包含4),也就是包含term 1和2。

sortedValues 在flush时候:  [0, 5, 2, 4, 6, 1, 3]

很明显这是根据要存储的term按照字符串排序后,term id 的排列

ordMap 在flush时候: [0, 5, 2, 6, 3, 1, 4]

初看是和sortedValues 很类似,其实这是一个数组索引数组,我们根据文档号查找到其包含的term id 时候,我们遇到另外一个问题,因为term 存储不是按照termid或者term索引顺序存储的,而是按照term字符串的大小排列的,即使知道termid ,并不意味着顺序访问就能得到真正的term,所以ordMap 就是这个数据索引,假设我们知道文档1包含term 1,2,那么我们根据ordMap的下标(其就是term id ) 查找term1 的位置是5,也就是在sortvalues 存储在第5个term 位置,同理term2在第2个位置。

有意思的是在生成OrdMap中用的代码:

for(int ord=0;ord<valueCount;ord++) {
      ordMap[sortedValues[ord]] = ord;
   }

做到生成代码为O(n),而不是O(n*n)。到此可以回答前面的问题,所有的term都是单独去重排序存储(sortedValues),另外为所有文档维护term 信息pending ords,pendingCounts(ordCounts),同时便于根据termid 查找具体term内容提供索引数组ordMap 查找。


分享到:
评论
1 楼 q133662517 2018-02-28  
谢谢

相关推荐

    src.rar_solr

    Solr 是一个开源的全文搜索服务器,由 Apache Lucene 提供支持,被广泛应用于构建高效、可扩展的搜索应用。本教程将深入讲解 Solr 的基本使用,包括查询、修改和删除操作,以及统计功能。 一、Solr 安装与配置 1. ...

    Lucene5学习之Facet(续)

    7. **工具支持**:在实际应用中,我们可能需要结合其他工具,如Solr或Elasticsearch,它们都基于Lucene并提供了更高级别的Facet管理功能。了解如何在这些工具中集成和配置Facet,将有助于提升整体解决方案的效率。 ...

    Apache Solr(solr-7.7.3.zip)

    Apache Solr 是一个开源的企业级搜索平台,它基于 Java 并且构建在 Apache Lucene 库之上。Solr 提供了强大的全文搜索、近实时搜索、 faceted search(分面搜索)、命中高亮、拼写检查、自动补全等多种功能,广泛...

    Solr学习笔记。。

    Solr 是一个开源的全文搜索服务器,由Apache Lucene项目维护。它提供了高效、可扩展的搜索和分析功能,常用于大数据量的全文检索、数据分析和分布式搜索应用。本篇文章将详细探讨Solr的安装运行、添加分词器以及配置...

    Solr 入门资料

    Solr 是一个开源的全文搜索引擎,基于 Java 平台,由 Apache Software Foundation 维护。它是 Lucene 的扩展,提供了更高级别的企业级搜索功能,如分布式搜索、缓存、多语言支持、近实时搜索等。Solr 适用于构建大型...

    搜索引擎技术教程 网络搜索引擎原理-第7章 Xapian简介 共39页.pptx

    - Xapian 在某些方面比Lucene/Solr更具优势,特别是在嵌入式应用和对性能要求较高的场景下。 - **与Sphinx对比**: - Sphinx 是一款基于SQL的全文搜索引擎,特别适合与MySQL等数据库集成。 - Sphinx 的索引建立...

    java进阶Solr从基础到实战

    Solr它是一种开放源码的、基于 Lucene 的搜索服务器,可以高效的完成全文检索的功能。在本套课程中,我们将全面的讲解Solr,从Solr基础到Solr高级,再到项目实战,基本上涵盖了Solr中所有的知识点。 主讲内容 章节一...

    solr基础知识介绍

    Solr创建的索引与Lucene完全兼容,这意味着Solr可以阅读和使用其他Lucene应用程序构建的索引。 Solr的功能包括分布式索引设计,它允许索引自动分割成多个部分,并且能够在不停止Solr服务器的情况下更改配置。Solr还...

    Solr分组统计

    同时,模糊查询和精确查询是Solr查询语言(Lucene Query Parser Syntax)的一部分,它们分别用于处理用户可能输入的不完全匹配或完全匹配的关键词。 模糊查询允许用户使用通配符或近似搜索来找到相似或拼写相近的...

    高效的企业级搜索引擎Solr

    #### 二、Solr的性能优化策略 为了进一步提升Solr的性能,通常会采用多级缓存策略来增强其搜索和并发处理能力。 ##### 1. SolrCache缓存 - **LRUCache**:采用了最近最少使用的策略来管理缓存中的数据,当缓存...

    cl-solr:用于 Common Lisp 的 Apache Solr API

    4. **cl-solr 库的功能**: cl-solr 提供了对 Solr 的全面封装,包括但不限于创建索引、更新文档、删除文档、搜索操作、处理结果集、处理 facet(分面)搜索、拼写检查、同义词等功能。它还可能包含了错误处理和事务...

    solr查询语法.pdf

    Solr是一种基于Apache Lucene的开源搜索引擎,提供了丰富的查询语法来满足各种搜索需求。在了解Solr查询语法前,我们首先需要了解几个核心概念。 首先,Solr的查询解析是通过queryParser来配置的,通常使用默认配置...

    基于lucene组件的全文搜索系统

    例如,通过分布式索引和搜索技术,如Solr或Elasticsearch,可以将Lucene应用到大规模的数据场景。同时,定期更新索引以保持数据的新鲜度,以及合理配置硬件资源,都是保证系统性能的关键。 至于提供的`app`文件,...

    solr技术方案.pdf

    Solr技术方案是一种针对全文搜索和检索的开源解决方案,它基于Apache Lucene,提供高性能、高可用性的搜索服务。在面对传统数据库层面的模糊搜索(like查询)导致的性能问题和用户体验不佳的情况时,Solr成为了提升...

    solrium:Solr的通用R接口

    Solr是Apache Lucene项目的一个开源搜索引擎服务器,它提供了全文搜索、命中高亮、 faceted search(分面搜索)和动态集群等功能。而`solrium`则是为R语言设计的一个客户端包,允许用户通过R与Solr服务器进行交互,...

    Go文本索引库Bleve.zip

    当使用 Java 和 JVM 的时候使用比较多的是 Lucene,Elasticsearch 和 Solr。但是有时候不想要那么多依赖,想要避免外部服务,简单编译部署就可以运行。所以 Couchbase 团队就构建了一个 Go 库,支持大部分 Lucene ...

    rails _sunspot 学习笔记

    它基于 Solr 和 Lucene 构建,可以方便地集成到 Rails 应用中,为用户提供高效的搜索功能。 #### 二、Sunspot 安装 在开始之前,请确保已经安装了以下组件: 1. **Ruby on Rails 3**:本文档主要针对 Rails 3 ...

Global site tag (gtag.js) - Google Analytics