`
suichangkele
  • 浏览: 200312 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

lucene中的docValue实现源码解读(八)——SortedNumericDocValue的写入

阅读更多

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源码分析

    ### Lucene3源码分析知识点概述 #### 一、全文检索的基本原理 ##### 1. 总论 全文检索系统是一种高效的信息检索技术,能够帮助用户在海量文档中快速找到包含特定关键词的信息。Lucene是Java领域内最受欢迎的全文...

    Lucene项目的文档和源码

    源码阅读是理解任何软件内部工作原理的最好方式,通过研究Lucene的源码,我们可以深入了解其内部的数据结构、算法实现以及优化技巧。例如,可以学习到如何实现Trie数据结构进行高效查询,或者如何使用BitSet进行布尔...

    lucene.net2.9.4.2源码版

    《深入剖析Lucene.NET 2.9.4.2源码》 Lucene.NET是一个开源全文搜索引擎库,它是Apache Lucene项目的.NET版本。这个源码版是2.9.4.2版本,相较于2.9.4版进行了一些局部改进,以适应.NET平台的需求和优化。在本文中...

    Lucene源码解读1

    【Lucene源码解读1】 Lucene是一款开源的全文搜索引擎库,由Apache软件基金会开发,广泛应用于各种信息检索系统。其强大的搜索功能和高效的性能深受开发者喜爱。在深入理解Lucene之前,我们需要先了解它的核心概念...

    Lucene3.5源码jar包

    总的来说,深入学习Lucene 3.5.0的源码,可以帮助开发者掌握全文检索的核心技术,了解其内部工作原理,并能灵活应用到自己的项目中。这份源码不仅适用于初学者,也是经验丰富的开发者的宝贵参考资料。通过阅读和理解...

    Lucene.Net-2.9.2 c#源码

    通过深入理解Lucene.Net 2.9.2的源码,开发者可以定制自己的分析器、优化查询性能、调整索引策略,从而在实际项目中充分发挥Lucene.Net的潜力。在构建查询网站时,结合C#的特性,可以构建出高效、灵活且用户体验良好...

    Lucene学习源码.rar

    本文将主要围绕Java Lucene进行深入探讨,并基于提供的“Lucene学习源码.rar”文件中的“Lucene视频教程_讲解部分源码”展开讨论。 一、Lucene核心概念 1. 文档(Document):Lucene中的基本单位,用于存储待检索...

    lucene全文检索案例源码

    通过对“lucene全文检索案例源码”的学习,我们可以理解Lucene如何在实际项目中实现全文检索。从索引构建到搜索执行,每个步骤都至关重要。通过源码的深入研究,有助于我们在实际开发中更好地运用Lucene,提升搜索...

    lucene.net 2.9.1 源码

    《深入剖析Lucene.NET 2.9.1:源码解析与应用开发》 Lucene.NET 2.9.1是开源搜索引擎库Lucene的.NET版本,它为.NET开发者提供了强大的全文检索和索引功能。这个版本的源码提供了一个宝贵的资源,帮助我们理解其内部...

    使用Lucene对doc、docx、pdf、txt文档进行全文检索功能的实现 - 干勾鱼的CSDN博客 - CSDN博客1

    在Java开发中,Lucene被广泛用于实现文件的全文检索功能,包括对doc、docx、pdf、txt等常见格式文档的文本内容检索。在本文中,我们将探讨如何使用Lucene对这些文件类型进行全文检索的实现。 首先,为了实现全文...

    基于lucene搜索引擎的java源码

    学习这个源码包可以帮助你理解如何在Java环境中使用Lucene进行全文检索,以及如何实现数据库与索引之间的交互。这不仅涉及到了Lucene的核心功能,也涵盖了实际项目中常见的增量索引和数据库集成问题。通过阅读和理解...

    lucene in action源码2

    总之,《Lucene in Action》的源码是一份宝贵的教育资源,它能帮助开发者深入理解搜索引擎的运作原理,从而在实际项目中更好地利用Lucene。通过细致研究源码,我们不仅可以解决具体的技术问题,还能培养出更强的解决...

    lucene 华电项目 源码

    本文将结合“lucene 华电项目 源码”,深度解析Lucene的核心原理以及在华电项目中的实际应用。 首先,我们要理解Lucene的基本架构。Lucene的核心组件包括Analyzer(分析器)、Document(文档)、IndexWriter(索引...

    Lucene中的FST算法描述

    4. FSTHashMap:这是一个基于探测法实现的HashMap,其key是基于FSTNode生成的hash值,而value是FSTnode在FSTbytes数组中的位置索引。FSTHashMap可以加速判断某个节点是否已经被存储到FSTbytes中。 5. Frontier:这...

    Lucene In Action 2源码

    源码文件通常包含了书中各个章节的示例程序,这些示例涵盖了Lucene的基本用法到高级特性的实现,如文档索引、搜索查询、结果排序、过滤器、分词器、高亮显示等。通过研究这些源码,开发者可以了解如何有效地利用...

    lucene源码和程序

    在`lucene-1.4-final`这个压缩包中,包含了Lucene 1.4版本的源代码,你可以深入研究其内部实现,理解各个类和方法的工作原理。同时,这也可以帮助你定制分析器、优化搜索性能,或者扩展Lucene的功能,例如集成到你的...

    运用在lucene中的中文分词算法源码

    《深入剖析Lucene中的中文分词算法源码》 在信息检索领域,Lucene作为一款强大的全文搜索引擎库,被广泛应用于各种数据检索系统。而中文分词是Lucene处理中文文本时的关键步骤,它决定了搜索的准确性和效率。本文将...

    Lucene.Net源码与说明文档

    **Lucene.Net** 是一个基于 .NET Framework 的全文搜索引擎库,它是 Apache Lucene 项目的 .NET 实现。这个开源项目提供了高效、可扩展的搜索功能,使得开发者能够在其应用程序中轻松地实现高级的文本检索功能。 **...

    lucene-2.9.2.jar包+源码

    在Lucene-2.9.2的源码中,你可以看到关于TF-IDF的具体实现,如`TFIDFSimilarity`类,它是Lucene对TF-IDF算法的封装。它不仅包含了TF和IDF的计算逻辑,还考虑了诸如短语匹配、长度惩罚等因素,以提升搜索精度。 除了...

    Lucene源码

    作为一款Java实现的全文搜索引擎架构,Lucene 提供了完整的索引和查询引擎,使得开发者能够快速、有效地在大量数据中进行文本搜索。 ### Lucene 的核心组件 1. **索引(Indexing)**: Lucene 的索引过程将文档内容...

Global site tag (gtag.js) - Google Analytics