最近在使用es的过程中,要查询nested嵌套查询不存在的字段,查询结果不尽人意,做一下分析
1.索引数据
{
"catSource":"JC",
"planMonth":null,
"sendBidTime":null,
"planStatus":null,
"planName":null,
"secondCatName":"children3-1",
"source":null,
"projectSysNo":null,
"relation":{
"parent":"YJH20231018004040",
"name":"planMonthlyItem"
},
"tenderType":"PUBLIC",
"updaterId":10020051,
"areaName":null,
"planItemCode":"yjh-1714582956141731861",
"bidDocProjectReviewStartTime":null,
"planTenderStartTime":1697558400000,
"sourceType":"NEW",
"coreAttrName":null,
"plannedCnt":null,
"updaterName":"这是一个很长很长很长很长很长的名字",
"changeStatus":null,
"projectName":null,
"estimatedAmount":1,
"tenderRelList":[
],
"tenderDays":null
}
2.查询语句
GET idx_ppls_plan_monthly_info_qa/_search
{
"query": {
"bool": {
"filter": [
{
"terms": {
"planItemCode": ["yjh-1714582956141731861"]
}
},
{
"nested": {
"path": "tenderRelList",
"query": {
"bool": {
"must_not": {
"exists": {
"field": "tenderRelList.id"
}
}
}
}
}
}
]
}
}
}
查询结果如下,未查到
3.将查询语句改为如下方式
GET idx_ppls_plan_monthly_info_qa/_search
{
"query": {
"bool": {
"filter": [
{
"terms": {
"planItemCode": ["yjh-1714582956141731861"]
}
},
{
"bool": {
"must_not": [
{
"nested": {
"path": "tenderRelList",
"query": {
"exists": {
"field": "tenderRelList.id"
}
}
}
}
]
}
}
]
}
}
}
查询结果如下,正常返回数据
4.查询分析
单从查询语句分析,这2种查询方式看起来是一样的,都是要查询code为XXX的数据,并且tenderRelList为空的数据,但是返回结果截然相反,很奇怪。
网上查找了一下,没有找到原因,让chatgpt分析了一下,也是没分析出来,chatgpt认为2种查询方式返回的结果应该一样
然后,自己研究了一下,分析如下:
1.先看es的内部执行计划
从执行计划可以看出,第一种查询方式,是在查询了code的同时,还去查询了子数据,然后取交集,因为第一种写法,term查询之后,并没有and的关系,是直接去nested执行子查询,然后将2个查询结果取了交集,所以返回结果为空。
第2种查询方式,是在查询了code返回的数据后,在该数据的基础上过滤nested不存在的数据,所以这种查询是返回了结果的。
第一种查询的子查询,是直接查询了nested的子索引文档,因为他是先在子文档查询id不存在的数据,因为不存在这样的数据,所以没法反推出主文档的数据,因此返回结果为空,跟主查询取交集,就没数据了.
验证如下:
查询子文档存在,其中某个字段为空的数据,发现可以查到数据
查询子文档不存在,结果为空,跟一开始的查询结果是一致的
2.再看下直接查子文档不存在的执行计划
显然,第二种查询是查主文档存在,并且子文档进行过滤