lucene中的docValue实现源码解读(五)——BinaryDocValue的读取

阅读更多

BinaryDocValue的读取和NumericDocValue的读取的原理是差不多的,都是在打开一个新的段时,先读取meta文件,也就是 索引文件,读取到内存,然后保存起来,再用刀某个doc的docValue的时候再去读data文件。看下读取meta文件的代码吧,是在Lucene410DocValuesProducer.readBinaryEntry(IndexInput)中:

static BinaryEntry readBinaryEntry(IndexInput meta) throws IOException {
	BinaryEntry entry = new BinaryEntry();
	entry.format = meta.readVInt();//存储的格式
	entry.missingOffset = meta.readLong();//记录那些含有值的docSet的fp
	entry.minLength = meta.readVInt();//byte[]长度的最小值
	entry.maxLength = meta.readVInt();//byte[]长度的最大值
	entry.count = meta.readVLong();//所有的doc的数量
	entry.offset = meta.readLong();//真正存储docValue的部分的fp
	switch (entry.format) {
	case BINARY_FIXED_UNCOMPRESSED://没有压缩的,说明是长度一致的,直接根据id * minLength找到开始位置。
		break;
	case BINARY_PREFIX_COMPRESSED://没有使用!
		entry.addressesOffset = meta.readLong();
		entry.packedIntsVersion = meta.readVInt();
		entry.blockSize = meta.readVInt();
		entry.reverseIndexOffset = meta.readLong();
		break;
	case BINARY_VARIABLE_UNCOMPRESSED:
		entry.addressesOffset = meta.readLong();//这个是记录每个doc的byte[]的长度的那一块的fp。
		entry.packedIntsVersion = meta.readVInt();
		entry.blockSize = meta.readVInt();//这个是读取一个块的大小,因为在存储那些数子的时候,是按照块来存储的。
		break;
	default:
		throw new CorruptIndexException("Unknown format: " + entry.format + ", input=" + meta);
	}
	return entry;
}

 这样读取了meta文件,然后保存在内存中,我们看下具体怎么读取每个doc的docValue的吧:代码在Lucene410DocValuesProducer.getBinary(FieldInfo)中,

public BinaryDocValues getBinary(FieldInfo field) throws IOException {
	BinaryEntry bytes = binaries.get(field.number);
	switch (bytes.format) {
	
	case BINARY_FIXED_UNCOMPRESSED:
		return getFixedBinary(field, bytes);//所有的byte[]长度一致的格式
	case BINARY_VARIABLE_UNCOMPRESSED:
		return getVariableBinary(field, bytes);//byte[]长度不一样的格式
	case BINARY_PREFIX_COMPRESSED://没有用刀
		return getCompressedBinary(field, bytes);
	default:
		throw new AssertionError();
	}
}

 挨个看看,先看byte[]长度都一样的情况:

private BinaryDocValues getFixedBinary(FieldInfo field, final BinaryEntry bytes) throws IOException {
	// 因为已经知道了每个byte[]的长度,所以直接乘以数量,此时不会有missing的,即所有的doc都含有值,所以count就是值得个数
	final IndexInput data = this.data.slice("fixed-binary", bytes.offset, bytes.count * bytes.maxLength);//从索引中获得指定位置的数据
	final BytesRef term = new BytesRef(bytes.maxLength);
	final byte[] buffer = term.bytes;//读取到这个里面来
	final int length = term.length = bytes.maxLength;
	return new LongBinaryDocValues() {
		@Override
		public BytesRef get(long id) {
			try {
				data.seek(id * length);//找到开始的位置
				data.readBytes(buffer, 0, buffer.length);//读取一个byte[]
				return term;
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	};
}

可以发现,如果 byte[]的长度都一样,是很容易理解的。直接用id*每个byte[]的长度即可,而且byte[]的长度是已知的,因为最大值和最小值都保存了。再看下不一样长的: 

 

private BinaryDocValues getVariableBinary(FieldInfo field, final BinaryEntry bytes) throws IOException {
		
	//读取位置,因为当byte[]不一样的时候,会保存每个doc的byte[]的长度,这里就是将每个doc的长度读取出来(其实是按照块读取的,并不是每个doc都读取出来,不过我们不研究那个,这里假设是都读取出来了,或者是提供的功能能够得到每个doc的byte[]的开始位置)
	final MonotonicBlockPackedReader addresses = getAddressInstance(field, bytes);
	//将所有的值读取出来(也就是所有的doc的byte[])
	final IndexInput data = this.data.slice("var-binary", bytes.offset, bytes.addressesOffset - bytes.offset);
	final BytesRef term = new BytesRef(Math.max(0, bytes.maxLength));
	final byte buffer[] = term.bytes;

	return new LongBinaryDocValues() {
		@Override
		public BytesRef get(long id) {
			long startAddress = addresses.get(id);//当前doc的开始位置
			long endAddress = addresses.get(id + 1);//当前doc的结束位置
			int length = (int) (endAddress - startAddress);//长度,如果一个doc没有值,那么他的下一个doc的开始位置他的开始位置是一样的,则这个是0
			try {
				data.seek(startAddress);
				data.readBytes(buffer, 0, length);
				term.length = length;
				return term;//返回读取到的byte[].
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	};
}

这样也就看完了长度不一致的读取,在读取的时候,会根据id得到开始位置, 然后再从那个大大的byte[]中根据偏移量读取。他的效率不如长度一致的byte[]的高,因为要读取两次,而长度一致的只需要读取一次。还有需要注意的就是,任何一个doc都会读取到一个BytesRef,所以要看那个记录有没有值的bitset才能决定这个doc有没有binaryDocValue。 

 

你可能感兴趣的:(lucene,docvalue,binaryDocValue,存储格式)