SortedNumericDocValue,乍一看,有好多疑问困扰着我,这种类型的存储的也是排序的吗?是不是可以通过docid获得其排序?是不是和SortedBinaryDocValue一样也是单值域的?带着这些疑问,看完了代码,发现完全不是。SortedNumericDocValue不是对所有的doc进行排序的,即无法获得一个doc的排序,而且他是多值域的,即一个doc可以含有多个数字,这里的sorted说的是每个doc的多个数字在存储的时候是排序的,但是没有多个doc之间的排序。
和之前一样,看一下在内存的添加:
/** Buffers up pending long[] per doc, sorts, then flushes when segment flushes. */ class SortedNumericDocValuesWriter extends DocValuesWriter { /**每个doc含有的所有的long*/ private PackedLongValues.Builder pending; // stream of all values /**每个doc含有的long数字的个数,如果没有,则是0*/ private PackedLongValues.Builder pendingCounts; // count of values per doc private final FieldInfo fieldInfo; /** 刚刚处理的doc的id */ private int currentDoc; /** 用于保存当前的doc的多个long数字 */ private long currentValues[] = new long[8]; /** 当前的doc的最后一个long在currentValues的指针,也可以说用来记录当前的doc的所有的数字的个数 */ private int currentUpto = 0; //构造方法 public SortedNumericDocValuesWriter(FieldInfo fieldInfo, Counter iwBytesUsed) { this.fieldInfo = fieldInfo; this.iwBytesUsed = iwBytesUsed; pending = PackedLongValues.deltaPackedBuilder(PackedInts.COMPACT); pendingCounts = PackedLongValues.deltaPackedBuilder(PackedInts.COMPACT); bytesUsed = pending.ramBytesUsed() + pendingCounts.ramBytesUsed(); iwBytesUsed.addAndGet(bytesUsed); } //添加一个docvalue public void addValue(int docID, long value) { if (docID != currentDoc) {//如果切换了docid,说明要处理下一个doc了,则要结束当前的doc,因为一个doc有多个值 finishCurrentDoc();//结束当前的doc } // Fill in any holes: 填窟窿,对于没有值的doc在pendingCounts中填入0,表示这个doc的值的个数是0个 while (currentDoc < docID) { pendingCounts.add(0); // no values currentDoc++; } addOneValue(value);//对当前的doc添加一个值 updateBytesUsed(); } // finalize currentDoc: this sorts the values in the current doc private void finishCurrentDoc() { Arrays.sort(currentValues, 0, currentUpto);//将当前的doc的多个值从小到大排序 for (int i = 0; i < currentUpto; i++) {//写入到pending中 pending.add(currentValues[i]); } // record the number of values for this doc pendingCounts.add(currentUpto);//当前doc含有的long数字的个数 currentUpto = 0; currentDoc++; } //结束完所有的doc @Override public void finish(int maxDoc) { finishCurrentDoc(); // fill in any holes for (int i = currentDoc; i < maxDoc; i++) { pendingCounts.add(0); // no values } } /**添加一个long到数组*/ private void addOneValue(long value) { if (currentUpto == currentValues.length) { currentValues = ArrayUtil.grow(currentValues, currentValues.length + 1); } currentValues[currentUpto] = value; currentUpto++; } }
通过上面的方法可以清楚的看到,SortedNumericDocValue是支持一个doc多个数字的。对于每个doc记录了两个内容,一个是这个doc有哪些值(保存在pending中),并且是排序后存放的;第二个是这个doc的值的个数(保存在pendingCount里面)。
再看看flush时的操作:
@Override public void flush(SegmentWriteState state, DocValuesConsumer dvConsumer) throws IOException { final int maxDoc = state.segmentInfo.getDocCount(); assert pendingCounts.size() == maxDoc; final PackedLongValues values = pending.build();//所有的添加的值 final PackedLongValues valueCounts = pendingCounts.build();//每个doc含有的long的数量 dvConsumer.addSortedNumericField(fieldInfo, // doc -> valueCount, new Iterable<Number>() { @Override public Iterator<Number> iterator() { return new CountIterator(valueCounts);//每个doc含有的数字的个数 } }, // values new Iterable<Number>() { @Override public Iterator<Number> iterator() {//所有的数字 return new ValuesIterator(values); } }); } private static class ValuesIterator implements Iterator<Number> { final PackedLongValues.Iterator iter; ValuesIterator(PackedLongValues values) { iter = values.iterator(); } @Override public boolean hasNext() { return iter.hasNext(); } @Override public Number next() { if (!hasNext()) { throw new NoSuchElementException(); } return iter.next(); } @Override public void remove() { throw new UnsupportedOperationException(); } } private static class CountIterator implements Iterator<Number> { final PackedLongValues.Iterator iter; CountIterator(PackedLongValues valueCounts) { this.iter = valueCounts.iterator(); } @Override public boolean hasNext() { return iter.hasNext(); } @Override public Number next() { if (!hasNext()) { throw new NoSuchElementException(); } return iter.next(); } @Override public void remove() { throw new UnsupportedOperationException(); } }
flush的时候也很简单,就是传递给Consumer两个迭代器,一个用来某个doc的long值的个数,第二个用于传递所有的值。再看看具体向dierctory中写入的方法吧Lucene410DocValuesConsumer.addSortedNumericField(FieldInfo, Iterable<Number>, Iterable<Number>):
public void addSortedNumericField(FieldInfo field, final Iterable<Number> docToValueCount, final Iterable<Number> values) throws IOException { meta.writeVInt(field.number);//域号 meta.writeByte(Lucene410DocValuesFormat.SORTED_NUMERIC); if (isSingleValued(docToValueCount)) {//如果全部是单值的,即每个doc都只有一个值,则直接用之前的数字类型的。因为此时没法排序,这里说的排序是对域一个doc的多个值的排序 meta.writeVInt(SORTED_SINGLE_VALUED); // The field is single-valued, we can encode it as NUMERIC addNumericField(field, singletonView(docToValueCount, values, null));//这个就是在记录NumericDocValye的时候的格式,要分为三个格式。 } else {//正常情况下,即一个doc含有多个数字的情况 meta.writeVInt(SORTED_WITH_ADDRESSES); // write the stream of values as a numeric field,先写数字类型的,即把所有的值写入到directory中。 addNumericField(field, values, true); // write the doc -> ord count as a absolute index to the stream。 addAddresses(field, docToValueCount);//在写入索引,用来读取每个doc的自己的多个long值。 } } private void addAddresses(FieldInfo field, Iterable<Number> values) throws IOException { meta.writeVInt(field.number);// meta.writeByte(Lucene410DocValuesFormat.NUMERIC); meta.writeVInt(MONOTONIC_COMPRESSED); meta.writeLong(-1L); meta.writeLong(data.getFilePointer()); meta.writeVLong(maxDoc); meta.writeVInt(PackedInts.VERSION_CURRENT); meta.writeVInt(BLOCK_SIZE); final MonotonicBlockPackedWriter writer = new MonotonicBlockPackedWriter(data, BLOCK_SIZE); long addr = 0; writer.add(addr);// for (Number v : values) { addr += v.longValue(); writer.add(addr);//记录每个doc之前一共有多少个long,这样就能很快的找到每个doc在numeric那一块的开始,然后读取多少个doc,也就是这个doc的所有的数字。 } writer.finish(); meta.writeLong(data.getFilePointer()); }
看完了flush就知道了SortedNumericDocValue是如何存储的了,他是分为两部分,一部分是数字,也就是所有的数字,每个doc的所有的数字是一起存放的,并且是排序后存放的;第二部分存储的是每个doc的第一个数字在所有的数字中的排序,比如第一个doc有三个数字,那么第二个doc在第二部分存储的就是3,因为这样在第一部分读取3个long之后,就是这个doc的自己的long了,这个doc的下一个doc的存储的值减去这个doc的存储的值就是这个doc的long的个数,那么在读取这个数量的long,就是这个这个doc的所有的long了。
同时还能看到,在所有的doc中,的确是没有排序,仅仅是一个doc的多个数字排序了。
相关推荐
### Lucene3源码分析知识点概述 #### 一、全文检索的基本原理 ##### 1. 总论 全文检索系统是一种高效的信息检索技术,能够帮助用户在海量文档中快速找到包含特定关键词的信息。Lucene是Java领域内最受欢迎的全文...
源码阅读是理解任何软件内部工作原理的最好方式,通过研究Lucene的源码,我们可以深入了解其内部的数据结构、算法实现以及优化技巧。例如,可以学习到如何实现Trie数据结构进行高效查询,或者如何使用BitSet进行布尔...
《深入剖析Lucene.NET 2.9.4.2源码》 Lucene.NET是一个开源全文搜索引擎库,它是Apache Lucene项目的.NET版本。这个源码版是2.9.4.2版本,相较于2.9.4版进行了一些局部改进,以适应.NET平台的需求和优化。在本文中...
【Lucene源码解读1】 Lucene是一款开源的全文搜索引擎库,由Apache软件基金会开发,广泛应用于各种信息检索系统。其强大的搜索功能和高效的性能深受开发者喜爱。在深入理解Lucene之前,我们需要先了解它的核心概念...
总的来说,深入学习Lucene 3.5.0的源码,可以帮助开发者掌握全文检索的核心技术,了解其内部工作原理,并能灵活应用到自己的项目中。这份源码不仅适用于初学者,也是经验丰富的开发者的宝贵参考资料。通过阅读和理解...
通过深入理解Lucene.Net 2.9.2的源码,开发者可以定制自己的分析器、优化查询性能、调整索引策略,从而在实际项目中充分发挥Lucene.Net的潜力。在构建查询网站时,结合C#的特性,可以构建出高效、灵活且用户体验良好...
本文将主要围绕Java Lucene进行深入探讨,并基于提供的“Lucene学习源码.rar”文件中的“Lucene视频教程_讲解部分源码”展开讨论。 一、Lucene核心概念 1. 文档(Document):Lucene中的基本单位,用于存储待检索...
通过对“lucene全文检索案例源码”的学习,我们可以理解Lucene如何在实际项目中实现全文检索。从索引构建到搜索执行,每个步骤都至关重要。通过源码的深入研究,有助于我们在实际开发中更好地运用Lucene,提升搜索...
《深入剖析Lucene.NET 2.9.1:源码解析与应用开发》 Lucene.NET 2.9.1是开源搜索引擎库Lucene的.NET版本,它为.NET开发者提供了强大的全文检索和索引功能。这个版本的源码提供了一个宝贵的资源,帮助我们理解其内部...
在Java开发中,Lucene被广泛用于实现文件的全文检索功能,包括对doc、docx、pdf、txt等常见格式文档的文本内容检索。在本文中,我们将探讨如何使用Lucene对这些文件类型进行全文检索的实现。 首先,为了实现全文...
学习这个源码包可以帮助你理解如何在Java环境中使用Lucene进行全文检索,以及如何实现数据库与索引之间的交互。这不仅涉及到了Lucene的核心功能,也涵盖了实际项目中常见的增量索引和数据库集成问题。通过阅读和理解...
总之,《Lucene in Action》的源码是一份宝贵的教育资源,它能帮助开发者深入理解搜索引擎的运作原理,从而在实际项目中更好地利用Lucene。通过细致研究源码,我们不仅可以解决具体的技术问题,还能培养出更强的解决...
本文将结合“lucene 华电项目 源码”,深度解析Lucene的核心原理以及在华电项目中的实际应用。 首先,我们要理解Lucene的基本架构。Lucene的核心组件包括Analyzer(分析器)、Document(文档)、IndexWriter(索引...
4. FSTHashMap:这是一个基于探测法实现的HashMap,其key是基于FSTNode生成的hash值,而value是FSTnode在FSTbytes数组中的位置索引。FSTHashMap可以加速判断某个节点是否已经被存储到FSTbytes中。 5. Frontier:这...
源码文件通常包含了书中各个章节的示例程序,这些示例涵盖了Lucene的基本用法到高级特性的实现,如文档索引、搜索查询、结果排序、过滤器、分词器、高亮显示等。通过研究这些源码,开发者可以了解如何有效地利用...
在`lucene-1.4-final`这个压缩包中,包含了Lucene 1.4版本的源代码,你可以深入研究其内部实现,理解各个类和方法的工作原理。同时,这也可以帮助你定制分析器、优化搜索性能,或者扩展Lucene的功能,例如集成到你的...
《深入剖析Lucene中的中文分词算法源码》 在信息检索领域,Lucene作为一款强大的全文搜索引擎库,被广泛应用于各种数据检索系统。而中文分词是Lucene处理中文文本时的关键步骤,它决定了搜索的准确性和效率。本文将...
**Lucene.Net** 是一个基于 .NET Framework 的全文搜索引擎库,它是 Apache Lucene 项目的 .NET 实现。这个开源项目提供了高效、可扩展的搜索功能,使得开发者能够在其应用程序中轻松地实现高级的文本检索功能。 **...
在Lucene-2.9.2的源码中,你可以看到关于TF-IDF的具体实现,如`TFIDFSimilarity`类,它是Lucene对TF-IDF算法的封装。它不仅包含了TF和IDF的计算逻辑,还考虑了诸如短语匹配、长度惩罚等因素,以提升搜索精度。 除了...
作为一款Java实现的全文搜索引擎架构,Lucene 提供了完整的索引和查询引擎,使得开发者能够快速、有效地在大量数据中进行文本搜索。 ### Lucene 的核心组件 1. **索引(Indexing)**: Lucene 的索引过程将文档内容...