Java操作Elasticsearch:嵌套字段多条件And查询异常问题
问题:Elasticsearch嵌套数组条件查询不符合预期
数据集结构
{ "took": 3, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 3, "relation": "eq" }, "max_score": 1.0, "hits": [ { "_index": "hotels", "_type": "_doc", "_id": "7148", "_score": 1.0, "_source": { "places": [ { "placeId": 3, "type": "MAIN", "name": "pruggern" } ] } }, { "_index": "hotels", "_type": "_doc", "_id": "7147", "_score": 1.0, "_source": { "places": [ { "placeId": 3, "type": "MAIN", "name": "pruggern" } ] } }, { "_index": "hotels", "_type": "_doc", "_id": "7146", "_score": 1.0, "_source": { "places": [ { "placeId": 3, "type": "AROUND", "name": "pruggern" }, { "placeId": 1, "type": "MAIN", "name": "schladming" } ] } } ] } }
查询需求
根据给定的placeId集合和type值筛选酒店:
- 当查询
placeId=3且type=MAIN时,返回ID为7148、7147的酒店 - 当查询
placeId=[1,3]且type=MAIN时,返回全部3个酒店
尝试的查询及问题
尝试1:构建Bool查询
Java代码:
BoolQueryBuilder placeQueryWithType = boolQuery(); placeQueryWithType.minimumShouldMatch(1); placeQueryWithType.should(termsQuery(PLACES + ".placeId", placeIds)); //array of places placeQueryWithType.must(termQuery(PLACES + ".type", placeType.name())); //Either MAIN or AROUND mainQuery.must(placeQueryWithType);
生成的DSL:
{ "query": { "bool" : { "must" : [ { "bool" : { "must" : [ { "term" : { "places.type" : { "value" : "MEETING_POINT", "boost" : 1.0 } } } ], "should" : [ { "terms" : { "places.placeId" : [ 3 ], "boost" : 1.0 } } ], "adjust_pure_negative" : true, "minimum_should_match" : "1", "boost" : 1.0 } } ], "adjust_pure_negative" : true, "boost" : 1.0 } } }
问题:返回所有酒店,不符合预期。原因是默认object类型会扁平化存储数组,导致placeId和type的关联丢失,只要文档中存在任意元素匹配type、任意元素匹配placeId就会被命中。
尝试2:嵌套Bool查询
DSL:
{ "query":{ "bool" : { "must" : [ { "bool" : { "should" : [ { "bool" : { "must" : [ { "terms" : { "places.placeId" : [ 3 ], "boost" : 1.0 } }, { "term" : { "places.type" : { "value" : "MEETING_POINT", "boost" : 1.0 } } } ], "adjust_pure_negative" : true, "boost" : 1.0 } } ], "adjust_pure_negative" : true, "minimum_should_match" : "1", "boost" : 1.0 } } ], "adjust_pure_negative" : true, "boost" : 1.0 } } }
问题:依然无法得到预期结果,核心原因是没有处理嵌套数组的关联匹配。
正确解决方案
核心原理
Elasticsearch中,数组类型字段若为默认object类型,会扁平化存储数组内对象的字段,导致字段关联关系丢失。要确保数组中同一个对象同时满足placeId和type条件,必须使用nested查询,且需先将places字段设置为nested类型。
步骤1:修改Mapping(若未设置nested类型)
更新索引mapping,将places设为nested类型:
PUT /hotels/_mapping { "properties": { "places": { "type": "nested" } } }
修改后需重新索引所有数据,确保新mapping生效。
步骤2:正确的查询DSL
针对placeId=3且type=MAIN的查询:
{ "query": { "bool": { "must": [ { "nested": { "path": "places", "query": { "bool": { "must": [ { "terms": { "places.placeId": [3] } }, { "term": { "places.type": "MAIN" } } ] } } } } ] } } }
针对placeId=[1,3]且type=MAIN的查询,仅需修改terms数组值:
{ "query": { "bool": { "must": [ { "nested": { "path": "places", "query": { "bool": { "must": [ { "terms": { "places.placeId": [1,3] } }, { "term": { "places.type": "MAIN" } } ] } } } } ] } } }
步骤3:对应的Java代码实现
// 构建嵌套内部的联合条件:同时匹配placeId和type BoolQueryBuilder nestedCondition = boolQuery() .must(termsQuery("places.placeId", placeIds)) .must(termQuery("places.type", placeType.name())); // 创建nested查询,指定路径为places NestedQueryBuilder nestedQuery = nestedQuery( "places", nestedCondition, ScoreMode.None // 根据需求选择评分模式,None表示不影响主文档评分 ); // 将nested查询加入主查询的must条件中 mainQuery.must(nestedQuery);
内容的提问来源于stack exchange,提问作者Sandeep Nair
相关产品推荐
相关产品推荐

