PHP中使用json_encode生成Elasticsearch查询JSON结构的问题
PHP构建Elasticsearch查询时json_encode的数组结构问题
我尝试用PHP数组构建Elasticsearch查询,再用json_encode()转成JSON,但Elasticsearch无法识别这个JSON。
我的PHP数组片段:
'query' => [ 'bool' => [ 'must' => [ 'multi_match' => [ 'query' => $request->get('keyword'), 'fields' => ['addressLine1', 'addressCity', 'contactName'] ], 'bool' => [ 'should' => [ 'range' => [ '_metrics.count' => [ 'gte' => 1 ] ] ] ] ], 'filter' => [ 'term' => [ 'organisationId.keyword' => [ 'value' => $organisationId ] ] ] ] ]
执行json_encode($params)后得到的JSON:
{ "query":{ "bool":{ "must":{ "multi_match":{ "query":"277 Some Road", "fields":[ "addressLine1", "addressCity", "contactName" ] }, "bool":{ "should":{ "range":{ "_metrics.count":{ "gte":1 } } } } }, "filter":{ "term":{ "organisationId.keyword":{ "value":"xxxx-xxxx-xxxx-xxxx" } } } } } }
这个JSON语法合法,但发送给Elasticsearch时返回错误:
{ "error": { "root_cause": [ { "type": "parsing_exception", "reason": "[multi_match] malformed query, expected [END_OBJECT] but found [FIELD_NAME]", "line": 16, "col": 13 } ], "type": "x_content_parse_exception", "reason": "[16:13] [bool] failed to parse field [must]", "caused_by": { "type": "parsing_exception", "reason": "[multi_match] malformed query, expected [END_OBJECT] but found [FIELD_NAME]", "line": 16, "col": 13 } }, "status": 400 }
手动修改成以下JSON结构后可以正常运行:
{ "query":{ "bool":{ "must":[ { "multi_match": { "query": "277 Some Road", "fields":[ "addressLine1", "addressCity", "contactName" ] } }, { "bool":{ "should":[ { "range":{ "_metrics.count":{ "gte":1 } } } ] } } ], "filter":[ { "term":{ "organisationId.keyword":{ "value":"xxxx-xxxx-xxxx-xxxx" } } } ] } } }
能看到核心差异是must和filter需要是对象数组,但我调整PHP数组后始终无法让json_encode()生成正确结构,请问该怎么修改PHP数组?
问题根源
Elasticsearch的bool查询中,must、should、filter这些参数要求的是查询子句的数组,而你原本的PHP数组里,must和filter是关联数组(键值对结构),json_encode()会把它们转成JSON对象,而非数组。
修改后的PHP数组
你需要把must里的每个查询子句都用单独的数组包裹,让整个must变成一个索引数组(不带键名的数组);同理filter也需要改成索引数组:
'query' => [ 'bool' => [ 'must' => [ // 第一个查询子句:用数组包裹multi_match [ 'multi_match' => [ 'query' => $request->get('keyword'), 'fields' => ['addressLine1', 'addressCity', 'contactName'] ] ], // 第二个查询子句:用数组包裹bool查询 [ 'bool' => [ 'should' => [ // should里的range同样需要数组包裹 [ 'range' => [ '_metrics.count' => [ 'gte' => 1 ] ] ] ] ] ] ], 'filter' => [ // filter里的term用数组包裹 [ 'term' => [ 'organisationId.keyword' => [ 'value' => $organisationId ] ] ] ] ] ]
原理说明
- PHP中,不带键名的索引数组会被
json_encode()转成JSON数组([]包裹的结构); - 带键名的关联数组会被转成JSON对象(
{}包裹的结构); - Elasticsearch要求
must/filter是查询子句的数组,所以必须让PHP里对应的结构是索引数组,每个子句是独立的关联数组。
这样修改后,执行json_encode()就能生成Elasticsearch需要的正确JSON结构了。
内容的提问来源于stack exchange,提问作者ChrisBratherton
相关产品推荐
相关产品推荐

