基于NEST实现Elasticsearch产品规格Name/Value嵌套聚合的子聚合取值问题
解决Elasticsearch Nested聚合获取子聚合值的问题
首先得提一个关键前提:你的productSpecification字段在当前映射里是普通object类型,但要实现规格名称和对应值的关联聚合,必须把它改成nested类型!因为普通object字段会被Elasticsearch扁平化存储,规格的Name和Value会失去关联关系,聚合出来的结果会完全不符合预期。先修正你的映射:
"mappings": { "product": { "properties": { "productSpecification": { "type": "nested", // 这里必须改成nested类型 "properties": { "productSpecificationId": { "type": "long" }, "specificationId": { "type": "long" }, "productId": { "type": "long" }, "name": { "fielddata": true, "type": "text" }, "value": { "fielddata": true, "type": "text" } } }, // 其他字段保持不变 "name": { "type": "text", "fields": { "keyword": { "ignore_above": 256, "type": "keyword" } } }, "description": { "type": "text", "fields": { "keyword": { "ignore_above": 256, "type": "keyword" } } }, "reviewRatingCount": { "type": "integer" }, "productId": { "type": "integer" }, "url": { "type": "text", "fields": { "keyword": { "ignore_above": 256, "type": "keyword" } } }, "dispatchTimeInDays": { "type": "integer" }, "productCode": { "type": "text", "fields": { "keyword": { "ignore_above": 256, "type": "keyword" } } } } } }
接下来修正你的NEST聚合代码——因为字段是nested类型,必须用Nested聚合包裹内层的Terms聚合,才能保证每个规格的Name和Value是关联的:
var response = await client.SearchAsync<Product>(s => s .Aggregations(a => a .Nested("spec_nested", n => n .Path(p => p.ProductSpecification) .Aggregations(aa => aa .Terms("level1", t => t .Field(f => f.ProductSpecification.First().Name) .Aggregations(aaa => aaa .Terms("level2", t2 => t2 .Field(f3 => f3.ProductSpecification.First().Value) ) ) ) ) ) ) );
现在重点解决你最头疼的获取子聚合值的问题:每个level1的桶里都包含level2子聚合,你需要从当前桶的Aggregations属性里取出对应的Terms聚合,再遍历它的桶即可:
if (response.Aggregations != null) { rtxAggs.Clear(); rtxAggs.AppendText(Environment.NewLine); // 先拿到外层的nested聚合 var nestedAgg = response.Aggs.Nested("spec_nested"); // 再拿到level1的规格名称聚合 var level1Agg = nestedAgg.Aggs.Terms("level1"); foreach (var level1Bucket in level1Agg.Buckets) { rtxAggs.AppendText($"规格名称: {level1Bucket.Key}{Environment.NewLine}"); // 从当前规格名称的桶中取出对应的子聚合(规格值) var level2Agg = level1Bucket.Aggs.Terms("level2"); foreach (var level2Bucket in level2Agg.Buckets) { rtxAggs.AppendText($" 对应值: {level2Bucket.Key} (匹配产品数: {level2Bucket.DocCount}){Environment.NewLine}"); } } }
关键补充说明:
- 为什么一定要加
Nested聚合?因为nested类型的字段会被Elasticsearch当作独立的微型文档存储,只有通过Nested聚合才能正确关联每个规格的Name和Value,避免出现“规格A的名称和规格B的值错误聚合”的问题。 - 如果你的映射暂时无法修改(比如已有大量历史数据),那普通object类型的聚合逻辑代码和上面一致,但结果会有数据关联错误的问题,不建议这么做。
内容的提问来源于stack exchange,提问作者rs82uk
相关产品推荐
相关产品推荐

