好问题!有了 index 包的底子,看 search 包会顺畅很多。
---
search 包的入口:SearchService
跟 index 包一样,SearchService 是 search 包的"门面",所有搜索请求都会先经过它。
```java
// 搜索服务,每个节点都有一个
public class SearchService {
// 执行搜索
public SearchPhaseResult executeQueryPhase(...)
// 执行获取(取回 _source)
public SearchPhaseResult executeFetchPhase(...)
// 执行聚合
public SearchPhaseResult executeAggregationPhase(...)
}
```
从 SearchService 开始,你会看到这个流程:
```
SearchService.executeQueryPhase()
↓
创建 Context(搜索上下文)
↓
IndexSearcher(Lucene 搜索器)
↓
Query 构建
↓
执行搜索
↓
返回结果
```
---
主线路径(跟着一个搜索请求走)
```
【协调节点】
TransportSearchAction
↓ 广播到所有分片
【数据节点】
SearchService.executeQueryPhase()
↓ 创建搜索上下文
SearchContext
↓ 构建 Lucene Query
QueryBuilder → Lucene Query
↓ 执行搜索
IndexSearcher.search(query)
↓ 返回结果
SearchPhaseResult
↓ 返回给协调节点
【协调节点】
SearchPhaseController.merge()
↓ 合并排序/分页/聚合
返回给用户
```
---
核心类一览
1. SearchService(入口门面)
每个节点上的搜索服务,所有搜索请求的入口。
2. SearchContext(搜索上下文)
一次搜索的上下文,包含查询、排序、聚合等所有信息。
3. QueryPhase(查询阶段)
执行 Lucene 搜索,返回文档 ID + 评分。
4. FetchPhase(取回阶段)
根据文档 ID,去 Lucene 取回 _source 等字段。
5. AggregationPhase(聚合阶段)
执行桶聚合、指标聚合等。
6. SearchPhaseController(协调控制器)
协调节点上,负责合并所有分片的结果。
---
建议的阅读顺序
```
第 1 步:SearchService
→ 找 executeQueryPhase 方法
→ 看它怎么创建 SearchContext
第 2 步:SearchContext
→ 看它里面装了什么(Query、Sort、Aggregations)
第 3 步:QueryPhase
→ 看它怎么把 QueryBuilder 转成 Lucene Query
→ 看它怎么调用 IndexSearcher.search()
第 4 步:FetchPhase
→ 看它怎么根据文档 ID 取回 _source
第 5 步:SearchPhaseController
→ 看它怎么合并多个分片的结果
```
---
一个具体的代码追踪路线
```java
// 1. SearchService 入口
public SearchPhaseResult executeQueryPhase(SearchShardRequest request) {
// 创建搜索上下文
SearchContext context = createContext(request);
// 执行查询
return queryPhase.execute(context);
}
// 2. QueryPhase
public SearchPhaseResult execute(SearchContext context) {
// 构建 Lucene Query
Query luceneQuery = context.query().toQuery(context);
// 执行搜索
IndexSearcher searcher = context.searcher();
TopDocs topDocs = searcher.search(luceneQuery, context.size());
// 返回结果
return new SearchPhaseResult(topDocs);
}
// 3. SearchPhaseController(协调节点)
public void merge(List<SearchPhaseResult> results) {
// 合并所有分片的结果
// 排序、分页、聚合
for (SearchPhaseResult result : results) {
merged.add(result);
}
return merged.sort().paginate();
}
```
---
为什么先看 index 包再看 search 包会更顺?
index 包 search 包
你知道 Lucene 怎么写 你看 Lucene 怎么读
你知道 IndexWriter 是什么 你知道 IndexSearcher 是什么
你知道 Document 是什么 你知道 TopDocs 是什么
你知道 Segment 是什么 你知道 Term 查询怎么工作
search 包的 QueryPhase 本质上就是在:
1. 把用户查询转成 Lucene Query
2. 调用 IndexSearcher.search()
3. 把 Lucene TopDocs 包装成 ES 结果
你看过 Lucene,这个过程你早就懂了!只是看 ES 怎么封装而已。
---
总结
问题 答案
从哪里开始? SearchService
核心类有哪些? SearchService, SearchContext, QueryPhase, FetchPhase, SearchPhaseController
主线路径? SearchService → SearchContext → IndexSearcher → 返回 → 合并
和 index 包的关系? 读依赖于写,所以先看 index 包
你的优势在哪? 懂 Lucene,知道 IndexSearcher 怎么工作
一句话:从 SearchService 开始,顺着 SearchService.executeQueryPhase() → QueryPhase → IndexSearcher.search() 这条线走到底。 你很快就能把整个搜索链路串起来。🚀