ES查询需严格区分term(keyword字段精确匹配)和match(text字段全文检索),bool中等值条件应放filter,参数须白名单校验并过滤非法字符,PHP构造时注意数组嵌套与key拼写。

ES 查询里 term 和 match 别混用
term 是精确匹配,match 是全文检索,混用会导致 DSL 语法不合法或结果错乱。比如对 keyword 类型字段用 match,Elasticsearch 会报 text fields must be searched with match queries;反过来对 text 字段用 term,则查不到内容(因为分词后没完全一致的 token)。
- 字段类型为
keyword(如 status、category_id、user_id)→ 一律用term或terms - 字段类型为
text(如 title、description)→ 用match、multi_match,别碰term - 不确定类型时,先查 mapping:
GET /your_index/_mapping,看字段的"type"值
bool 查询中 filter 和 must 别放反
filter 不参与评分、可缓存,must 影响相关度打分。把等值条件(如 status: "published")写进 must 会导致无关文档被拉进来,还拖慢响应;写进 filter 才是正确姿势。
- 等值、范围、存在性判断(
term,range,exists)→ 全部塞进filter数组 - 关键词搜索(
match,multi_match)→ 放must或should(设minimum_should_match: 1) - 错误示例:
['must' => [['term' => ['status' => 'active']]]]→ 应改为['filter' => [['term' => ['status' => 'active']]]]
参数白名单校验不能跳过
用户输入直接拼进 query body 会触发 script 注入或恶意布尔表达式,比如传 status=*" OR 1=1 这类 payload。Elasticsearch 不像 MySQL 有预编译机制,靠的是结构校验。
- 所有查询字段名(如
status,category_id)必须从白名单数组里取:['status', 'category_id', 'is_featured'] - 值要做基础过滤:
trim()+mb_substr($val, 0, 64),避免超长字符串撑爆集群 - 禁止字段名含点号或下划线开头(防访问内部元字段),用正则
/^[a-z][a-z0-9_]*$/校验
PHP 客户端构造 bool 查询容易漏括号
原生客户端(elasticsearch/elasticsearch)要求 query 结构严格嵌套,少一层数组或错位 key 就抛 BadRequest400Exception。常见是 bool 下漏了 filter 或 must,或者把 term 写成 terms 却传单值。
- 写完 query 先 dump 出来:
var_dump($params['body']);,确认结构层级和 key 名拼写 -
term接字符串,terms接数组 ——['term' => ['status' => 'active']]✔️,['terms' => ['status' => 'active']]❌ - 多条件组合时,用
array_merge_recursive()比手拼更安全,但注意它会把同 key 的数组合并,慎用于should这种需保留顺序的场景
text 却硬要用 term 查,或者 keyword 字段忘了加 .keyword 后缀(7.x 以前)。上线前跑一遍 mapping 检查比调半天 query 更省时间。


















