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

lucene中的docValue实现源码解读(十)——SortedSet的写入

阅读更多

 

看看最后一种docValue,sortedSet,他有点难,如果不是之前看了四种其他的格式的话,不是很容易理解,同理,如果看懂了其他的格式,则很容易理解。先说一下SortedSet吧,它是存储的byte[],一个doc是可以有多个值的,在存储的时候,是排序的,即在索引中是可以查找到每个doc的byte[]的排序的。他的存储是综合了sortedDocValue(存储排序的单值byte[])和SortedNumericDocValue,在存储的时候对于所有的byte[]是要先获得对应的排序,如果我们把顺序的地方和具体的byte[]值分开的话,单独存放所有的byte[]就可以使用和SortedDocValues同样的格式,如果存储好了所有的byte[],那么每个doc就可以根据自己的byte[]的排序就可以获得其自己的byte[]了,也就是只需要保存一些数字就可以了,就可以使用SortedNumericDocValue的格式了。

看看再内存中的保存,使用的是SortedSetDocValuesWriter :

class SortedSetDocValuesWriter extends DocValuesWriter {
	/**用来存储所有的byte[],并定义其id*/
	final BytesRefHash hash;
	/** 记录termid,所有的doc的都记录,不过在每个doc记录的时候,是按照termId排序的,但是对于不同的doc是不排序的 */
	private PackedLongValues.Builder pending; // stream of all termIDs
	/** 每个doc含有的byte[]的个数的,重复的计算一个 */
	private PackedLongValues.Builder pendingCounts; // termIDs per doc
	private final Counter iwBytesUsed;
	private long bytesUsed; // this only tracks differences in 'pending' and 'pendingCounts'
	private final FieldInfo fieldInfo;
	/** 当前处理的doc的id,用于区别是不是同一个doc*/
	private int currentDoc;
	/** 当前的doc处的所有的byte[] */
	private int currentValues[] = new int[8];
	/**当前的doc含有的byte[]在currentValues中的指针*/
	private int currentUpto = 0;
	/**所有的doc中含有的byte[]的个数的最大值 */
	private int maxCount = 0;
	
