Skip to content
ES
查询详解

查询详解

ES 的查询 DSL(Domain Specific Language)是其最核心的能力。本文涵盖完整查询分类、相关度评分原理和最佳实践。


查询分类全景

大类 查询类型 说明 参与评分
查询所有 match_all 匹配所有文档
全文检索 match 分词后 OR 匹配
match_phrase 短语精确匹配,保持顺序
match_phrase_prefix 短语前缀匹配(搜索补全)
multi_match 多字段搜索
query_string / simple_query_string 类 Lucene 语法字符串搜索
精确查询 term 词条精确匹配(不分词)
terms 批量词条匹配
range 范围查询
exists 字段非空
prefix 前缀匹配
wildcard 通配符匹配(*?
regexp 正则匹配
fuzzy 模糊/纠错匹配
ids 按 _id 批量查询
地理查询 geo_distance 圆形范围
geo_bounding_box 矩形范围
geo_shape 复杂地理形状
复合查询 bool 多条件组合 子句决定
function_score 自定义评分
constant_score 固定评分(包装 filter)
boosting 降权查询
dis_max 取多查询中最佳匹配
专用查询 nested 嵌套对象查询
has_child / has_parent 父子关系查询
script 脚本过滤
knn 向量近似搜索

基本语法

GET /{index}/_search
{
    "query": {
        "查询类型": {
            "字段名": "查询条件"
        }
    },
    "from": 0,
    "size": 10,
    "_source": ["field1", "field2"],
    "sort": [{ "field": "asc" }]
}

响应关键字段

字段 说明
took ES 执行查询耗时(毫秒)
timed_out 搜索是否超时
_shards.total / successful / failed 分片统计
hits.total.value 匹配文档总数(relation: "eq" 为精确值,"gte" 为估算)
hits.max_score 文档中最高的相关性得分
hits.hits._score 单个文档得分(filter/match_all 上下文为 null
hits.hits._source 文档原始 JSON

🔬 深入原理:ES 7.0+ 的 hits.total 默认只精确计数到 10000track_total_hits: 10000)。超过此阈值返回 "relation": "gte"。需要精确计数时设置 "track_total_hits": true,但这会消耗额外资源。


全文检索

全文检索对搜索条件先分词再匹配,适用于 text 类型字段。

match — 单字段搜索

GET /bank/_search
{
    "query": {
        "match": {
            "address": "mill lane"
        }
    }
}

🔬 深入原理:搜索 "mill lane" → 分词为 ["mill", "lane"] → 默认 OR 关系 → 匹配包含 “mill” “lane” 的文档。

match 的 operator 参数

# AND 关系:同时包含 mill 和 lane
GET /bank/_search
{
    "query": {
        "match": {
            "address": {
                "query": "mill lane",
                "operator": "and"
            }
        }
    }
}

minimum_should_match — 至少匹配 N 个词

# 至少匹配 75% 的词
GET /bank/_search
{
    "query": {
        "match": {
            "description": {
                "query": "快速 可靠 分布式 搜索引擎",
                "minimum_should_match": "75%"
            }
        }
    }
}

match_phrase — 短语匹配

要求词语同时出现顺序一致

GET /bank/_search
{
    "query": {
        "match_phrase": {
            "address": "mill lane"
        }
    }
}
参数 说明 默认值
slop 允许词语间的间隔(slop=1 允许中间隔一个词) 0

match_phrase_prefix — 前缀短语匹配

适合"边输入边搜索"场景:

GET /_search
{
    "query": {
        "match_phrase_prefix": {
            "title": "elasticsea"
        }
    }
}

⚡ 性能提示:前缀匹配可能产生大量 term 枚举。可通过 max_expansions(默认 50)限制。

multi_match — 多字段搜索

GET /bank/_search
{
    "query": {
        "multi_match": {
            "query": "mill lane",
            "fields": ["address", "city", "state"]
        }
    }
}

匹配模式(type 参数):

type 说明
best_fields 取最佳匹配字段的得分(默认)
most_fields 多字段得分相加
cross_fields 跨字段匹配(如 “张三” 匹配 first_name + last_name
phrase 在每个字段上执行 match_phrase
phrase_prefix 在每个字段上执行 match_phrase_prefix
# cross_fields 典型场景:人名跨字段搜索
GET /_search
{
    "query": {
        "multi_match": {
            "query": "张三",
            "fields": ["first_name", "last_name"],
            "type": "cross_fields",
            "operator": "and"
        }
    }
}

精确查询

精确查询不对搜索条件分词,适用于 keyword、数值、日期、布尔等类型。

term — 词条精确匹配

GET /bank/_search
{
    "query": {
        "term": {
            "state": { "value": "ND" }
        }
    }
}

🚨 陷阱:termtext 字段查询可能查不到——写入时 text 已被分词为多个小写 term,而 term 不做分词。对 text 字段用 match,对 keyword 字段用 term

terms — 批量词条匹配

GET /bank/_search
{
    "query": {
        "terms": {
            "state": ["ND", "CA", "NY"]
        }
    }
}

range — 范围查询

GET /bank/_search
{
    "query": {
        "range": {
            "age": {
                "gte": 18,
                "lte": 65
            }
        }
    }
}
参数 含义
gte ≥(大于等于)
gt >(大于)
lte ≤(小于等于)
lt <(小于)
format 日期格式(日期字段专用)
time_zone 时区(日期字段专用)

prefix / wildcard / regexp

# 前缀匹配
GET /_search
{ "query": { "prefix": { "title.keyword": "Elastic" } } }

# 通配符匹配
GET /_search
{ "query": { "wildcard": { "title.keyword": "Elasti*" } } }

# 正则匹配
GET /_search
{ "query": { "regexp": { "title.keyword": "[Ee]lastic.*" } } }

⚡ 性能提示:prefixwildcardregexp 开销大,生产环境谨慎使用。考虑使用 match_phrase_prefix 或 edge n-gram 替代。

fuzzy — 纠错/模糊匹配

# 允许 1 次编辑距离的错误
GET /_search
{
    "query": {
        "fuzzy": {
            "title": {
                "value": "elastc",
                "fuzziness": "AUTO"
            }
        }
    }
}
参数 说明
fuzziness AUTO(默认,自动计算)或数字(编辑距离)
prefix_length 前 N 个字符必须完全匹配(提升性能)

地理查询

geo_distance — 圆形范围

GET /restaurants/_search
{
    "query": {
        "geo_distance": {
            "distance": "5km",
            "location": {
                "lat": 40.73,
                "lon": -74.1
            }
        }
    }
}

geo_bounding_box — 矩形范围

GET /restaurants/_search
{
    "query": {
        "geo_bounding_box": {
            "location": {
                "top_left": { "lat": 40.73, "lon": -74.1 },
                "bottom_right": { "lat": 40.717, "lon": -73.99 }
            }
        }
    }
}

复合查询

bool — 多条件组合

GET /bank/_search
{
    "query": {
        "bool": {
            "must": [
                { "match": { "state": "ND" } }
            ],
            "must_not": [
                { "term": { "gender": "M" } }
            ],
            "should": [
                { "term": { "city": "New York" } },
                { "term": { "city": "Boston" } }
            ],
            "filter": [
                { "range": { "age": { "gte": 18, "lte": 65 } } },
                { "range": { "balance": { "gte": 20000 } } }
            ],
            "minimum_should_match": 1
        }
    }
}
子句 逻辑 参与评分 适用场景
must AND(必须匹配) 核心查询条件
should OR(选择性匹配) 加分条件
must_not NOT(必须不匹配) 排除条件
filter AND(必须匹配) 精确过滤(有缓存)

query vs filter 深度对比

维度 query 上下文 filter 上下文
目的 做排名:匹配越好分数越高 做过滤:在或不在
输出 每个文档都有 _score 无评分(_score: null0
性能 计算 TF-IDF/BM25,较慢 不评分 + 自动缓存(Filter Cache)
缓存 不缓存(不同查询得分不同) 缓存 bitset,重复查询极快

💡 最佳实践:精确值过滤用 filter,全文相关性排序用 must

constant_score — 固定分数

将 filter 包装为 query 上下文并赋予固定分数:

GET /_search
{
    "query": {
        "constant_score": {
            "filter": { "term": { "level": "vip" } },
            "boost": 1.5
        }
    }
}

dis_max — 最佳字段得分

取多查询中得分最高的(而非求和),适合同一个 query 搜多个字段:

GET /_search
{
    "query": {
        "dis_max": {
            "queries": [
                { "match": { "title": "elasticsearch" } },
                { "match": { "body": "elasticsearch" } }
            ],
            "tie_breaker": 0.3
        }
    }
}

🔬 深入原理:tie_breaker(0~1)将非最高得分字段的分数乘以该系数后也纳入总分。0 = 只看最高分(默认),1 = 所有字段等权重加和。

boosting — 降权查询

GET /_search
{
    "query": {
        "boosting": {
            "positive": { "match": { "title": "apple" } },
            "negative": { "match": { "title": "pie" } },
            "negative_boost": 0.5
        }
    }
}

function_score — 自定义评分

修改文档的相关性算分,实现"付费推广"“热门加权"等效果。

image-20260610104700486

基本结构

GET /products/_search
{
    "query": {
        "function_score": {
            "query": { "match": { "title": "手机" } },
            "functions": [
                {
                    "filter": { "term": { "brand": "华为" } },
                    "weight": 2
                },
                {
                    "field_value_factor": {
                        "field": "sales",
                        "factor": 0.1,
                        "modifier": "log1p"
                    }
                }
            ],
            "boost_mode": "multiply",
            "score_mode": "sum",
            "max_boost": 10
        }
    }
}

打分函数类型

函数 说明 示例
weight 常数加权 "weight": 2 — 匹配该 filter 的文档分数乘 2
field_value_factor 用文档中某字段的值 "field": "popularity" — 越热门的文档得分越高
random_score 随机分数 用于随机推荐/AB 测试
script_score Painless 脚本自定义 完全控制评分逻辑
decay 衰减函数(高斯/线性/指数) 距离目标值越近得分越高

评分算法演变

算法 ES 版本 特点
TF-IDF 5.0 之前 词频线性增长,无上限
BM25 5.0 至今 词频增长曲线趋于水平(词频饱和度)

🔬 深入原理:BM25 引入了词频饱和度(Term Frequency Saturation)和文档长度归一化。一个词在文档中出现 1000 次并不意味着相关性是出现 100 次的 10 倍——增长曲线会变平。

重要参数

参数 说明 可选值
boost_mode function score 与 query score 的运算方式 multiply(默认)/ replace / sum / avg / max / min
score_mode 多个 function 之间如何合并 multiply(默认)/ sum / avg / first / max / min
max_boost 限制最高加权倍数(防止极端) 数值

相关度评分详解

explain — 查看评分明细

GET /bank/_explain/1
{
    "query": {
        "match": { "address": "mill lane" }
    }
}

返回每个词条对最终得分的贡献。对于复杂查询,使用 POST /bank/_search + "explain": true 对所有结果输出评分明细。

BM25 公式(简化理解)

score(q, d) = Σ IDF(qi) × TF(qi, d) / (TF(qi, d) + k1 × (1 - b + b × |d|/avgD))
因子 含义
IDF 逆文档频率 — 词在越少文档中出现,权重越大
TF 词频 — 词在本文档中出现次数
** d
avgD 平均文档长度
k1 词频饱和度参数(默认 1.2)
b 长度归一化参数(默认 0.75,0 = 不考虑长度)

💡 最佳实践:大多数场景不需要调整 BM25 参数。但在短文本(如商品标题)和长文本(如文章正文)混合搜索时,可调低 b 值降低长度惩罚。


nested query — 嵌套对象精确查询

普通 object 类型会被扁平化,导致搜索失去关联性。nested 类型保持对象内字段的关联。

# Mapping
PUT /orders
{
    "mappings": {
        "properties": {
            "items": {
                "type": "nested",
                "properties": {
                    "name": { "type": "keyword" },
                    "price": { "type": "double" }
                }
            }
        }
    }
}

# 查询:订单中同时包含 "手机"(价格<5000)和 "手机壳"(价格<50)的商品
GET /orders/_search
{
    "query": {
        "nested": {
            "path": "items",
            "query": {
                "bool": {
                    "must": [
                        { "match": { "items.name": "手机" } },
                        { "range": { "items.price": { "lte": 5000 } } }
                    ]
                }
            }
        }
    }
}
问题 object 类型 nested 类型
存储 扁平化,丢失对象边界 独立 Lucene 文档,保持关联
查询 可能误匹配(跨对象) 精确关联匹配
性能 慢(需要 join)

search_after — 实时深度分页

详见 04-排序分页与高亮


常见陷阱

陷阱 说明
🚨 termtext 字段查不到 text 写入时已分词,term 不做分词导致不匹配
🚨 match 是 OR 关系 "mill lane" 包含 mill lane 即命中
🚨 filter 不参与评分 需要排序的场景必须用 must
🚨 multi_match 字段越多越慢 权衡搜索覆盖度和性能
🚨 prefix/wildcard/regexp 开销大 全表扫描式匹配,大索引中极慢
🚨 object 嵌套查询可能误匹配 跨对象字段交叉匹配;用 nested 类型解决
🚨 hits.total 超过 10000 不精确 需要精确数设 track_total_hits: true
🚨 function_score 不加 max_boost 极端值可能让某些文档完全淹没其他结果