在Nest中创建动态范围聚合的技术求助
问题:动态创建Nest范围聚合并转换为AggregationDictionary
从客户端获取了一组带范围的POCO facets,需要将它们最终转换为AggregationDictionary,但无法掌握创建动态聚合(类型可能为RangeAggregationDescriptor)的语法,特此求助。
POCO对象定义
public class TypedFacets { public string Name { get; set; } public string Field { get; set; } public IReadOnlyCollection<Range> RangeValues { get; set; } = new List<Range>(); public int Size { get; set; } } public class Range { public string Name { get; set; } public double? From { get; set; } public double? To { get; set; } }
原Nest代码(存在问题)
var facets = new List<TypedFacets>() { new TypedFacets() { Name = "potatoRange", Field = "potatoRange", RangeValues = new List<Range>() { new Range() { From = 0, To = null, Name = "chips" }, new Range() { From = 1, To = null, Name = "crisps" } } } }; var aggregations = new AggregationContainerDescriptor<Template>(); facets.Where(f => f.RangeValues.Any()).ToList().ForEach(f => { var rad = new RangeAggregationDescriptor<Template>(); f.RangeValues.ToList().ForEach(rangeValue => { rad = rad.Ranges(rs => rs.From(rangeValue.From).To(rangeValue.To).Key(rangeValue.Name)); }); // 此行代码无法运行,需要修改 aggregations.Range(f.Name, r => r .Field(f.Field).Ranges(rs => rad.Ranges)); }); return ((IAggregationContainer)aggregations).Aggregations;
解决方案
原代码的核心问题是试图直接复用已构建的RangeAggregationDescriptor的Ranges集合,而Nest的聚合配置是通过构建器委托逐步构建的,不能直接传递已有的配置结果。正确的做法是在Range方法的配置委托内部,直接遍历RangeValues逐个添加范围规则。
修正后的完整代码
var facets = new List<TypedFacets>() { new TypedFacets() { Name = "potatoRange", Field = "potatoRange", RangeValues = new List<Range>() { new Range() { From = 0, To = null, Name = "chips" }, new Range() { From = 1, To = null, Name = "crisps" } } } }; var aggregationDescriptor = new AggregationContainerDescriptor<Template>(); // 遍历所有带范围的facet foreach (var facet in facets.Where(f => f.RangeValues.Any())) { aggregationDescriptor = aggregationDescriptor.Range(facet.Name, r => r .Field(facet.Field) // 直接在Ranges委托内遍历构建每个范围 .Ranges(rs => { foreach (var rangeValue in facet.RangeValues) { rs = rs.Range(rng => rng .From(rangeValue.From) .To(rangeValue.To) .Key(rangeValue.Name)); } return rs; })); } // 转换为AggregationDictionary返回 var aggregations = ((IAggregationContainer)aggregationDescriptor).Aggregations; return aggregations;
简化写法(用LINQ Aggregate)
如果想让代码更简洁,可以用LINQ的Aggregate方法替代foreach遍历:
foreach (var facet in facets.Where(f => f.RangeValues.Any())) { aggregationDescriptor = aggregationDescriptor.Range(facet.Name, r => r .Field(facet.Field) .Ranges(rs => facet.RangeValues.Aggregate(rs, (current, rangeValue) => current.Range(rng => rng .From(rangeValue.From) .To(rangeValue.To) .Key(rangeValue.Name) ) ))); }
关键说明
- 不需要提前创建
RangeAggregationDescriptor实例,直接在Range方法的配置委托内完成所有范围规则的构建 - 用
foreach替代List.ForEach,避免不必要的集合转换,同时提升代码可读性 - 每个范围规则通过
Range()方法逐个添加,最终返回构建完成的范围配置器
内容的提问来源于stack exchange,提问作者Luke
相关产品推荐
相关产品推荐