	public SortedSetDocValuesWriter(FieldInfo fieldInfo, Counter iwBytesUsed) {
		this.fieldInfo = fieldInfo;
		this.iwBytesUsed = iwBytesUsed;
		hash = new BytesRefHash(new ByteBlockPool(new ByteBlockPool.DirectTrackingAllocator(iwBytesUsed)),BytesRefHash.DEFAULT_CAPACITY, new DirectBytesStartArray(BytesRefHash.DEFAULT_CAPACITY, iwBytesUsed));
		pending = PackedLongValues.packedBuilder(PackedInts.COMPACT);
		pendingCounts = PackedLongValues.deltaPackedBuilder(PackedInts.COMPACT);
		bytesUsed = pending.ramBytesUsed() + pendingCounts.ramBytesUsed();
		iwBytesUsed.addAndGet(bytesUsed);
	}
        public void addValue(int docID, BytesRef value) {
                //检查的条件,略过
		if (docID != currentDoc) {//结束这个doc
			finishCurrentDoc();
		}
		// Fill in any holes:
		while (currentDoc < docID) {
			pendingCounts.add(0); //没有值的doc含有的byte[]的个数是0
			currentDoc++;
		}

		addOneValue(value);//添加一个值
		updateBytesUsed();//更新使用的内存
	}
        //结束一个doc
	private void finishCurrentDoc() {
		Arrays.sort(currentValues, 0, currentUpto);//将当前的doc的所有的值进行排序
		int lastValue = -1;
		int count = 0;
		for (int i = 0; i < currentUpto; i++) {
			int termID = currentValues[i];
			// if its 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);//添加这个doc的byte[]的个数
		maxCount = Math.max(maxCount, count);
		currentUpto = 0;
		currentDoc++;
	}
        //
	private void addOneValue(BytesRef value) {
		int termID = hash.add(value);//添加到hash表里面,如果返回的值是小于0的表示已经存在了,否则第一次出现。
		if (termID < 0) {
			termID = -termID - 1;
		} else {
			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[currentUpto] = termID;//添加到临时的数组里面去。
		currentUpto++;
	}

可以看到,他的处理逻辑和SortedNumericDocValue是差不多的,允许一个doc添加多个byte[]。在内存中保存的时候,是保存了一个doc的多个byte[]以及一个doc的byte[]的个数。再结束一个doc的多个byte[]的时候,是先进行了排序。

再看看如何写入到directory中去,先调用flush方法:

 

public void flush(SegmentWriteState state, DocValuesConsumer dvConsumer) throws IOException {
	final int maxDoc = state.segmentInfo.getDocCount();
	final int maxCountPerDoc = maxCount;
	assert pendingCounts.size() == maxDoc;
	final int valueCount = hash.size();//所有的byte[]的个数,
	final PackedLongValues ords = pending.build();//所有的doc含有的所有的byte[]的id
	final PackedLongValues ordCounts = pendingCounts.build();//每个doc含有的byte[]的个数
	
	// 将所有的byte[]排序,并整理hash表,移动到数组里面去。
	final int[] sortedValues = hash.sort(BytesRef.getUTF8SortedAsUnicodeComparator());
	final int[] ordMap = new int[valueCount];//下标是term id,值是排序。可以快速的得到某个id的term的排序
	for (int ord = 0; ord < valueCount; ord++) {
		ordMap[sortedValues[ord]] = ord;
	}

	dvConsumer.addSortedSetField(fieldInfo,
			// ord -> value
			new Iterable<BytesRef>() {//返回所有的byte[],已经排好顺序了。
				public Iterator<BytesRef> iterator() {
					return new ValuesIterator(sortedValues, valueCount, hash);
				}
			},
			// doc -> ordCount。每个doc含有的byte[]的个数
			new Iterable<Number>() {
				public Iterator<Number> iterator() {
					return new OrdCountIterator(maxDoc, ordCounts);
				}
			},
			//用于返回所有的doc的byte[]的排序
			new Iterable<Number>() {
				public Iterator<Number> iterator() {
					return new OrdsIterator(ordMap, maxCountPerDoc, ords, ordCounts);
				}
			});
	
}

通过上面可以看出,在flush的过程中,是向最终的Consumer传递了三个参数,第一个是用于形成所有的词典表的,即所有的byte[],第二个是每个doc含有的byte[]的个数,第三个是每个doc的所有的byte[] 的排序。有了这三个iterable,我们看一下具体的flush过程吧:方法是Lucene410DocValuesConsumer.addSortedSetField(FieldInfo, Iterable<BytesRef>, Iterable<Number>, Iterable<Number>):

 

 

/**
 * @param values:按照顺序返回的byte[]
 * @param docToOrdCount:每个doc含有的顺序(也就是byte[])的个数
 * @param ords:所有的doc的byte[]的排序
 */
public void addSortedSetField(FieldInfo field, Iterable<BytesRef> values, final Iterable<Number> docToOrdCount,	final Iterable<Number> ords) throws IOException {
	meta.writeVInt(field.number);//
	meta.writeByte(Lucene410DocValuesFormat.SORTED_SET);
	if (isSingleValued(docToOrdCount)) {//如果全部的doc都只有一个byte[],则和SortedDocValue是一样的,忽略这种情况
		meta.writeVInt(SORTED_SINGLE_VALUED);
		// The field is single-valued, we can encode it as SORTED
		addSortedField(field, values, singletonView(docToOrdCount, ords, -1L));
	} else {
		meta.writeVInt(SORTED_WITH_ADDRESSES);
		// write the ord -> byte[] as a binary field。
		addTermsDict(field, values);//添加词典表,也就是记录所有的byte[],这个方法在sortedDocValue中也使用到了,也就是存储格式和SortedDocValue是一个存储格式(可以回顾一下SortedDocValue的存储格式,地址是:http://suichangkele.iteye.com/blog/2410752)。使用这个格式的好处是可以快速的根据排序的到对应的byte[]
		// 写入一个doc含有的所有的byte[]的排序,也就是一个doc对应的多个数字,这个和sortedDocValue的也是一样的。
		addNumericField(field, ords, false);
		// write the doc -> ord count as a absolute index to the stream
		addAddresses(field, docToOrdCount);//写入一个doc的第一个排序在所有的排序中的开始位置,  这个和SortedNumericDocValue是一样的。
	}
}

正如我在文章开头说的,如果之前看懂了其他四个docValue的存储格式,看这个很简单。上面的addSortedSetField方法,如果把addAddress方法去掉的话,就是和SortedDocValue一样,不同的地方是,对于SortedSetDocValue是多个值的,所以要记录每个doc含有的byte[]的个数,所以要加入第三个方法。如果把addTermsDict方法去掉,就是和SortedNumericDocValue一样了,理解思路是:SortedNumericDocValue也是多个值的,即一个doc含有多个数字,在SortedSetDocValue中也需要使用SortedNumericDocValue,只不过是作为一个doc的多个byte[]的排序,还需要存储具体的byte[],所以要加上第一个部分addTermsDict。有了这三个方法,就可以大致的知道查找的思路了,在查找某个doc的byte[]的时候,先从addressses中查找他的第一个byte[]的排序是多少,一共多少个byte[],这样在从numericField中查找具体的排序,再从termDict中根据排序找到对应的byte[]。下一篇博客中看一下对应的方法吧。

分享到:
评论

相关推荐

    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