Lucene5学习之NumericRangeQuery使用

  说到NumericRangeQuery查询,你们肯定觉得很简单,不就是数字范围查询吗?用户提供一个上限值和一个下限值,底层API里直接>min,<max,真的是这样吗?其实在Lucene里只能对字符串String建立索引,那么数字怎么转成String,你肯定又会想当然的认为toString()一下就OK啦?OK,假如真的是这样的,那字符串"3" > "26"问题怎么解决?OK,可以通过在数字前面加前导零解决,“03”<"26"是没错,可是前导零加几位没法确定,加多了浪费硬盘空间,加少了支持索引的数字位数受限。即使你解决了位数受限问题,但Lucene里的范围查询本质还是通过BooleanQuery进行条件连接起来的,term条件太多还是会出现too many boolean Clause异常的。其实Lucene内部是把数字(int,long,float,double)转成十六进制的数字来处理的。具体怎么转成的请参看NumericUtils这个工具类的源码,

 

Java代码  

  1. /** 
  2.    * Converts a <code>float</code> value to a sortable signed <code>int</code>. 
  3.    * The value is converted by getting their IEEE 754 floating-point "float format" 
  4.    * bit layout and then some bits are swapped, to be able to compare the result as int. 
  5.    * By this the precision is not reduced, but the value can easily used as an int. 
  6.    * @see #sortableIntToFloat 
  7.    */  
  8.   public static int floatToSortableInt(float val) {  
  9.     int f = Float.floatToRawIntBits(val);  
  10.     if (f<0) f ^= 0x7fffffff;  
  11.     return f;  
  12.   }  

 上面贴的就是把float转成十六进制的数字的代码,里面尽是位运算,看的人晕晕的,要完全搞懂,不是一件容易的事情。

 

   为了减少BooleanQuery条件太多的问题,采用了Trie树结构来存储Term,这又涉及到Trie树算法,又是一道坎儿,不懂算法,内部实现又看不懂,心塞塞啊!

 

Java代码  

  1. /** This helper does the splitting for both 32 and 64 bit. */  
  2.   private static void splitRange(  
  3.     final Object builder, final int valSize,  
  4.     final int precisionStep, long minBound, long maxBound  
  5.   ) {  
  6.     if (precisionStep < 1)  
  7.       throw new IllegalArgumentException("precisionStep must be >=1");  
  8.     if (minBound > maxBound) return;  
  9.     for (int shift=0; ; shift += precisionStep) {  
  10.       // calculate new bounds for inner precision  
  11.       final long diff = 1L << (shift+precisionStep),  
  12.         mask = ((1L<<precisionStep) - 1L) << shift;  
  13.       final boolean  
  14.         hasLower = (minBound & mask) != 0L,  
  15.         hasUpper = (maxBound & mask) != mask;  
  16.       final long  
  17.         nextMinBound = (hasLower ? (minBound + diff) : minBound) & ~mask,  
  18.         nextMaxBound = (hasUpper ? (maxBound - diff) : maxBound) & ~mask;  
  19.       final boolean  
  20.         lowerWrapped = nextMinBound < minBound,  
  21.         upperWrapped = nextMaxBound > maxBound;  
  22.         
  23.       if (shift+precisionStep>=valSize || nextMinBound>nextMaxBound || lowerWrapped || upperWrapped) {  
  24.         // We are in the lowest precision or the next precision is not available.  
  25.         addRange(builder, valSize, minBound, maxBound, shift);  
  26.         // exit the split recursion loop  
  27.         break;  
  28.       }  
  29.         
  30.       if (hasLower)  
  31.         addRange(builder, valSize, minBound, minBound | mask, shift);  
  32.       if (hasUpper)  
  33.         addRange(builder, valSize, maxBound & ~mask, maxBound, shift);  
  34.         
  35.       // recurse to next precision  
  36.       minBound = nextMinBound;  
  37.       maxBound = nextMaxBound;  
  38.     }  
  39.   }  

 说实话,我还没有完全参透这段源码,留着以后有空研究算法的时候再来啃这块骨头吧。

 

   上面说了一大堆废话,都是涉及底层数字范围查询设计原理的东西,只说了个大概,具体实现涉及的算法和原理我也还没参透,表示很抱歉,如果你对这方面算法很了解,麻烦请告知我,谢谢!

    NumericRangeQuery原理理解起来很难,但使用起来却是非常简单: 

 

Java代码  

  1. Query q = NumericRangeQuery.newFloatRange("weight", 0.03f, 0.10f, true, true);  

    后面两个boolean值用来控制是否包含两个上下边界值的。

 

    不过要注意的是NumericRangeQuery只对IntField,LongField,FloatField,DoubleField等这些表示数字的Field域有效,NumericRangeQuery还有一个比较重要的设置就是Precision Step,何为Precision Step呢?翻译过来就是精度步长,还是不够直观无法理解,对不对?说通俗一点就是拿多大一个长度来截取Term,因为你的数字转成十六进制的字符串后,可能很长,需要按照一定的步长截取成多个Term进行索引的,比如“1111101111111011”,如果你的Precision Step值为16的话(不同数据类型的步长默认值不同,都定义在NumericUtils工具类里),那最终只有1个term,如果Precision Step值为8,那最终索引中就会有2个Term,这就是为什么官方API里说percisionStep值越小会越占硬盘空间但搜索速度越快了。Term多了肯定越占硬盘空间了。 NumericRangeQuery就说到这儿了,Thanks all.

    如果你还有什么问题请加我Q-Q:7-3-6-0-3-1-3-0-5,

