现有以下数据:
[
{
"title":"Hello Wolrd",
"desc":"你好世界",
"customFields":[
{
"key":"tags",
"value":"灌水"
},
{
"key":"test",
"value":"问候"
},
]
},
{
"title":"Hello Jack",
"desc":"你好 jack",
"customFields":[
{
"key":"tags",
"value":"问候"
}
]
}
]
customFields 属于自定义字段,因为涉及业务需要对自定义字段做筛选,我使用以下查询语法:
{
"query":{
"bool":{
"must":{
"trem":[
{
"customFields.key":"tags"
},
{
"customFields.value":"问候"
}
]
}
}
}
}
预期结果是只返回有问候的 tags ,但实际两条都返回了,这种情况怎么做精准的筛选呢
根据官方文档给出:
How arrays of objects are flattened
Arrays of inner object fields do not work the way you may expect. Lucene has no concept of inner objects, so Elasticsearch flattens object hierarchies into a simple list of field names and values. For instance, the following document:
意思是如果单纯的object类型是会被拉伸为扁平化,故在筛选出无法精准打击到想要的数据,所以官方推出了Nested datatype,嵌套文档就是用来解决问题,但他是有限制的,比如当字段最好不要超过50个,字段过多会导致内存泄露的风险,目前我已知的解决方案有:
优点: 查询一般方便(需要使用特定的关键词),关系明确
缺点: 1.mapping映射爆炸,具体参考官方文档mapping篇章
2 需要维护多个文档,实际Nested 会产生多个隐藏文档,但实际用户看到的只有一个。
优点: 查询方便,不需要维护额外的文档
缺点: 1. 建立一个新的copy_to字段都需要手动修改mappings关系,无法使用动态映射(具体的话不太清楚,目前查到的资料很缺失)
优点: 查询方便, 不需要维护额外的文档
缺点: mapping映射爆炸,具体参考官方文档mapping篇章
上面的方案我都没用,我还是用了数组对象的+单独字段方式
只是把其中需要搜寻的key-value单独作为新的字段存到es里, 也就是存两份,一份存到customFields对象中,一份存到整个文档中 tag : "value"
查的时候只把customFields给到前端,新的字段当做查询字段使用不显式,这么做的原因主要还是项目的数据结构已经定型,数据结构无法做到很大的变动,比如直接砍掉customFields对象数组,又不想用Nested ,又因为需要使用动态映射,最终选了这个
1
xjngbla 2021-11-10 10:56:39 +08:00
trem 单词写错了,应该是 term
|
2
lithium4010 2021-11-10 10:58:38 +08:00
你这里 term 里面是或的关系
|
3
sun2920989 2021-11-10 11:02:14 +08:00
不出意外的话,你应该是需要 nested.
|
4
Chad0000 2021-11-10 11:05:06 +08:00 1
如果这个 ES 是转存的,不是原始数据,可能你在转换时存为 object 好点? "customFields": { "tags":"灌水", "test":"问候" }
|
5
Saxton OP @sun2920989 nested 嵌套关系吗
|
7
X0ray 2021-11-10 11:16:47 +08:00
{
"query": { "bool": { "must": [ { "term": { "customFields.key": "tags" } }, { "term": { "customFields.value": "问候" } } ] } } } |
9
sun2920989 2021-11-10 11:20:21 +08:00 1
@Saxton #5 简单来说,es 的数据结构存储你这样的数据,如果不使用 nested,实际上会被重新打平拆分的,你的数据的第一条实际上会变为 customFields.key:[tags,test], customFields.value:[灌水,问候] ,所以就会被你在筛选 tags + 问候 时也被筛选到.以上信息源自于我的记忆不保证准确,你自行搜索下.
|
10
Saxton OP @sun2920989 bingo 没错 我刚才阅读了文档 确实是这样 我已经按照楼上那个思路换成 object 方式了 不以数组方式储存了 将字段名作为 obj 的 key 即可 感谢 !
|
11
sun2920989 2021-11-10 11:24:16 +08:00
@Saxton #10 嗯,那你需要额外处理下第一条数据那种一对多的关系.
|
12
liangzhe 2021-11-10 14:03:45 +08:00
@sun2920989 大佬,请教下如果用户自定义的 tag 极其多,超过了 es 单条文档的字段数量限制应该怎么做?目前遇到了这个问题
|
13
sun2920989 2021-11-10 14:15:39 +08:00 1
@liangzhe #12 不是大佬,客气了.你所指的 tag 极多是在什么层级呢,以楼主主贴的数据结构看,即使 tag 再多,对于 es 来讲也是 customFields.key 这样一个字段,只是字段里的内容比较长而已.es 对整体的字段数量,以及对单一一个字段里面内容的长度,比如 keyword 类型,也是有限制的,但是这些也都可以按照实际情况进行一定的配置来调大一些.主要看你的数据结构了.
|
14
wbd31 2021-11-10 17:15:55 +08:00 1
方案 3 的 object 可以设置为 flattened 类型来避免映射爆炸
https://www.elastic.co/guide/en/elasticsearch/reference/current/flattened.html |
15
sun2920989 2021-11-10 17:39:57 +08:00
不管是 object 还是 nested,或者只是基础的 text, keyword 等等,不管什么类型的数据结构,只要字段总量是确定的,也不用过于担心映射爆炸的问题,映射爆炸主要是对于动态增加的字段来说的,与是否是对象和是否是嵌套无关.比如字段名字叫做 tag1,tag2 这样随着业务不断新增的时候,才会需要考虑这个问题.像我上一楼回复中说的,如果是主楼那样的数据结构,不管有多少个 tag,其实也只有 customFields.key 一个字段而已,字段是数组的话,字段内的数值变多,并不会引起映射爆炸.同样,以上说法也是我记得的情况,可以再找找资料了解一下.
|
16
sun2920989 2021-11-10 17:45:26 +08:00
按照追加的说法,将下一层级中需要搜索的字段提出来到上一层级中再存一分,我感觉并不能解决此类问题,比如,主楼第一条数据最终还是会存储为 tag:['tags','test'],因为是一对多的结构.所以一样会被主楼这样的条件所查询到.当然可能这个地方我理解的和楼主实现的方式不一样.
|
17
sun2920989 2021-11-10 17:49:12 +08:00
如果说楼主使用的方式是 tags:'灌水' ,test:问候,这样来存储的话,才是真的将一个 tag 变成了一个字段,这样随着 tag 变得越来越多,才需要考虑映射爆炸的问题.因为此时的总映射字段数量是不确定的,是持续动态变多的.
|
18
Saxton OP @sun2920989 是的,就是这个方式, 其实 tags 这个 key 本身变化由用户决定,所以不会出现被拉成平行的问题,但也伴随着时间的推移自定义的字段会越来越多,目前暂时没想到其他好的解决方案,只能从业务层去限制用户的字段数量
|
19
sun2920989 2021-11-11 10:41:45 +08:00
@Saxton #18 好的,了解了.也建议再从业务层上考虑下是否会存在某个 tag 的 key 对应了多个不同的 value 的场景.防止后续出现意外.比如[{key:tags1,value:测试 1},{key:tags1,value:测试 2}],这样的结构.
|