MyException - 我的异常网
当前位置:我的异常网» 开源软件 » lucene中的docValue实现源码解读(10)——SortedSe

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

www.MyException.Cn  网友分享于:2018-03-12  浏览:0次
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[]。下一篇博客中看一下对应的方法吧。

文章评论

“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
老程序员的下场
老程序员的下场
我的丈夫是个程序员
我的丈夫是个程序员
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
一个程序员的时间管理
一个程序员的时间管理
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
程序员应该关注的一些事儿
程序员应该关注的一些事儿
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
漫画:程序员的工作
漫画:程序员的工作
旅行,写作,编程
旅行,写作,编程
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
程序员都该阅读的书
程序员都该阅读的书
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
10个调试和排错的小建议
10个调试和排错的小建议
Java程序员必看电影
Java程序员必看电影
如何成为一名黑客
如何成为一名黑客
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
我是如何打败拖延症的
我是如何打败拖延症的
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
程序员和编码员之间的区别
程序员和编码员之间的区别
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
总结2014中国互联网十大段子
总结2014中国互联网十大段子
 程序员的样子
程序员的样子
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
每天工作4小时的程序员
每天工作4小时的程序员
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
鲜为人知的编程真相
鲜为人知的编程真相
程序员的鄙视链
程序员的鄙视链
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
中美印日四国程序员比较
中美印日四国程序员比较
那些争议最大的编程观点
那些争议最大的编程观点
程序员必看的十大电影
程序员必看的十大电影
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有