Elasticsearch数组索引聚合实现及替代方案咨询
Elasticsearch 数组聚合需求:文档结构分析与优化方案
嘿,我来帮你拆解这个问题,一步步看你的需求能不能实现,以及怎么优化文档结构~
当前文档结构的可行性分析
你的原始文档结构是这样的:
{ "id": "<<some id>>", "list": [ "a", "b", "c" ] } { "id": "<<some id>>", "list": [ "c", "a" ] }
针对你的两个聚合需求:
- 统计包含a的文档总数:完全支持!Elasticsearch会自动将数组字段做扁平化的倒排索引,你可以用
filter聚合过滤list:a,就能得到包含a的文档数量,示例聚合语句大概是这样:{ "size": 0, "aggs": { "docs_with_a": { "filter": { "term": { "list": "a" } } } } } - 统计a位于首位的文档总数:当前结构不支持!因为Elasticsearch在存储数组时会丢失元素的顺序信息,它只会记录文档包含哪些元素,不会记录元素的位置。所以没办法直接判断a是不是数组的第一个元素。
更合适的文档结构方案
针对你的需求,下面分析你提出的两个备选结构,还有一个更轻量化的方案:
方案1:按位置存储元素(你的第一个备选结构)
结构示例:
{ "id": "<<some id>>", "list": { "values": ["c", "a"], "ordered": { "0": "c", "1": "a" } } }
- 优势:
- 保留了元素的顺序信息,要统计a在首位,只需过滤
list.ordered.0:a即可,聚合语句类似:{ "size": 0, "aggs": { "a_first_docs": { "filter": { "term": { "list.ordered.0": "a" } } } } } values数组依然可以用来统计包含a的文档总数,和原始结构的用法一致。
- 保留了元素的顺序信息,要统计a在首位,只需过滤
- 注意点:需要确保
list.ordered在mapping中被设置为object类型,这样每个位置的字段能被单独索引。
方案2:元素映射位置(你的第二个备选结构)
结构示例:
{ "id": "<<some id>>", "list": { "values": ["c", "a"], "ordered": { "a": 1, "c": 0 } } }
- 优势:
- 完全满足你的两个基础需求:统计包含a用
list.values:a,统计a在首位用list.ordered.a:0。 - 额外支持你提到的元素位置比较类聚合(比如
a的位置比c靠前,可以过滤list.ordered.a < list.ordered.c),灵活性拉满。
- 完全满足你的两个基础需求:统计包含a用
- 注意点:这个结构只适用于
list中的元素无重复的场景,如果有重复元素,object的key会被覆盖,导致位置信息丢失。
轻量化方案:单独存储首元素
如果你的需求只局限于这两个聚合(统计包含a、统计a在首位),可以用更简单的结构:
{ "id": "<<some id>>", "list": ["a", "b", "c"], "first_list_element": "a" }
- 优势:实现成本最低,不需要嵌套结构,统计a在首位直接过滤
first_list_element:a,统计包含a还是用list:a,查询和聚合的语句都更简洁。 - 局限:如果之后需要扩展更多关于元素位置的复杂查询,这个结构就不够用了。
总结
- 原始结构只能满足第一个需求,无法处理位置相关的聚合;
- 如果需要支持位置比较类的复杂操作,推荐你用方案2(元素映射位置);
- 如果只需要当前的两个需求,轻量化方案是最省心的选择;
- 方案1是中间过渡选项,适合需要保留完整位置信息但不需要元素位置比较的场景。
内容的提问来源于stack exchange,提问作者Filippo De Luca
相关产品推荐
相关产品推荐

