本文已参与「新人创作礼」活动,一起开启掘金创作之路。
近期在工作中需要使用es进行简单的全文检索,所以最近都在边学边做笔记,也会把自己遇到的bug以及如何解决的,一并记录在这里,供大家学习参考。
需求描述
在实战场景中,可能会出现bool的嵌套查询的使用。
例如,查询[薯片 瓜子]在goodDoc.goodName中,查询结果要求必须包含这两个词。这个时候就需要使用嵌套查询的形式,单个词的查询用should,外面再用must嵌套,即可满足搜索需求。查询语句如下:
GET /test_xxx/_search
{
"query": {
"bool": {
"must":[
{
"bool" : {
"should": [
{
"nested" : {
"query" : {
"match": {
"xxx1Doc.xxxValue" : {
"query" : "xxx"
}
}
},
"path" : "xxx1Doc",
"inner_hits" : {
"name": "u1",
"highlight" : {
"fields" : {
"xxx1Doc.xxxValue" : { }
}
}
}
}
},
{
"nested" : {
"query" : {
"match" : {
"xxx2Doc.xxxValue" : {
"query" : "xxx"
}
}
},
"path" : "xxx2Doc",
"inner_hits" : {
"name": "h1",
"highlight" : {
"fields" : {
"xxx2Doc.xxxValue" : { }
}
}
}
}
},
{
"nested" : {
"query" : {
"match" : {
"xxx3Doc.xxxValue" : {
"query" : "xxx"
}
}
},
"path" : "xxx3Doc",
"inner_hits" : {
"name": "a1",
"highlight" : {
"fields" : {
"xxx3Doc.xxxValue" : { }
}
}
}
}
}
]
}
},{
"bool" : {
"should": [
{
"nested" : {
"query" : {
"match": {
"xxx1Doc.xxxValue" : {
"query" : "xxx"
}
}
},
"path" : "xxx1Doc",
"inner_hits" : {
"name": "u2",
"highlight" : {
"fields" : {
"xxx1Doc.xxxValue" : { }
}
}
}
}
},
{
"nested" : {
"query" : {
"match" : {
"xxx2Doc.xxxValue" : {
"query" : "xxx"
}
}
},
"path" : "xxx2Doc",
"inner_hits" : {
"name": "h2",
"highlight" : {
"fields" : {
"xxx2Doc.xxxValue" : { }
}
}
}
}
},
{
"nested" : {
"query" : {
"match" : {
"xxx3Doc.xxxValue" : {
"query" : "xxx"
}
}
},
"path" : "xxx3Doc",
"inner_hits" : {
"name": "a2",
"highlight" : {
"fields" : {
"xxx3Doc.xxxValue" : { }
}
}
}
}
}
]
}
}],
"adjust_pure_negative" : true,
"boost" : 1.0
}
}
}
bool->must->bool->should
代码实现
构思好查询语句之后,就要在springboot代码中去实现这个逻辑。
关于bool下的must和should集合的形式,可以通过如下方式去实现:
List<QueryBuilder> must = boolQueryBuilder.must();
List<QueryBuilder> innerShould = innerBool.should();
详细代码如下:
//首先对[薯片 瓜子]通过空格分隔符转换成String数组
String[] vals = req.getSearchContent().split(" ");
List<QueryBuilder> must = boolQueryBuilder.must();
int n = 0;
for (String val : vals) {
n++;
BoolQueryBuilder innerBool = new BoolQueryBuilder();
List<QueryBuilder> innerShould = innerBool.should();
for (DocTypeEnum docs : DocTypeEnum.values()) {
MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(docs.getKey() + ".xxxValue", val);
NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery(docs.getKey(), matchQueryBuilder, ScoreMode.None).
innerHit(new InnerHitBuilder().setName(docs.getKey() + "" + n).setHighlightBuilder(
new HighlightBuilder().field(docs.getKey() + ".fieldValue")).setSize(MIX_SIZE));
innerShould.add(nestedQueryBuilder);
}
must.add(innerBool);
}
这段代码需要注意,在整个查询语句中,innerhits会多次使用相同的字段,所以在这里需要取别名,保证唯一性。
"inner_hits" : {
"name": "a2", //取别名,保证唯一性
"highlight" : {
"fields" : {
"xxx3Doc.xxxValue" : { }
}
}
}