上一篇【第32篇】Elasticsearch时间序列聚合——date_histogram与auto_date_histogram下一篇【第34篇】Elasticsearch管道聚合与聚合实战——bucket_script与移动平均摘要在真实业务场景中聚合分析往往需要在特定条件下进行——例如仅统计已支付订单的平均金额或分别统计错误日志、警告日志和正常日志的占比。Elasticsearch提供了强大的过滤器聚合体系来解决这些问题。本文深入讲解filter单过滤器聚合在查询上下文下进一步缩小聚合范围、filters多过滤器聚合多个命名过滤器并行执行、adjacency_matrix邻接矩阵聚合分析标签之间的共现关系。同时针对Elasticsearch的特殊文档类型本文全面覆盖nested嵌套对象聚合解决嵌套文档独立查询的问题、reverse_nested反嵌套聚合从嵌套上下文返回父文档、parent父子文档聚合以及sampler采样聚合高基数场景下的性能优化方案。关键词filter聚合、filters聚合、邻接矩阵、嵌套聚合、采样聚合。一、过滤器聚合体系概述1.1 为什么需要过滤器聚合常规的聚合操作是对查询返回的所有文档执行计算。但在实际分析中常有如下需求“统计上海市用户的订单总值”需要对特定城市的用户执行聚合“分别统计各产品线的销售额和退货额”需要并行执行多个不同条件的聚合“分析哪些商品标签经常同时出现”需要分析标签交叉关系过滤器聚合通过在聚合层面添加过滤条件实现了对聚合范围的精细化控制而无需修改顶层查询。1.2 三种过滤器聚合对比聚合类型用途过滤器数量输出结构filter单一条件下的聚合1个单桶filters多条件并行聚合多个多桶并列adjacency_matrix条件交叉共现分析多个矩阵二、filter单过滤器聚合2.1 基本概念filter聚合创建一个包含与指定过滤器匹配的所有文档的单个桶。通常用于将聚合上下文缩小到特定的文档子集。POST/orders/_search{size:0,aggs:{paid_orders:{filter:{term:{status:paid}},aggs:{avg_amount:{avg:{field:amount}}}}}}此请求首先用filter聚合筛选statuspaid的文档然后在该子集上计算平均金额。2.2 结合range范围过滤的实战POST/products/_search{size:0,aggs:{high_value_products:{filter:{bool:{must:[{range:{price:{gte:1000}}},{term:{in_stock:true}}]}},aggs:{by_category:{terms:{field:category.keyword,size:10},aggs:{avg_rating:{avg:{field:rating}}}}}}}}2.3 filter聚合与query过滤的区别一个常见的困惑是既然顶层query已经可以过滤文档为什么还需要filter聚合区别在于query过滤影响所有聚合的计算范围filter聚合只在特定聚合子树中生效不影响其他聚合POST/orders/_search{size:0,query:{range:{order_date:{gte:2026-01-01}}},aggs:{all_orders_stats:{extended_stats:{field:amount}},large_orders:{filter:{range:{amount:{gte:1000}}},aggs:{large_stats:{extended_stats:{field:amount}}}}}}上面示例中all_orders_stats统计的是2026年所有订单而large_orders只在2026年且金额1000的订单上计算。三、filters多过滤器聚合3.1 基本概念与命名过滤器filters聚合可以并行运行多个过滤器每个过滤器产生一个独立的桶POST/logs/_search{size:0,aggs:{log_levels:{filters:{filters:{errors:{match:{message:error}},warnings:{match:{message:warning}},info:{match:{message:info}}}}}}}响应示例{aggregations:{log_levels:{buckets:{errors:{doc_count:45},warnings:{doc_count:120},info:{doc_count:850}}}}}3.2 other_bucket_key——捕获不匹配文档other_bucket_key参数为不与任何过滤器匹配的文档创建额外桶POST/logs/_search{size:0,aggs:{log_levels_with_other:{filters:{other_bucket_key:other_messages,filters:{errors:{match:{message:error}},warnings:{match:{message:warning}}}}}}}这会额外生成一个名为other_messages的桶收集所有既不匹配error也不匹配warning的日志。3.3 匿名过滤器数组也可以使用过滤器数组无需命名POST/logs/_search{size:0,aggs:{anonymous_filters:{filters:{filters:[{match:{message:error}},{match:{message:warning}},{match:{message:info}}]}}}}响应中桶按数组顺序返回为匿名桶key为数字索引。3.4 命名过滤器 vs 匿名过滤器特性命名过滤器匿名过滤器写法{errors: {...}}[{...}]桶标识自定义名称数字索引(0,1,2…)可读性高低适用场景需要明确识别每个桶含义动态生成的过滤器列表other_bucket_key支持支持3.5 实战多维度业务指标并行统计POST/ecommerce/_search{size:0,aggs:{business_metrics:{filters:{other_bucket_key:other,filters:{new_customers:{term:{is_new_customer:true}},high_value:{range:{order_amount:{gte:500}}},mobile_orders:{term:{device_type:mobile}},returned:{term:{status:returned}}}},aggs:{revenue:{sum:{field:order_amount}},avg_items:{avg:{field:item_count}}}}}}四、adjacency_matrix邻接矩阵聚合4.1 核心概念adjacency_matrix聚合提供了一组过滤器的交叉分析返回一个邻接矩阵。对于N个过滤器响应包含所有单个过滤器桶及其两两交叉桶。POST/emails/_search{size:0,aggs:{interactions:{adjacency_matrix:{filters:{grpA:{terms:{accounts:[hillary,sidney]}},grpB:{terms:{accounts:[donald,mitt]}},grpC:{terms:{accounts:[vladimir,nigel]}}}}}}}响应将包含以下桶grpA仅A相关的文档grpB仅B相关的文档grpC仅C相关的文档grpAgrpB同时匹配A和B的文档grpAgrpC同时匹配A和C的文档grpBgrpC同时匹配B和C的文档4.2 对称矩阵——为何不返回全部交叉邻接矩阵是对称的因此grpAgrpC与grpCgrpA是同一组文档。Elasticsearch会对过滤器名称排序始终用字典序较小的名称作为分隔符左侧。4.3 自定义分隔符默认交叉桶用连接过滤器名称可通过separator参数自定义POST/tags/_search{size:0,aggs:{tag_cooccurrence:{adjacency_matrix:{filters:{tech:{term:{tags:tech}},ai:{term:{tags:ai}},cloud:{term:{tags:cloud}}},separator: }}}}4.4 过滤器数量限制对于N个过滤器矩阵最多产生N²/2个桶。默认最大过滤器数为100可通过index.max_adjacency_matrix_filters索引设置更改。4.5 实战标签共现分析POST/articles/_search{size:0,aggs:{tag_analysis:{adjacency_matrix:{filters:{elasticsearch:{term:{tags:Elasticsearch}},kibana:{term:{tags:Kibana}},logstash:{term:{tags:Logstash}},beats:{term:{tags:Beats}}}},aggs:{avg_views:{avg:{field:view_count}}}}}}五、nested嵌套对象聚合5.1 nested类型的特殊挑战在Elasticsearch中当字段被定义为nested类型时每个嵌套对象被作为独立的隐藏文档存储。普通的聚合无法正确地在嵌套对象上工作——需要专门的nested聚合。索引映射示例PUT/store{mappings:{properties:{products:{type:nested,properties:{name:{type:keyword},price:{type:double},quantity:{type:integer}}}}}}5.2 nested聚合用法GET/store/_search{size:0,aggs:{product_analysis:{nested:{path:products},aggs:{product_names:{terms:{field:products.name.keyword}},avg_price:{avg:{field:products.price}}}}}}5.3 reverse_nested反嵌套聚合reverse_nested聚合用于从嵌套文档上下文返回到父文档层级对父文档执行聚合GET/store/_search{size:0,aggs:{product_analysis:{nested:{path:products},aggs:{by_name:{terms:{field:products.name.keyword},aggs:{product_stats:{reverse_nested:{},aggs:{store_info:{terms:{field:store_location.keyword}}}}}}}}}}这个查询首先在嵌套的products中按名称分组然后通过reverse_nested返回到父文档层级统计每个商品名称对应的店铺位置分布。六、parent父子文档聚合6.1 join类型回顾在父子文档关系中父文档和子文档是独立的文档通过join字段关联PUT/company{mappings:{properties:{relation:{type:join,relations:{department:employee}}}}}6.2 children聚合对子文档聚合POST/company/_search{size:0,aggs:{departments:{terms:{field:name.keyword},aggs:{employees:{children:{type:employee},aggs:{avg_salary:{avg:{field:salary}},max_salary:{max:{field:salary}}}}}}}}6.3 parent聚合对父文档聚合从子文档反向聚合父文档POST/company/_search{size:0,query:{term:{title.keyword:Senior Engineer}},aggs:{senior_engineers:{terms:{field:name.keyword},aggs:{their_departments:{parent:{type:department},aggs:{dept_name:{terms:{field:name.keyword}}}}}}}}七、sampler采样聚合7.1 适用场景在高基数cardinality场景下terms聚合需要精确计算所有唯一值对内存和CPU的开销极大。例如统计一个拥有数亿记录索引中的最热门URL。sampler聚合通过对文档进行抽样大幅降低计算量同时在统计意义上保持结果的相关性POST/click_logs/_search{size:0,query:{match:{query:error}},aggs:{sample:{sampler:{shard_size:200},aggs:{top_keywords:{significant_terms:{field:keywords,size:20}}}}}}7.2 shard_size参数shard_size控制每个分片采样的文档数量。采样发生在分片级别采样后的文档将用于子聚合的计算。权衡shard_size越小 → 查询越快但准确性越低shard_size越大 → 结果更准确但性能接近全量聚合7.3 sampler vs 全量聚合对比维度sampler采样全量聚合速度快仅处理采样文档慢处理所有文档准确性近似统计有效精确内存低高适用场景高基数、探索性分析精确报表、审计代表性取决于抽样比例100%八、过滤器聚合组合实战8.1 综合监控大盘以下示例构建一个完整的服务监控聚合——同时统计总体请求量、按环境的错误率、按服务的响应时间分布及采样热URLPOST/api_logs/_search{size:0,query:{range:{timestamp:{gte:now-1h}}},aggs:{by_environment:{filters:{other_bucket_key:unknown,filters:{production:{term:{env:production}},staging:{term:{env:staging}},development:{term:{env:development}}}},aggs:{errors:{filter:{range:{status:{gte:500}}},aggs:{by_service:{terms:{field:service.keyword,size:10},aggs:{response_stats:{extended_stats:{field:response_time}}}}}},sampled_urls:{sampler:{shard_size:500},aggs:{hot_urls:{terms:{field:url.keyword,size:10}}}}}}}}九、总结与最佳实践核心要点回顾filter聚合创建单桶子集对特定条件下的文档进行聚合而不影响其他聚合filters聚合并行执行多个过滤器适合多维度指标对比分析adjacency_matrix分析过滤器之间的交叉关系适合标签共现和用户交集分析nested聚合专门针对嵌套文档类型reverse_nested可从嵌套上下文返回父文档层级sampler采样聚合在高基数场景下以抽样换取性能适合探索性分析和热榜统计最佳实践建议优先用filter聚合而非顶层query当需要对不同聚合子集分别计算时用filter聚合实现更灵活filters命名过滤器给每个过滤器有意义的名称便于解析响应和理解业务含义adjacency_matrix注意桶数量N个过滤器最多产生N²/2个桶控制N在合理范围建议≤20nested聚合注意性能嵌套聚合会展开所有嵌套文档数据量大时性能影响显著sampler配合significant_terms采样后使用significant_terms可获得统计学上有意义的top N结果嵌套深度不宜过多虽然Elasticsearch没有硬限制聚合嵌套层级但实践建议控制在3级以内上一篇【第32篇】Elasticsearch时间序列聚合——date_histogram与auto_date_histogram下一篇【第34篇】Elasticsearch管道聚合与聚合实战——bucket_script与移动平均
【Elasticsearch从入门到精通】第33篇:Elasticsearch过滤器聚合与嵌套聚合——filter、filters与adjacency_matrix
上一篇【第32篇】Elasticsearch时间序列聚合——date_histogram与auto_date_histogram下一篇【第34篇】Elasticsearch管道聚合与聚合实战——bucket_script与移动平均摘要在真实业务场景中聚合分析往往需要在特定条件下进行——例如仅统计已支付订单的平均金额或分别统计错误日志、警告日志和正常日志的占比。Elasticsearch提供了强大的过滤器聚合体系来解决这些问题。本文深入讲解filter单过滤器聚合在查询上下文下进一步缩小聚合范围、filters多过滤器聚合多个命名过滤器并行执行、adjacency_matrix邻接矩阵聚合分析标签之间的共现关系。同时针对Elasticsearch的特殊文档类型本文全面覆盖nested嵌套对象聚合解决嵌套文档独立查询的问题、reverse_nested反嵌套聚合从嵌套上下文返回父文档、parent父子文档聚合以及sampler采样聚合高基数场景下的性能优化方案。关键词filter聚合、filters聚合、邻接矩阵、嵌套聚合、采样聚合。一、过滤器聚合体系概述1.1 为什么需要过滤器聚合常规的聚合操作是对查询返回的所有文档执行计算。但在实际分析中常有如下需求“统计上海市用户的订单总值”需要对特定城市的用户执行聚合“分别统计各产品线的销售额和退货额”需要并行执行多个不同条件的聚合“分析哪些商品标签经常同时出现”需要分析标签交叉关系过滤器聚合通过在聚合层面添加过滤条件实现了对聚合范围的精细化控制而无需修改顶层查询。1.2 三种过滤器聚合对比聚合类型用途过滤器数量输出结构filter单一条件下的聚合1个单桶filters多条件并行聚合多个多桶并列adjacency_matrix条件交叉共现分析多个矩阵二、filter单过滤器聚合2.1 基本概念filter聚合创建一个包含与指定过滤器匹配的所有文档的单个桶。通常用于将聚合上下文缩小到特定的文档子集。POST/orders/_search{size:0,aggs:{paid_orders:{filter:{term:{status:paid}},aggs:{avg_amount:{avg:{field:amount}}}}}}此请求首先用filter聚合筛选statuspaid的文档然后在该子集上计算平均金额。2.2 结合range范围过滤的实战POST/products/_search{size:0,aggs:{high_value_products:{filter:{bool:{must:[{range:{price:{gte:1000}}},{term:{in_stock:true}}]}},aggs:{by_category:{terms:{field:category.keyword,size:10},aggs:{avg_rating:{avg:{field:rating}}}}}}}}2.3 filter聚合与query过滤的区别一个常见的困惑是既然顶层query已经可以过滤文档为什么还需要filter聚合区别在于query过滤影响所有聚合的计算范围filter聚合只在特定聚合子树中生效不影响其他聚合POST/orders/_search{size:0,query:{range:{order_date:{gte:2026-01-01}}},aggs:{all_orders_stats:{extended_stats:{field:amount}},large_orders:{filter:{range:{amount:{gte:1000}}},aggs:{large_stats:{extended_stats:{field:amount}}}}}}上面示例中all_orders_stats统计的是2026年所有订单而large_orders只在2026年且金额1000的订单上计算。三、filters多过滤器聚合3.1 基本概念与命名过滤器filters聚合可以并行运行多个过滤器每个过滤器产生一个独立的桶POST/logs/_search{size:0,aggs:{log_levels:{filters:{filters:{errors:{match:{message:error}},warnings:{match:{message:warning}},info:{match:{message:info}}}}}}}响应示例{aggregations:{log_levels:{buckets:{errors:{doc_count:45},warnings:{doc_count:120},info:{doc_count:850}}}}}3.2 other_bucket_key——捕获不匹配文档other_bucket_key参数为不与任何过滤器匹配的文档创建额外桶POST/logs/_search{size:0,aggs:{log_levels_with_other:{filters:{other_bucket_key:other_messages,filters:{errors:{match:{message:error}},warnings:{match:{message:warning}}}}}}}这会额外生成一个名为other_messages的桶收集所有既不匹配error也不匹配warning的日志。3.3 匿名过滤器数组也可以使用过滤器数组无需命名POST/logs/_search{size:0,aggs:{anonymous_filters:{filters:{filters:[{match:{message:error}},{match:{message:warning}},{match:{message:info}}]}}}}响应中桶按数组顺序返回为匿名桶key为数字索引。3.4 命名过滤器 vs 匿名过滤器特性命名过滤器匿名过滤器写法{errors: {...}}[{...}]桶标识自定义名称数字索引(0,1,2…)可读性高低适用场景需要明确识别每个桶含义动态生成的过滤器列表other_bucket_key支持支持3.5 实战多维度业务指标并行统计POST/ecommerce/_search{size:0,aggs:{business_metrics:{filters:{other_bucket_key:other,filters:{new_customers:{term:{is_new_customer:true}},high_value:{range:{order_amount:{gte:500}}},mobile_orders:{term:{device_type:mobile}},returned:{term:{status:returned}}}},aggs:{revenue:{sum:{field:order_amount}},avg_items:{avg:{field:item_count}}}}}}四、adjacency_matrix邻接矩阵聚合4.1 核心概念adjacency_matrix聚合提供了一组过滤器的交叉分析返回一个邻接矩阵。对于N个过滤器响应包含所有单个过滤器桶及其两两交叉桶。POST/emails/_search{size:0,aggs:{interactions:{adjacency_matrix:{filters:{grpA:{terms:{accounts:[hillary,sidney]}},grpB:{terms:{accounts:[donald,mitt]}},grpC:{terms:{accounts:[vladimir,nigel]}}}}}}}响应将包含以下桶grpA仅A相关的文档grpB仅B相关的文档grpC仅C相关的文档grpAgrpB同时匹配A和B的文档grpAgrpC同时匹配A和C的文档grpBgrpC同时匹配B和C的文档4.2 对称矩阵——为何不返回全部交叉邻接矩阵是对称的因此grpAgrpC与grpCgrpA是同一组文档。Elasticsearch会对过滤器名称排序始终用字典序较小的名称作为分隔符左侧。4.3 自定义分隔符默认交叉桶用连接过滤器名称可通过separator参数自定义POST/tags/_search{size:0,aggs:{tag_cooccurrence:{adjacency_matrix:{filters:{tech:{term:{tags:tech}},ai:{term:{tags:ai}},cloud:{term:{tags:cloud}}},separator: }}}}4.4 过滤器数量限制对于N个过滤器矩阵最多产生N²/2个桶。默认最大过滤器数为100可通过index.max_adjacency_matrix_filters索引设置更改。4.5 实战标签共现分析POST/articles/_search{size:0,aggs:{tag_analysis:{adjacency_matrix:{filters:{elasticsearch:{term:{tags:Elasticsearch}},kibana:{term:{tags:Kibana}},logstash:{term:{tags:Logstash}},beats:{term:{tags:Beats}}}},aggs:{avg_views:{avg:{field:view_count}}}}}}五、nested嵌套对象聚合5.1 nested类型的特殊挑战在Elasticsearch中当字段被定义为nested类型时每个嵌套对象被作为独立的隐藏文档存储。普通的聚合无法正确地在嵌套对象上工作——需要专门的nested聚合。索引映射示例PUT/store{mappings:{properties:{products:{type:nested,properties:{name:{type:keyword},price:{type:double},quantity:{type:integer}}}}}}5.2 nested聚合用法GET/store/_search{size:0,aggs:{product_analysis:{nested:{path:products},aggs:{product_names:{terms:{field:products.name.keyword}},avg_price:{avg:{field:products.price}}}}}}5.3 reverse_nested反嵌套聚合reverse_nested聚合用于从嵌套文档上下文返回到父文档层级对父文档执行聚合GET/store/_search{size:0,aggs:{product_analysis:{nested:{path:products},aggs:{by_name:{terms:{field:products.name.keyword},aggs:{product_stats:{reverse_nested:{},aggs:{store_info:{terms:{field:store_location.keyword}}}}}}}}}}这个查询首先在嵌套的products中按名称分组然后通过reverse_nested返回到父文档层级统计每个商品名称对应的店铺位置分布。六、parent父子文档聚合6.1 join类型回顾在父子文档关系中父文档和子文档是独立的文档通过join字段关联PUT/company{mappings:{properties:{relation:{type:join,relations:{department:employee}}}}}6.2 children聚合对子文档聚合POST/company/_search{size:0,aggs:{departments:{terms:{field:name.keyword},aggs:{employees:{children:{type:employee},aggs:{avg_salary:{avg:{field:salary}},max_salary:{max:{field:salary}}}}}}}}6.3 parent聚合对父文档聚合从子文档反向聚合父文档POST/company/_search{size:0,query:{term:{title.keyword:Senior Engineer}},aggs:{senior_engineers:{terms:{field:name.keyword},aggs:{their_departments:{parent:{type:department},aggs:{dept_name:{terms:{field:name.keyword}}}}}}}}七、sampler采样聚合7.1 适用场景在高基数cardinality场景下terms聚合需要精确计算所有唯一值对内存和CPU的开销极大。例如统计一个拥有数亿记录索引中的最热门URL。sampler聚合通过对文档进行抽样大幅降低计算量同时在统计意义上保持结果的相关性POST/click_logs/_search{size:0,query:{match:{query:error}},aggs:{sample:{sampler:{shard_size:200},aggs:{top_keywords:{significant_terms:{field:keywords,size:20}}}}}}7.2 shard_size参数shard_size控制每个分片采样的文档数量。采样发生在分片级别采样后的文档将用于子聚合的计算。权衡shard_size越小 → 查询越快但准确性越低shard_size越大 → 结果更准确但性能接近全量聚合7.3 sampler vs 全量聚合对比维度sampler采样全量聚合速度快仅处理采样文档慢处理所有文档准确性近似统计有效精确内存低高适用场景高基数、探索性分析精确报表、审计代表性取决于抽样比例100%八、过滤器聚合组合实战8.1 综合监控大盘以下示例构建一个完整的服务监控聚合——同时统计总体请求量、按环境的错误率、按服务的响应时间分布及采样热URLPOST/api_logs/_search{size:0,query:{range:{timestamp:{gte:now-1h}}},aggs:{by_environment:{filters:{other_bucket_key:unknown,filters:{production:{term:{env:production}},staging:{term:{env:staging}},development:{term:{env:development}}}},aggs:{errors:{filter:{range:{status:{gte:500}}},aggs:{by_service:{terms:{field:service.keyword,size:10},aggs:{response_stats:{extended_stats:{field:response_time}}}}}},sampled_urls:{sampler:{shard_size:500},aggs:{hot_urls:{terms:{field:url.keyword,size:10}}}}}}}}九、总结与最佳实践核心要点回顾filter聚合创建单桶子集对特定条件下的文档进行聚合而不影响其他聚合filters聚合并行执行多个过滤器适合多维度指标对比分析adjacency_matrix分析过滤器之间的交叉关系适合标签共现和用户交集分析nested聚合专门针对嵌套文档类型reverse_nested可从嵌套上下文返回父文档层级sampler采样聚合在高基数场景下以抽样换取性能适合探索性分析和热榜统计最佳实践建议优先用filter聚合而非顶层query当需要对不同聚合子集分别计算时用filter聚合实现更灵活filters命名过滤器给每个过滤器有意义的名称便于解析响应和理解业务含义adjacency_matrix注意桶数量N个过滤器最多产生N²/2个桶控制N在合理范围建议≤20nested聚合注意性能嵌套聚合会展开所有嵌套文档数据量大时性能影响显著sampler配合significant_terms采样后使用significant_terms可获得统计学上有意义的top N结果嵌套深度不宜过多虽然Elasticsearch没有硬限制聚合嵌套层级但实践建议控制在3级以内上一篇【第32篇】Elasticsearch时间序列聚合——date_histogram与auto_date_histogram下一篇【第34篇】Elasticsearch管道聚合与聚合实战——bucket_script与移动平均