springboot~elasticsearch对nested集合类型的字段进行不等于的检索
对于es的数据类型来说,如果它是一个复杂类型,而我们需要把复杂类型进行检索,那么应该定义成nested
类型,而对于它的检索,如果是非集合数据,它与其它类型没有分别;而如果你的nested存储的数据是一个集合,那在进行不等于这种操作时,就需要说明一下了。
举例子
"id": "1",
"operate": [
{
"actionTime": "2017-03-21 11:57:37.700",
"operateType": "WangLu",
"wordNum": "3188",
"userName": "zidongluru"
},
{
"actionTime": "2017-03-21 11:57:37.700",
"operateType": "DaoRu",
"wordNum": "3188",
"userName": "qinbixue1609"
}
]
对于上面的数据,其中operate是一个nested类型的,它存储数据为一个集合,我们查询operateType不等于WangLu的数据,如果直接使用not_must配合term,那结果是不对的,它会把这个文档正常返回,为什么?按说这个operate里是包含了WangLu
的,如果包含它是不应该返回的;这就是es检索nested集合处理不等于的问题,当会查询所有集合中的元素,有一个不等于WangLu
的,它就认为是不等于的,这与我们的需求不符合,所以要想其它方法。
script脚本检索nested集合失败
{
"size": 5,
"_source": [
"Gid",
"Type",
"operate"
],
"query": {
"bool": {
"must": [
{
"nested": {
"path": "operate",
"query": {
"script": {
"script": "doc['operate.operateType'].stream().map(o->o).filter(o->o=='YiJiaoSu').count()==0"
}
}
}
},
{
"term": {
"Gid": "1970325267114815"
}
}
]
}
}
}
使用function方式解决了这个问题
- 参考
https://cloud.tencent.com/developer/article/1973186
https://stackoverflow.com/questions/54022283/elasticsearch-search-query-why-params-source-nested-field-size-is-not-workin
{
"size": 5,
"_source": [
"Gid",
"Type",
"operate"
],
"query": {
"bool": {
"must": [
{
"function_score": {
"query": {
"match_all": {}
},
"functions": [
{
"script_score": {
"script": {
"source": "params._source.operate.stream().map(o->o.operateType).filter(o->o=='YiJiaoSu').count()==0 ? 1 : 0"
}
}
}
],
"min_score": 1
}
},
{
"term": {
"Id": "1970325267114815"
}
}
]
}
}
}
这种由于是集合里的集合再检索,所以需要和其它检索条件一起用,全表检索,性能比较差。
翻译成java客户端代码
String val = "条件";
String name = "nested内部字段名";
String scoreStr = "params._source.operate==null || params._source.operate.stream().map(o->o." + name + ").filter(o->o=='" + val + "').count()==0? 1 : 0";
Script sc = new Script(ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG, scoreStr, MapUtil.empty());
ScriptScoreFunctionBuilder builder = new ScriptScoreFunctionBuilder(sc);
FunctionScoreQueryBuilder scoreBuilder = QueryBuilders.functionScoreQuery(builder);
scoreBuilder.setMinScore(1);
scoreBuilder.scoreMode(FunctionScoreQuery.ScoreMode.FIRST);
queryIncludeBuilder.must(scoreBuilder);