TermRangeQuery源码解析

本文涉及的产品
Serverless 应用引擎 SAE,800核*时 1600GiB*时
云原生网关 MSE Higress,422元/月
性能测试 PTS,5000VUM额度
简介: 简单介绍下 在较早版本的 Lucene 中对一定范围内的查询RanageQuery 。该Query 继承于 MulitTermQuery,在重写(rewrite )Query 树的时候将会遵从一个原则: 根据起始区间值获取term, 然后遍历,根据满足条件的term 的数目来决定重写Query 的

简单介绍下 在较早版本的 Lucene 中对一定范围内的查询RanageQuery 。该Query 继承于 MulitTermQuery,在重写(rewrite )Query 树的时候将会遵从一个原则:

根据起始区间值获取term, 然后遍历,根据满足条件的term 的数目来决定重写Query 的类型

如下代码所示:

 

 

  (图一)具体见M ultiTermQuery.ConstantScoreAutoRewrite.rewrite() 方法


两种方式区别:


方式一:如果区间范围较大,获取terms 较多则采取Filter 过滤的方式遍历以start 开始的term ,获取[start,end] 的范围内的 TermEnum 从而取出docIDSet 。

 

方式二:如果区间范围不大,获取terms 不多,将区间Query 分解成多个termQuery 独立查询,然后根据BooleanQuery 来合并docId

缺点: 

方式一:只支持字符串形式的范围查询,区间满足的term 数据越多,查询性能越差。

方式二:会构造太多termQuery 很可能造成 TooManyClause 异常,而且获取结果再合并将极大影响性能。

    因为方式二其实现和普通BooleanQuery   --> termQuery 查询方式一致,而本文主要阐述Range 查询,所以将不会方式二实现原理。

    OK,那我们看看TermRangeQuery如何实现查询的,我们知道重写Query树后 ,接下来就是生成weight 树,从图一中可以看到方式一中重写的RangeQuey 被包装成 ConstantScoreQuery(newMultiTermQueryWrapperFilter(query)); 那么从下面的代码实现结构可以看到生成的 weight:

  ConstantScoreQuery . createWeight()

|

|-- new ConstantScoreQuery.ConstantWeight(searcher);

生成 Weight 树后, weight 树将负责 Scorer 树的生成,如下代码实现结构所示 :

ConstantWeight. Scorer()

           |

           |-- new ConstantScorer ( similarity , reader, this );

                  |

                 |-- DocIdSet docIdSet  =  MultiTermQueryWrapperFilter .getDocIdSet(reader) ;

                        |--    DocIdSetIterator iter = docIdSet.iterator();

                        |--      docIdSetIterator = iter;

 

Query 树 ->weight 树 ->Scorer 树生成后,将开始打分并收集 docId 的过程。如下所示:

Scorer scorer = weight.scorer();

          |

          |-- scorer.score(collector);//scorer= ConstantScorer

整个 score 过程是遍历直到取出的值 == NO_MORE_DOCS 。见如下代码所示:

 

 

而 nextDoc 由 ConstantScorer 实现:


 

 

结合ConstantScorer的构造函数可以看到整个docId的范围过滤都在:

 

完成,接下来在看看该方法的具体实现:

 


MultiTermQueryWrapperFilter .getDocIdSet(reader) ;

               |                   // 得到 TermRangeQuery 的 Term 枚举器

               |-- final TermEnum enumerator = query .getEnum(reader);

                                                            |         

                                                           |-- new TermRangeTermEnum(reader, field , lowerTerm , upperTerm , includeLower , includeUpper , collator );

TermRangeTermEnum的构造函数 其包含的成员变量如下:

_ String lowerTerm; 左边界字符串

_ String upperTerm; 右边界字符串

_ boolean includeLower; 是否包括左边界

_ boolean includeUpper; 是否包含右边界

_ String field; 域

_ Collator collator; 其允许用户实现其函数 int compare(String source, String target) 来决定怎么样算是大于,怎么样算是小于。

