热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

数值_Lucene的数值索引以及范围查询

本文由编程笔记#小编为大家整理,主要介绍了Lucene的数值索引以及范围查询相关的知识,希望对你有一定的参考价值。 对文本搜索引擎的倒排索引(数据结构和算法)、评分系统、分词系统都清楚掌握之后,本人对
本文由编程笔记#小编为大家整理,主要介绍了Lucene的数值索引以及范围查询相关的知识,希望对你有一定的参考价值。


对文本搜索引擎的倒排索引(数据结构和算法)、评分系统、分词系统都清楚掌握之后,本人对数值索引和搜索一直有很大的兴趣,最近对Lucene对数值索引和范围搜索做了些学习,并将主要内容整理如下:

1. Lucene不直接支持数值(以及范围)的搜索,数值必须转换为字符(串);

2. Lucene搜索数值的初步方案;

3. Lucene如何索引数值,并支持范围查询。

 

1. Lucene不直接支持数值搜索

Lucene不直接支持数值(以及范围)的搜索,数值必须转换为字符(串)——这是由倒排索引这个核心所决定,lucene要求term按照字典序(lexicographic sortable)排列。如果只是简单的将数值转为字符串,会带来很多的问题:

 

2. Lucene搜索数值的初步方案

2.1 如直接保存11,24,3,50,按照字典序查询范围[24,50],会将3一起带出来。这个问题有个简单的解决方案,就是将字符串补全成定长的串,如000011,000024,000003,000050。这样就能解决[000024,000050]这样的字符范围查询。

2.2 建立索引的时候,term按照数字顺序排序,上面的例子以3,11,24,50,搜索也能正确。

显而易见,上述方案有“硬伤”:

 2.1方案的问题是,固定多少位难以控制,补的位数多则浪费空间,少则存储的数值范围有限;

 2.2方案的问题是,对范围[24,50]查询,必须要展开成25,26...50,这样Boolean query查询效率会低到无法接受。

 

3. Lucene如何索引数值,并支持范围查询

  首先可以把数值转换成字符串,且保持顺序。也就是说如果 number1 < number2 ,那么transform(number) < transform(number)。transform就是把数值转成字符串的函数,如果拿数学术语来说,transform就是单调的。

  *注意, 数字做索引时, 只能是同一类型, 例如不可能是同一个field, 里面有int, 又有float的.

 

 3.1 Lucene 对NumericField建索引的时候,首先把Numeric Value转成 Lexicographic Sortable Binary然后根据某个步长(Precision Step 后面详说)不断右移然后转换成 Lexicographic Sortable String建索引,本质上相当于建了一个Trie。

  怎么把numeric value转成  Lexicographic Sortable Binary 所有的Byte的词典顺序就是Numeric顺序。

  对于Long 二进制表示方式 http://en.wikipedia.org/wiki/Two‘s_complement

  最高位是符号位0表示正数 1表示负数。对于正数来说低63位越大这个数越大,对于负数来说也是低63位越大(0xFFFFFFFFFFFFFFFF是-1,最大的负整数)这个数越大。所以只要把符号位取反Long就可以按字节映射成一个 Lexicographic Sortable Binary了。

 对于Double 二进制表示方式 http://en.wikipedia.org/wiki/Binary64

技术分享图片

The real value assumed by a given 64-bit double-precision datum with a given biased exponent  and a 52-bit fraction is

技术分享图片

对于正Double来说低63位越大这个数越大,对于负Double来说低63位越大这个数越小。负数情况和Long是相反的,因此对于小于0的Double把低63位取反,然后和Long相同再把符号位取反,Double就可以按字节映射成一个 Lexicographic Sortable Binary了。

对于Int和Float 32位的类型一样道理,就不赘述了。

 

 3.2 利用Trie的性质把RangeQuery分解成尽量少TermQuery,然后用这些TermQuery做搜索就可以了

原理就是Shift从0开始以precisionStep为步长递增,对每一个Shift试图找到最多两个子Range:Lower和Upper,然后中间的Range继续递归直到break发生,这时的Range成为Center Range。当Shift=n时,对于split出来的Range满足把minBound的低Shift位全部置0和把maxBound的低Shift位全部置1后之间的所有数值都在要查询的Range中。基本思想和树状数组类似。

 

看例子更容易明白比如[1, 10000]这个Range,通过splitRange出来的Range:

Shift: 0  

Lower: [0x1,0xF],  表示从1到15

Upper: [0x2710,0x2710] 表示10000到10000

Shift: 4

Lower:[0x10, 0xF0]   表示从16(0x10)到255(0xFF) 

Upper:[0x2700, 0x2700]  表示从9984(0x2700)到 9999(0x270F)

Shift: 8

Lower: [0x100,0xF00]  表示从256(0x100)到 4095(0xFFF)

Upper: [0x2000,0x2600] 表示从8192(0x2000)到9983(0x26FF)

Shift: 12

