这是《Elasticsearch 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 全文搜索是 Elasticsearch 最核心的能力。它不是简单的字符串包含,而是包括分词、召回、评分、字段权重、短语距离、高亮和多字段组合的完整链路。
本章覆盖 match、operator、phrase、multi-match、高亮、相关性和常见搜索问题。
9.1 match 查询
GET /products/_search
{
"query": {
"match": {
"title": "轻薄笔记本"
}
}
}
执行过程:
查询文本 -> search analyzer 分词 -> 多个词项 -> 倒排索引匹配 -> 计算得分 -> 排序返回
如果分词为:
轻薄
笔记本
默认等价于:
轻薄 OR 笔记本
9.2 operator 与 minimum_should_match
要求所有词都匹配:
{
"match": {
"title": {
"query": "轻薄 笔记本",
"operator": "and"
}
}
}
等价于:
轻薄 AND 笔记本
更灵活的匹配比例:
{
"match": {
"title": {
"query": "轻薄 高刷新率 笔记本 16G",
"minimum_should_match": "75%"
}
}
}
选择建议:
| 场景 | 建议 |
|---|---|
| 搜索召回优先 | 默认 OR |
| 精确短语类查询 | and 或 phrase |
| 长查询多关键词 | minimum_should_match |
| 搜索无结果率高 | 降低匹配门槛或补词表 |
| 无关结果多 | 提高匹配要求或调整字段权重 |
9.3 match_phrase
短语查询要求词项按顺序且距离接近:
GET /products/_search
{
"query": {
"match_phrase": {
"title": "轻薄笔记本"
}
}
}
允许中间插入其他词:
{
"match_phrase": {
"title": {
"query": "轻薄 笔记本",
"slop": 2
}
}
}
slop 是词项移动次数,不是简单“中间允许几个字”。值越大召回越多,但也更容易误召回。
适合:
- 品牌加型号;
- 固定专有名词;
- 错误码短语;
- 标题精确组合。
不适合作为泛搜索的唯一查询,召回会偏少。
9.4 match_phrase_prefix
最后一个词按前缀匹配:
{
"match_phrase_prefix": {
"title": "Elasticsearch sea"
}
}
适合输入框即时搜索,但性能不如专门的 edge_ngram 或 completion suggester。生产搜索建议将补全索引与主搜索索引分开设计。
9.5 multi_match
多字段搜索:
GET /products/_search
{
"query": {
"multi_match": {
"query": "轻薄笔记本",
"fields": ["title^3", "brand^2", "category", "description"],
"type": "best_fields"
}
}
}
^3 表示字段权重提升。
常用类型:
| type | 行为 | 适用 |
|---|---|---|
best_fields |
取最佳字段得分 | 标题、描述等字段竞争 |
most_fields |
多字段得分相加 | 多分词器、多语言召回 |
cross_fields |
跨字段匹配同一组词 | 姓名、地址拆字段 |
phrase |
多字段短语匹配 | 精确组合 |
phrase_prefix |
多字段前缀短语 | 搜索补全 |
best_fields
{
"multi_match": {
"query": "笔记本",
"fields": ["title", "description"],
"type": "best_fields",
"tie_breaker": 0.3
}
}
tie_breaker 会把其他匹配字段得分按比例并入总分,避免只看单一字段。
cross_fields
适合:
first_name = Tom
last_name = Smith
查询:Tom Smith
{
"multi_match": {
"query": "Tom Smith",
"fields": ["first_name", "last_name"],
"type": "cross_fields",
"operator": "and"
}
}
9.6 query_string 与 simple_query_string
query_string 支持 Lucene 语法:
{
"query_string": {
"query": "title:(笔记本 OR 电脑) AND price:[3000 TO 9000]"
}
}
语法错误会导致查询失败,且暴露给用户有风险。
simple_query_string 更宽容:
{
"simple_query_string": {
"query": "笔记本 +轻薄 -二手",
"fields": ["title", "description"]
}
}
用户搜索框通常使用 simple_query_string 或程序生成的 bool/multi_match,不直接暴露 query_string。
9.7 高亮
基础高亮:
GET /products/_search
{
"query": {
"match": { "title": "笔记本" }
},
"highlight": {
"fields": {
"title": {}
}
}
}
自定义标签:
{
"highlight": {
"pre_tags": ["<em class='highlight'>"],
"post_tags": ["</em>"],
"fields": {
"title": {},
"description": { "fragment_size": 120, "number_of_fragments": 2 }
}
}
}
参数:
| 参数 | 说明 |
|---|---|
fragment_size |
片段长度 |
number_of_fragments |
返回片段数 |
pre_tags |
前标签 |
post_tags |
后标签 |
require_field_match |
是否要求字段匹配才高亮 |
高亮会带来额外 CPU 和内存开销,长文本应限制片段数量和长度。
9.8 相关性基础
默认打分算法是 BM25,核心因素:
- 词项频率 TF:词在文档中出现越多,越相关;
- 逆向文档频率 IDF:词越罕见,权重越高;
- 字段长度归一化:同样匹配下,短字段通常更重要;
- 查询词项权重;
- 字段 boost;
- 查询结构。
查看得分解释:
GET /products/_explain/10001
{
"query": {
"match": { "title": "笔记本" }
}
}
常见问题:
| 现象 | 可能原因 |
|---|---|
| 热门商品排后 | BM25 只看文本,不理解销量 |
| 长标题排后 | 字段长度归一化 |
| 常见词权重过高 | 词表或停用词不合理 |
| 品牌不优先 | 字段权重未设置 |
| 结果不稳定 | 分数相同缺少第二排序键 |
业务排序不能完全依赖默认 BM25,通常需要结合销量、库存、新品、转化率、业务权重和人工规则。
9.9 字段权重设计
{
"multi_match": {
"query": "Nova 笔记本",
"fields": [
"title^4",
"brand^3",
"category^2",
"tags^2",
"description"
],
"type": "best_fields"
}
}
权重不是越大越好。过大的 boost 会让文本分数淹没业务分数,建议从 1、2、3 这类小值开始,用真实查询集评估。
9.10 搜索无结果排查
排查步骤:
1. 确认文档存在:GET by id
2. 确认索引和别名正确
3. 检查 refresh 时间
4. 检查 Mapping 类型
5. _analyze 写入分词
6. _analyze 搜索分词
7. 用 match 验证文本召回
8. 用 term 验证 keyword 过滤
9. 检查 bool 是否互相矛盾
10. 检查权限过滤和软删除条件
常见根因:
- text 使用 term;
- keyword 大小写不一致;
- 分词器没有业务词;
- filter 条件冲突;
- 文档尚未 refresh;
- 索引切换后别名错误;
- 数据同步失败;
- nested 查询结构错误。
9.11 召回率与精确率
| 指标 | 含义 | 提升方式 |
|---|---|---|
| 召回率 | 应该返回的结果中被召回比例 | 同义词、多字段、多分词、降低匹配门槛 |
| 精确率 | 返回结果中相关结果比例 | 字段权重、短语查询、过滤、重排 |
搜索优化通常是两者权衡:
宽召回 -> 粗排序 -> 业务过滤 -> 精排/重排 -> 返回
不要只看某几个关键词的手工结果,应建立查询样本集和标注数据。
9.12 搜索日志埋点
推荐记录:
{
"trace_id": "abc",
"query_id": "q-10001",
"user_id": "u8888",
"keyword": "轻薄笔记本",
"filters": { "brand": ["NOVA"], "price": [3000, 9000] },
"took_ms": 35,
"total_hits": 123,
"returned": 20,
"es_indices": ["products_v2"],
"request_json": {},
"created_at": "2026-08-25T10:00:00Z"
}
用于:
- 慢查询分析;
- 无结果查询统计;
- 热门查询词治理;
- 相关性评估;
- 搜索效果迭代。
9.13 本章小结
- match 会先分析查询文本,再与倒排索词项匹配;
- operator 和 minimum_should_match 控制多词匹配门槛;
- match_phrase 强调顺序和距离,适合精确组合;
- multi_match 支持多字段和字段权重;
- 搜索框避免直接暴露 query_string;
- BM25 只理解文本相关性,业务排序需要额外模型或规则;
- 搜索问题要用
_analyze、_explain和搜索日志系统排查。
9.14 思考题
- match 和 term 的本质区别是什么?
- 什么场景用
operator=and,什么场景用minimum_should_match? best_fields和most_fields的差异是什么?- 为什么商品搜索不能只按 BM25 排序?
- 搜索无结果时,你的排查清单是什么?