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源码分析
Lucene项目的文档和源码,很好的java学习资料哦
lucene索引查看工具及源码lucene索引查看工具及源码lucene索引查看工具及源码
Lucene.Net-2.9.2 c# 源码,已经用它做了查询网站
lucene全文检索案例源码 lucene全文检索案例源码
Lucene是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎。该资源是Lucene的完整源码,利用源码,可以定制适合自身需要的搜索引擎。
lucene.net2.9.4.2源码版 dll版本是2.9.4.2 对2.9.4版的局部改进版
Lucene3.5全部源码,打包jar文件,可以直接打开查看源码,Lucene开发必备
Lucene源码解读、功能分析,Lucene入门
Lucene学习源码.rar
这是基于lucene搜索引擎的java源码,里面数据库,包括建立索引,增量索引一应俱全,希望对大家有作用。
lucene.net 2.9.1 源码,lucene.net 2.9.1最新dll
lucene-core-4.3.1的源码 可以用的 放心用吧 官网上很多链接都打不开
描述了Lucene中如何使用FST算法构建term的内存索引,使用了很多图,直观的展现了FST图的构建流程,能够对想了解lucene内部实现机制原理的同学有帮助。
Lucene,作为一种全文搜索的辅助工具,为我们进行条件搜索,无论是像Google,Baidu之类的搜索引 擎,还是论坛中的搜索功能,还是其它C/S架构的搜索,都带来了极大的便利和比较高的效率。本文主要是利用Lucene对MS Sql...
Lucene.net 搜索引擎 Lucene.net源码 Lucene.net中文文档; 好的东西需要分享
lucene 华电项目 源码
lucene3.0.3源码和教程
lucene5.0源码包,里面有源码、帮助文档、API、架包
Lucene 4.1 最新版本 源码 修复诸多BUG 含英文API 新增AnalyzingSuggester和FuzzySuggester等,性能优化 欢迎下载