ElasticSearch-bool的嵌套查询

1,791 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

近期在工作中需要使用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" : { }
    }
  }
}