Elastic实战:nested查询与数组同一元素匹配多个值
0. 引言
之前有同学实际生产中遇到了一个问题,题目本身不涉及生产环境上的问题,纯粹的DSL,但是因为是实际数据,因此数据量上会大很多,也增加了排错的难度。下面我们具体看下这个问题,让大家具体体会下实际生产的问题与训练题之间的区别在哪儿。
申明:该分享已得到该同学的授权,数据也已经过脱敏
1. 题目
问题:需要针对商品数据进行查询,要求查询skus.wareSkuAttrList.wareAttrValue,原数据如下,其组合有6kg,L、9,M、9,XL,但是查询时将9,L的组合也查询出来了,要求找到查询中的错误。
skus:[
{wareSkuAttrList:[{wareAttrValue:6KG},{wareAttrValue:L}]},
{wareSkuAttrList:[{wareAttrValue:9},{wareAttrValue:M}]},
{wareSkuAttrList:[{wareAttrValue:9},{wareAttrValue:XL}]}
]
索引mappings
PUT test_product
{
"mappings": {
"properties": {
"activityId": {
"type": "keyword"
},
"attrs": {
"type": "nested",
"properties": {
"attrId": {
"type": "long"
},
"attrName": {
"type": "keyword"
},
"attrValue": {
"type": "keyword"
}
}
},
"floorName": {
"type": "keyword"
},
"goodPercent": {
"type": "long"
},
"hosStock": {
"type": "boolean"
},
"marketPrice": {
"type": "float"
},
"saleCount": {
"type": "long"
},
"salePrice": {
"type": "float"
},
"skus": {
"type": "nested",
"properties": {
"attrId": {
"type": "long"
},
"attrValue": {
"type": "keyword"
},
"marketPrice": {
"type": "float"
},
"skuId": {
"type": "keyword"
},
"skuImage": {
"type": "keyword"
},
"skuName": {
"type": "text"
},
"skuPrice": {
"type": "float"
},
"wareSkuAttrList": {
"type": "nested",
"properties": {
"attrDefinitionId": {
"type": "float"
},
"attrDefinitionName": {
"type": "keyword"
},
"wareAttrValue": {
"type": "keyword"
}
}
}
}
}
}
}
}
索引数据
# 数据
PUT test_product/_doc/1
{
"activityId": "1469181609600225280",
"attrs": [
{
"attrId": 101,
"attrName": "重量类型",
"attrValue": "6kg"
},
{
"attrId": 107,
"attrName": "尺码",
"attrValue": "L"
},
{
"attrId": 101,
"attrName": "重量类型",
"attrValue": "9"
},
{
"attrId": 107,
"attrName": "尺码",
"attrValue": "M"
},
{
"attrId": 101,
"attrName": "重量类型",
"attrValue": "9"
},
{
"attrId": 107,
"attrName": "尺码",
"attrValue": "XL"
}
],
"floorName": "",
"goodPercent": 100,
"hosStock": true,
"marketPrice": 500,
"saleCount": 1,
"salePrice": 400,
"skus": [
{
"marketPrice": 500,
"skuId": "3100140",
"skuImage": "xxx.jpg",
"skuName": "测试主图(6kg,L)",
"skuPrice": 400,
"wareSkuAttrList": [
{
"attrDefinitionId": 101,
"attrDefinitionName": "重量类型",
"wareAttrValue": "6kg"
},
{
"attrDefinitionId": 107,
"attrDefinitionName": "尺码",
"wareAttrValue": "L"
}
]
},
{
"marketPrice": 600,
"skuId": "3100141",
"skuImage": "xxx.jpg",
"skuName": "测试主图(9,M)",
"skuPrice": 500,
"wareSkuAttrList": [
{
"attrDefinitionId": 101,
"attrDefinitionName": "重量类型",
"wareAttrValue": "9"
},
{
"attrDefinitionId": 107,
"attrDefinitionName": "尺码",
"wareAttrValue": "M"
}
]
},
{
"marketPrice": 800,
"skuId": "3100142",
"skuImage": "xxx.jpg",
"skuName": "测试主图(9,XL)",
"skuPrice": 700,
"wareSkuAttrList": [
{
"attrDefinitionId": 101,
"attrDefinitionName": "重量类型",
"wareAttrValue": "9"
},
{
"attrDefinitionId": 107,
"attrDefinitionName": "尺码",
"wareAttrValue": "XL"
}
]
}
]
}
原查询
真实的原查询中还包含了复杂的聚合语句,但这道题的错误与聚合无关,所以优先将其筛选掉
# 查询
GET test_product/_search
{
"from": 0,
"size": 40,
"query": {
"bool": {
"must": [
{
"term": {
"activityId": {
"value": "1469181609600225280",
"boost": 1
}
}
}
],
"filter": [
{
"nested": {
"query": {
"nested": {
"query": {
"bool": {
"must": [
{
"terms": {
"skus.wareSkuAttrList.wareAttrValue": [
"9"
],
"boost": 1
}
}
],
"adjust_pure_negative": true,
"boost": 1
}
},
"path": "skus.wareSkuAttrList"
}
},
"path": "skus"
}
},
{
"nested": {
"query": {
"nested": {
"query": {
"bool": {
"must": [
{
"terms": {
"skus.wareSkuAttrList.wareAttrValue": [
"L"
],
"boost": 1
}
}
],
"adjust_pure_negative": true,
"boost": 1
}
},
"path": "skus.wareSkuAttrList"
}
},
"path": "skus"
}
}
],
"adjust_pure_negative": true,
"boost": 1
}
}
}
2.思路
首先要理解清楚问题,题目中是针对数组中的同一个字段wareAttrValue进行查询,也就是说针对的是数组同一个元素匹配多个值的查询场景,而原查询将9,L的组合查询出来的原因,是因为数据中包含了9,因此匹配到了一个条件就输出了。
其实看到题目的时候我的第一反应是这是一个nested结构,是不是没有使用nested查询导致的,这类情况是比较常见的情况,但是观看查询语句发现是已经使用了nested查询的,并不是这种情况
对于初步接触这类排错的,有一个预处理可以帮助我们快速定位问题,那就是删除不必要的信息,首先我们看题目给我们的信息包含mappings,doc,查询语句,我们可以先把mappings,查询语句做简化,而doc只需要执行就好。
注意:真实的题目比我上述给出的mappings,doc,查询语句更加复杂,所以其实上述数据是已经经过一次删除的,真实处理时要学会快速定位有效信息和无效信息,并且优先剔除掉确认的无效信息
处理后mappings
PUT test_product
{
"mappings": {
"properties": {
"skus": {
"type": "nested",
"properties": {
"wareSkuAttrList": {
"type": "nested",
"properties": {
"wareAttrValue": {
"type": "keyword"
}
}
}
}
}
}
}
}
处理后查询
GET test_product/_search
{
"from": 0,
"size": 40,
"query": {
"bool": {
"filter": [
{
"nested": {
"query": {
"nested": {
"query": {
"bool": {
"must": [
{
"terms": {
"skus.wareSkuAttrList.wareAttrValue": [
"9"
],
"boost": 1
}
}
],
"adjust_pure_negative": true,
"boost": 1
}
},
"path": "skus.wareSkuAttrList"
}
},
"path": "skus"
}
},
{
"nested": {
"query": {
"nested": {
"query": {
"bool": {
"must": [
{
"terms": {
"skus.wareSkuAttrList.wareAttrValue": [
"L"
],
"boost": 1
}
}
],
"adjust_pure_negative": true,
"boost": 1
}
},
"path": "skus.wareSkuAttrList"
}
},
"path": "skus"
}
}
],
"adjust_pure_negative": true,
"boost": 1
}
}
}
查看到这里有兴趣的同学可以先尝试解决这个问题,然后再往下看
现在再查看原查询就比较清晰了,首先我们先查看nested查询的语法,发现并无问题,那么再单独写一个针对数组元素匹配多个值的查询
GET test/_search
{
"query": {
"bool": {
"must": [
{
"term": {
"tags": "xxx"
}
},
{
"term": {
"tags": "yyy"
}
}
]
}
}
}
查看上述的查询发现虽然也写了must,但是是两个分开的must,就导致了must的作用并没有起到,相当于写了两个filter,满足其一即可。所以这也就是为什么原查询能将9,L组合查询出来的原因,因为9匹配到了。
所以我们接下来的思路就清晰了,就是要把原索引中的两个must组合为一个must,剩下的一个难点就是针对nested查询语法的熟悉程度,如果不熟悉语法的可能会出错,这里建议多查看官方文档,对比书写
3 答案
调整后的查询语句如下
GET test_product/_search
{
"from": 0,
"size": 40,
"query": {
"bool": {
"filter": [
{
"nested": {
"path": "skus",
"query": {
"bool": {
"must": [
{
"nested": {
"path": "skus.wareSkuAttrList",
"query": {
"term": {
"skus.wareSkuAttrList.wareAttrValue": {
"value": "9"
}
}
}
}
},
{
"nested": {
"path": "skus.wareSkuAttrList",
"query": {
"term": {
"skus.wareSkuAttrList.wareAttrValue": {
"value": "XL"
}
}
}
}
}
]
}
}
}
}
]
}
}
}
4 测试
查询条件改为9,XL时,查询结果,匹配到结果
查询条件改为9,L时,无匹配结果,符合要求,测试通过
- 点赞
- 收藏
- 关注作者
评论(0)