ES 9.x 中 bool 查询必须严格区分 must(影响评分)、filter(不评分、可缓存)和 should(可选匹配);不等值条件应置于 filter 内,用 must_not + term 实现 != 逻辑;filter 和 must 的子句必须为索引数组,避免 PHP 关联数组导致 JSON object 被 ES 忽略;所有用户输入需白名单校验防注入。

bool 查询里 filter 和 must 别混用
ES 9.x 要求 bool 查询必须显式区分 must(影响评分)、filter(不评分、可缓存)和 should(可选匹配)。不等值条件如 status != 0、price > 100 属于确定性过滤,应全部放进 filter,而不是 must。
常见错误是把 range 或 terms 写进 must 里,导致相关度打分异常、缓存失效、查询变慢。
-
filter里放:['range' => ['price' => ['gt' => 100]]]、['terms' => ['status' => [1, 2]]]、['bool' => ['must_not' => [['term' => ['deleted' => true]]]]] -
must里只放全文检索项,比如['multi_match' => ['query' => $kw, 'fields' => ['title^3', 'desc']]] - 别在
filter里嵌套must——filter下直接写子句,它本身已具备布尔逻辑能力
!= 条件不能直写 term,得用 must_not + term
Elasticsearch 没有原生 != 操作符。想表达 “status 不等于 0”,不能写 ['term' => ['status' => ['not' => 0]]](语法错误),也不能用 range 硬凑(如 gt: -1),正确做法是用 must_not 包一层 term。
示例:排除已删除商品
['bool' => [
'filter' => [
['must_not' => [['term' => ['is_deleted' => true]]]],
['term' => ['on_sale' => true]],
],
]]
-
must_not必须放在bool的顶层或filter内,不能直接挂在must下 - 多个
must_not条件要合并成一个数组,不要拆成多个must_not键(PHP 数组会覆盖) - 注意字段类型:确保
is_deleted是keyword或boolean类型,否则term匹配失败
PHP 数组结构必须是 array,不是 object
PHP 中用关联数组构造 DSL 时,must、filter、should 等键的值**必须是索引数组**(即 JSON array),否则 json_encode() 会生成 object,ES 直接忽略该子句。
错误写法(filter 变成 object):
$params['query'] = [
'bool' => [
'filter' => ['term' => ['status' => 1]], // ❌ 这是关联数组 → JSON object
],
];
正确写法(加中括号,强制为索引数组):
$params['query'] = [
'bool' => [
'filter' => [
['term' => ['status' => 1]], // ✅ 索引数组 → JSON array
['range' => ['price' => ['gte' => 50]]],
],
],
];
- 所有
must、filter、should、must_not的值都必须是[]形式,哪怕只有一个条件 - 别用
array_merge()合并时漏掉索引重排,会导致 key 变成字符串(如'0' => [...]),仍被当成 object - 调试时可用
dd(json_encode($params, JSON_PRETTY_PRINT))看输出是否含"filter": [{...}],而不是"filter": {...}
参数白名单校验不能省
用户输入的字段名、操作符、值,如果直接拼进 query body,可能触发 script、regexp 或 nested 查询注入。尤其当搜索支持自定义字段(如 sort=price:desc)时,sort 字段若未校验,可能传入 _script 导致任意代码执行。
- 排序字段限定白名单:
['price', 'created_at', 'sales_count'],拒绝_score、_doc、_script - 范围操作符只允许
gt、gte、lt、lte,禁止script、regexp - 关键词先
trim()+strip_tags()+mb_substr($kw, 0, 100)截断,防超长查询拖垮集群 - 类目路径
category_path用prefix查询时,确保 mapping 中该字段是"type": "keyword",否则 prefix 失效
最易被忽略的是 must_not 的嵌套层级和 PHP 数组的索引强制——这两处出错,查询看似正常执行,实则条件被静默丢弃,结果集完全不可控。