Center: [0x1000, 0x1000] 表示从4096(0x1000)到8191(0x1FFF)

一共7个Range最后一个Range是Center Range, 这7个Range也正好覆盖了[1,10000]

 

addRange中会对每个split出来的Long Range的minBound和maxBoud右移Shift位然后转成Lexicographic Sortable String,最后和建索引时一样在前面加一个Byte表示Shift。因为Shift是以precisionStep为步长递增的,所以splitRange出来的多个Lexicographic Sortable String Range是递增的(Pair顺序比较)。这样查找所有属于这些Range中的Term,只需要对这个field一直seek forward,不需要seek backward。

 

对于上面的例子,这7个Range转换成Lexicographic Sortable String, 然后用这些Range去查找所有属于这些Range范围内的Term。

比如shift: 8

Lower: [0x100,0xF00]  表示从256(0x100)到 4095(0xFFF)

0x100,最高位变成1  成为 0x80,00,00,00,00,00,01,00  然后右移8位变成 0x80,00,00,00,00,00,01 然后每7个bit变成一个Byte成为

0x40, 00, 00, 00, 00, 00, 00,01

0xF00 同理变成0x40, 00, 00, 00, 00, 00, 00,0F。

在最前面加一个Byte表示Shift那么最终的Lexicographic Sortable String

0x100  -> 0x28,40, 00, 00, 00, 00, 00, 00,01

0xF00  -> 0x28,40, 00, 00, 00, 00, 00, 00,0F

第一个Byte 0x28表示Shift为8,0x20是偏移量,区分不同数值类型。

这样如果要查找[256, 4095]的数值共有3840个,那么只需要查找15个Term  

 0x28,40, 00, 00, 00, 00, 00, 00,01 ~  0x28,40, 00, 00, 00, 00, 00, 00,0F

整体来看[0, 10000]之间共1000个数值,最多需要查找的Term数量是55个。

[0x1,0xF]               15 

[0x2710,0x2710]         1 

[0x10, 0xF0]             15

[0x2700, 0x2700]         1

[0x100,0xF00]           15 

[0x2000,0x2600]         7

[0x1000, 0x1000]         1

如果不做Trie树,那么需要最多遍历查找10000个Term。

 

理论上对于precisiOnStep=4时一个Range最多需要查找多少个Term?

根据splitRange可以看出除了最后一次Shift,前面的每次Shift最多产生两个Range(Lower 和 Upper),最后一个Shift产生的是Center Range。

64位的数字Value最多Shift  64/4=16次。 所以最多有Lower和Upper最多各15个Range, Center 1个Range,每个Range最多覆盖15个Term。

为什么不是16个Term?16个Term的话,这个Range的存在是没有意义可以进位到下一个Shift。

只有一种情况是特殊的就是无法进位的时候,比如Range是[Long.MIN_VALUE, Long.MAX_VALUE]  只得到一个Center Range在Shift=60时,覆盖了16个Term的。

所以理论上对precisiOnStep=4,最多需要查找的Term   31个Range * 15个Term/Range = 465

更一般的结论

  n = [ (bitsPerValue/precisionStep - 1) * (2^precisionStep - 1 ) * 2 ] + (2^precisionStep - 1 )

precisiOnStep=8, n=3825

precisiOnStep=2, n=189

显然precisionStep越小n越小,但是precisionStep越小意味着对每个Field需要index的Term越多,对64位的数值需要index的Term是64/precisionStep。

 

以上主要讨论了LongField的搜索,对于DoubleField只是需要做一步处理就是对于小于0的Double,低63位取反,接下来和LongField完全相同流程。对于Int和Float只是数值类型从64位变成32位了,其余的都一样。






推荐阅读
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文由编程笔记#小编整理,主要介绍了关于数论相关的知识,包括数论的算法和百度百科的链接。文章还介绍了欧几里得算法、辗转相除法、gcd、lcm和扩展欧几里得算法的使用方法。此外,文章还提到了数论在求解不定方程、模线性方程和乘法逆元方面的应用。摘要长度:184字。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • 解决Cydia数据库错误:could not open file /var/lib/dpkg/status 的方法
    本文介绍了解决iOS系统中Cydia数据库错误的方法。通过使用苹果电脑上的Impactor工具和NewTerm软件,以及ifunbox工具和终端命令,可以解决该问题。具体步骤包括下载所需工具、连接手机到电脑、安装NewTerm、下载ifunbox并注册Dropbox账号、下载并解压lib.zip文件、将lib文件夹拖入Books文件夹中,并将lib文件夹拷贝到/var/目录下。以上方法适用于已经越狱且出现Cydia数据库错误的iPhone手机。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • 本文讨论了如何使用Web.Config进行自定义配置节的配置转换。作者提到,他将msbuild设置为详细模式,但转换却忽略了带有替换转换的自定义部分的存在。 ... [详细]
author-avatar
伊金芳60442
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有