TermRangeTermEnum 来保证满足区间条件的 term 能被 MultiTermQueryWrapperFilter. TermGenerator.generate() 方法收集到OpenBitSet中,如下所示:

 

 

 而generate()具体实现为:

 

 

  从上述代码可以看出TermRangeTermEnum最关键的2个方法就是term()和next()方法,

(1) TermRangeTermEnum.term()方法是获取遍历过程中当前的term。

(2) TermRangeTermEnum.next()方法是遍历Term的枚举列表

在 TermRangeTermEnum 并没有重写 next() 方法,所以从父类 FilteredTermEnum 中的 next 可以看到:

 

 

 

从上述代码可以得知遍历结束取决:

(1)  endEnum()==true;// 结束枚举遍历

(2)  actualEnum.next()==false;// 整个term 的枚举遍历完毕

(3)  termCompare(term)==false;// 当前的term 字符串不在[start,end] 区间范围内

而从termRanageTermEnum 中endEnum 其实也是由termCompare(term) 方法来影响,所以人为能影响的区间查询都由termCompare 方法来决定,代码如下所示:

 

 

如上代码所述, 区间查询的时间是和区间范围内term 的个数有关系的,也就是说如果区间范围越大,意味着查询的next() term 的次数也会更多。

另外该范围查询只支持字符串范围查询,并不支持数值型的范围查询。所以 从 Lucene 2.9 开始,Lucene 提供对数字范围的支持,但是然而欲使用此查询,必须使用 NumericField 来添加域。 这也是NumericField和numericRangeQuery结合起来的数值型范围查询。

相关文章
|
2月前
|
监控 网络协议 Java
Tomcat源码解析】整体架构组成及核心组件
Tomcat,原名Catalina,是一款优雅轻盈的Web服务器,自4.x版本起扩展了JSP、EL等功能,超越了单纯的Servlet容器范畴。Servlet是Sun公司为Java编程Web应用制定的规范,Tomcat作为Servlet容器,负责构建Request与Response对象,并执行业务逻辑。
Tomcat源码解析】整体架构组成及核心组件
|
26天前
|
存储 缓存 Java
什么是线程池?从底层源码入手,深度解析线程池的工作原理
本文从底层源码入手,深度解析ThreadPoolExecutor底层源码,包括其核心字段、内部类和重要方法,另外对Executors工具类下的四种自带线程池源码进行解释。 阅读本文后,可以对线程池的工作原理、七大参数、生命周期、拒绝策略等内容拥有更深入的认识。
什么是线程池?从底层源码入手,深度解析线程池的工作原理
|
1月前
|
开发工具
Flutter-AnimatedWidget组件源码解析
Flutter-AnimatedWidget组件源码解析
148 60
|
26天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
222 37
|
5天前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
37 9
|
18天前
|
编解码 开发工具 UED
QT Widgets模块源码解析与实践
【9月更文挑战第20天】Qt Widgets 模块是 Qt 开发中至关重要的部分,提供了丰富的 GUI 组件,如按钮、文本框等,并支持布局管理、事件处理和窗口管理。这些组件基于信号与槽机制,实现灵活交互。通过对源码的解析及实践应用,可深入了解其类结构、布局管理和事件处理机制,掌握创建复杂 UI 界面的方法,提升开发效率和用户体验。
91 12
|
2月前
|
测试技术 Python
python自动化测试中装饰器@ddt与@data源码深入解析
综上所述,使用 `@ddt`和 `@data`可以大大简化写作测试用例的过程,让我们能专注于测试逻辑的本身,而无需编写重复的测试方法。通过讲解了 `@ddt`和 `@data`源码的关键部分,我们可以更深入地理解其背后的工作原理。
36 1
|
2月前
|
开发者 Python
深入解析Python `httpx`源码,探索现代HTTP客户端的秘密!
深入解析Python `httpx`源码,探索现代HTTP客户端的秘密!
74 1
|
2月前
|
开发者 Python
深入解析Python `requests`库源码,揭开HTTP请求的神秘面纱!
深入解析Python `requests`库源码,揭开HTTP请求的神秘面纱!
134 1
|
2月前
|
算法 安全 Java
深入解析Java多线程:源码级别的分析与实践
深入解析Java多线程:源码级别的分析与实践

推荐镜像

更多