直接在执行前调用$query->toArray()再Json::encode()打印,或在Connection::performRequest()中记录请求体;避免var_dump对象、混用链式与直接赋值、忽略caused_by错误提示。

Yii 中 ElasticSearch 查询没返回结果,怎么看到它实际发了什么 JSON?
直接看请求体是最准的调试方式。Yii 的 ElasticSearch 扩展(比如 yii2-elasticsearch)底层用的是官方 PHP 客户端,但默认不暴露原始查询 JSON。关键不是“怎么配置日志”,而是“在哪一刻把 $query 变成可读的 JSON”。
实操建议:
- 在执行前用
Elasticsearch::buildQuery()(如果用了yii2-elasticsearch的ActiveDataProvider)或手动调用Json::encode($queryArray)打印; - 更可靠的是拦截 HTTP 请求:在
vendor/elasticsearch/elasticsearch/src/Elasticsearch/Connections/Connection.php的performRequest()方法里加file_put_contents('/tmp/es-debug.log', print_r($requestBody, true), FILE_APPEND);; - 别依赖
var_dump($query)—— 它可能是yii\elasticsearch\Query对象,不是数组,直接 encode 会报错;得先调用toArray()或build()。
用 curl 手动复现时,400 Bad Request 错误里说 "failed to parse source" 怎么定位?
这说明 ES 拒绝了解析你发过去的 JSON,常见于字段名拼错、嵌套结构漏了 bool、must 等关键词,或者用了不支持的 DSL 版本语法(比如 ES 7 不再支持 _type)。
实操建议:
- 把 Yii 中打印出的 JSON 复制出来,用
curl -X POST 'http://localhost:9200/your-index/_search' -H 'Content-Type: application/json' -d '@query.json'测试; - 注意检查
query外层是否多包了一层——Yii 的Query::toArray()默认只输出query部分,而 curl 需要完整请求体(含query、size、from等); - ES 返回的错误信息里会有
caused_by字段,重点看它,比如"reason":"[match] query does not support [fuzziness]"就是参数位置错了。
yii\elasticsearch\Query 的 addFilter() 和原生 bool.filter 有什么区别?
没有本质区别,addFilter() 就是帮你往 bool.filter 数组里推数据,但它会自动处理嵌套层级和数组合并逻辑。问题常出在「你以为它合并了,其实覆盖了」或者「多个 addFilter() 调用顺序影响最终结构」。
实操建议:
- 不要混用
addFilter()和直接赋值$query->query = ['bool' => [...]],后者会清空之前所有条件; - 如果要用多个过滤条件,优先写成一个完整的
bool数组再传给query属性,比链式调用更可控; -
addFilter()对range、term这类简单查询友好,但对nested或geo_distance这种带子句的,容易因 key 冲突导致结构错乱。
本地开发开了 Xdebug,但断点进不到 Elasticsearch::search() 怎么办?
因为多数时候你调用的是 ActiveRecord::find()->where(...)->all(),真正触发 ES 请求的是 ActiveDataProvider 的 prepareModels(),而它内部调用的是 Query::search(),不是 Elasticsearch::search()。
实操建议:
- 断点打在
yii\elasticsearch\ActiveDataProvider::prepareModels()或yii\elasticsearch\Query::search()更有效; - 确认你用的是
yii\elasticsearch\ActiveRecord,而不是自己 new 出来的yii\elasticsearch\Elasticsearch实例——前者走的是封装后的查询流程,后者是裸客户端; - 如果用了
yii2-elasticsearch的Query类,它的search()方法最后会调用$this->getConnection()->search(...),这才是最终发请求的地方。
最常被忽略的一点:Yii 的 ES 查询对象在执行前可能被缓存或复用,toArray() 输出的结构未必和最终发送的一致,尤其是用了 with() 或 asArray() 后触发的额外字段处理。真要确认,就得抓网络包或改源码打日志。


