或者加裙
一起交流学习!

转载:http://iamyida.iteye.com/blog/2194799

时间: 2024-08-05 13:32:28

Lucene5学习之NumericRangeQuery使用的相关文章

Lucene5学习之Filter过滤器

  清明3天假,我猜小伙伴们都相约出去玩去了,对于我等屌丝来说,唯有在家写代码打发时间了.其实不是我喜欢宅,只是一个人去哪儿都没有激情,还不如在家安安静静的看看书写写代码来的安逸,对这个看脸的世界就差绝望了,就算代码虐我千万遍,我待代码还是如初恋啊!今天从早上9点起来,就中午做个饭,一坐就是整整10个小时,照着我预想的计划继续记录我的Lucene5学习轨迹,由于Filter体系下子类有点多,还要编写测试demo,所以这篇博客有点姗姗来迟,请大家多多包涵!                    

Lucene5学习之自定义排序

         在Lucene5学习之排序-Sort中,我们已经学习了Sort的用法,已经了解了,Lucene搜索返回的命中结果默认是按照索引文档跟搜索关键字的相关度已经排序的,而相关度又是基于内部的打分机制和索引文档id,内部的打分机制则是根据Term的IDF-TF以及创建索引时Field的boost等决定的,默认是按照得分降序排序,得分相同再按docId升序排序.如果你觉得默认的排序方式满足不了你的需求,你可以设置SortField按照特定的域来排序,特定的域排序其实根据域的type类型去

Lucene5学习之多线程创建索引

    昨晚睡觉前把多线程创建索引demo写好了,今天早上7点多就起来,趁着劲头赶紧记录分享一下,这样对那些同样对Lucene感兴趣的童鞋也有所帮助.     我们都知道Lucene的IndexWriter在构造初始化的时候会去获取索引目录的写锁writerLock,加锁的目的就是保证同时只能有一个IndexWriter实例在往索引目录中写数据,具体看截图:  而在多线程环境下,光保证只有IndexWriter实例能得到锁还不行,还必须保证每次只能有一个线程能获取到writerLock,Luce

Lucene5学习之TermRangeQuery使用

    TermRangeQuery是用于字符串范围查询的,既然涉及到范围必然需要字符串比较大小,字符串比较大小其实比较的是ASC码值,即ASC码范围查询.一般对于英文来说,进行ASC码范围查询还有那么一点意义,中文汉字进行ASC码值比较没什么太大意义,所以这个TermRangeQuery了解就行,用途不太大,一般数字范围查询NumericRangeQuery用的比较多一点,比如价格,年龄,金额,数量等等都涉及到数字,数字范围查询需求也很普遍.     我们来看看官方API里是怎么解释这个Que

Lucene5学习之Facet(续)

     默认Facet是统计落入某一组域值的总数的,然后按照总数从大到小排序,判定规则是域值是否相同,其实还可以根据域值是否在某个范围内来判定是否落入某一个分组.这里说的范围就是通过Range定义的,比如: Java代码          /**1小时之前的毫秒数*/   final LongRange PAST_HOUR = new LongRange("Past hour", this.nowSec - 3600L,           true, this.nowSec, tr

Lucene5学习之Suggest关键字提示

     首先需要搞清楚Suggest模块是用来解决什么问题的?Google我想大家都用过,当我们在搜索输入框里输入搜索关键字的时候,紧贴着输入框下方会弹出一个提示框,提示框里会列出Top N个包含当前用户输入的搜索关键字的搜索热词,如图:       这里说的不是前端的这种JS效果,而说的是输入一个关键字如何获取相关的搜索热词,至于js效果,自己Google jQuery自动补全插件,我以前玩过,这里关注的是提示数据如何获取,当然你也可以使用数据库SQL like "%xxxx%"来

Lucene5学习之FunctionQuery功能查询

    我猜,大家最大的疑问就是:不是已经有那么多Query实现类吗,为什么又设计一个FunctionQuery,它的设计初衷是什么,或者说它是用来解决什么问题的?我们还是来看看源码里是怎么解释FunctionQuery的:         意思就是基于ValueSource来返回每个文档的评分即valueSourceScore,那ValueSource又是怎么东东?接着看看ValueSource源码里的注释说明:  ValueSource是用来根据指定的IndexReader来实例化Funct

Lucene5学习之创建索引入门示例

    Lucene更新实在太快了,只好紧跟脚步开始学习Lucene5,花了点时间写了一个demo,就是程序根据用户提供的一个文件夹,读取该文件夹下的所有文件,然后读取文件里的内容写入索引.读取文件部分采用的是最新的NIO2.0API,因此,JDK必须使用1.7及以上版本.Lucene5开发压缩包请在Lucene官网下载.不多说了,对于码农来说,最直接的就是上代码. Java代码   package com.yida.framework.lucene5.core;      import jav

Lucene5学习之SpanQuery跨度查询

    SpanQuery下的子类有好几个,我就放一篇里集中说说.SpanQuery即跨度查询,首先要理解跨度这个概念,Lucene里跨度是用Spans这个类定义的,源码如下:   Java代码   /** Expert: an enumeration of span matches.  Used to implement span searching.   * Each span represents a range of term positions within a document.