复杂查询(wildcard、range、geo、script)分别说一下它们是怎么重写的?

下面把四类“复杂查询”在 Lucene/Elasticsearch 中的**重写(rewrite)流程**拆开说:  
核心思路都是“**先把索引里真正命中的底层 Term 或点集合找出来,再组合成可快速倒排扫描的原子查询**”。

-------------------------------------------------
1. WildcardQuery  
-------------------------------------------------
原始查询  
```
WildcardQuery("title", "jav*")
```
重写步骤  
1. 读取 title 字段的 **TermsEnum**(已按字典序排序)。  
2. 用 **Automaton**(通配符自动机)在字典里 **顺序遍历** 所有以 `jav` 开头的真实词项,例如:`java`, `javascript`, `javaw`。  
3. 把这些词项封装成一个 **BooleanQuery**(默认 SHOULD):  
   ```
   BooleanQuery
   ├── TermQuery(title:java)
   ├── TermQuery(title:javascript)
   └── TermQuery(title:javaw)
   ```
4. 如果命中的 Term 太多(默认 ≥1024,受 `indices.query.bool.max_clause_count` 限制),会改写成 **MultiTermQueryConstantScoreWrapper**(常量分数 + 位图合并),避免 BooleanQuery 爆炸。

-------------------------------------------------
2. RangeQuery(numeric/date/ip)  
-------------------------------------------------
原始查询  
```
RangeQuery("price", [50, 100)
```
重写步骤  
1. 判断字段类型:  
   • keyword/text → 退化成 **TermRangeQuery**,过程同 Wildcard 的顺序遍历。  
   • numeric/date/ip → 走 **PointRangeQuery**(BKD 树)。  
2. 在 BKD 树中 **点范围查询**,一次性拿到所有落在 `[50, 100)` 的 **docId 位图**。  
3. 重写成 **PointRangeQuery**(内部直接返回位图),不再拆 Term,速度 O(log N)。

-------------------------------------------------
3. Geo 查询  
-------------------------------------------------
以 `GeoDistanceQuery` 为例:  
1. 计算中心点 + 半径 → 得到 **覆盖矩形**。  
2. 在 BKD 树(geo_point 字段)中 **行/列扫描** 拿到矩形内的所有点。  
3. 再精确 **Haversine 公式** 过滤掉矩形四角外的点。  
4. 最终重写成 **LatLonPointDistanceQuery**(位图),无 Term 级拆分。

-------------------------------------------------
4. ScriptQuery  
-------------------------------------------------
原始查询  
```
ScriptQuery("doc['price'].value * doc['discount'].value > 40")
```
重写步骤  
1. **无法拆成 Term**,因为逻辑是任意代码。  
2. 重写阶段 **原样保留**(`ScriptQuery` 本身已是原子查询)。  
3. 执行时变成真正的 **逐文档脚本计算**(最昂贵,无法走倒排索引)。  
4. 如果脚本里引用了 `_score`,还会强制收集原始分数;否则可跳过。

-------------------------------------------------
一句话速记  
- Wildcard/Prefix → 拆 **Term 列表** → BooleanQuery  
- Range/Geo → 直接走 **BKD 树** → 位图  
- Script → **不拆分**,执行期逐条算  

重写结束后,QueryPhase 拿到全是可位图/跳表直接算交集的“原子查询”,才开始真正的倒排扫描。

### `wildcard` 函数的作用 在 Makefile 中,`wildcard` 是一个用于文件路径匹配的函数,其主要作用是根据指定的模式匹配当前系统中已存在的文件,并返回一个以空格分隔的文件列表。该函数在处理通配符时非常有用,特别是在变量定义或函数调用中,普通的通配符(如 `*`)不会自动展开,这时就需要使用 `wildcard` 来实现通配符匹配功能[^2]。 ### 使用方式 `wildcard` 函数的调用格式如下: ```makefile $(wildcard PATTERN...) ``` 其中 `PATTERN` 是一个或多个文件模式,多个模式之间使用空格分隔。`wildcard` 会根据这些模式查找匹配的文件,并返回匹配的文件列表。如果没有任何匹配的文件,`wildcard` 会返回空值[^3]。 例如,如果需要获取 `main` 目录、`hw` 目录和 `api` 目录下的所有 `.c` 文件,并将其赋值给变量 `SRCS_C`,可以写成: ```makefile SRCS_C := $(wildcard main/main.c) \ $(wildcard hw/*.c) \ $(wildcard api/*.c) ``` 同样,如果需要获取 `t32_api` 目录和 `cpp_dir` 目录下的所有 `.cpp` 文件,并赋值给 `SRCS_CPP`,可以写成: ```makefile SRCS_CPP := $(wildcard t32_api/*.cpp) \ $(wildcard cpp_dir/*.cpp) ``` 上述示例展示了如何使用 `wildcard` 函数获取特定目录下的所有源文件,这在构建大型项目时非常有用,因为它可以自动识别源文件,而无需手动列出所有文件名[^4]。 ### 与其他通配符处理方式的区别 在 Makefile 的规则中,通配符通常会自动展开。但在变量定义或函数调用中,通配符不会自动展开,因此需要使用 `wildcard` 函数来显式展开。例如,以下写法无法正确获取所有 `.c` 文件: ```makefile SRCS = *.c ``` 在这种情况下,`SRCS` 的值会被直接设置为 `*.c` 字符串,而不是匹配的文件列表。使用 `wildcard` 可以解决这个问题: ```makefile SRCS := $(wildcard *.c) ``` 此时,`SRCS` 将包含所有当前目录下的 `.c` 文件列表。 ### 示例明 假设当前目录结构如下: ``` . ├── main.c ├── foo.c ├── bar.c └── test.c ``` 使用以下 Makefile 代码: ```makefile SRCS := $(wildcard *.c) all: @echo $(SRCS) ``` 执行 `make all` 时,输出结果将是: ``` bar.c foo.c main.c test.c ``` 这表明 `wildcard` 成功匹配了所有 `.c` 文件,并将它们按字母顺序排列后返回[^5]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值