如何在 Elastic Search 中进行双重嵌套聚合?
How to do double nested aggregation in Elastic Search?
我有这样的数据结构:
class SearchItem
{
public string Category { get; set; }
public string SubCategory { get; set; }
public string Description { get; set; }
public double SalePrice { get; set; }
public IList<SearchFilter> Filters { get; set; }
}
class SearchFilter
{
public string Group { get; set; }
public string Attribute { get; set; }
}
如您所见,Filters
是 nested array
。 Filters
数据示例:
Color: Red,
Size: Medium,
Material: N/A
因此,其他项目可以具有相同的组名和属性值。
我尝试得到的结果:
Color
--Red(18)
--Yellow(5)
Size
--Medium(20)
--Large(5)
这是我的映射:
i.Mappings(m =>
m.Map<SearchItem>(map =>
map.AutoMap().Properties(p =>
p.Text(s => s.Name(n => n.Description).Analyzer("snowball"))
.Nested<SearchFilter>(
n=>n.Name(nn=>nn.Filters)
.Properties(pr => pr
.Text(t => t.Name(nn => nn.Attribute))
.Text(t => t.Name(nn => nn.Group)))
)
))));
这是我的查询:
var result = _elasticClient.Search<SearchItem>(x =>
x.Sort(ss => ss.Ascending(ff => ff.Sequence)).Size(48)
.Query(q => q.Bool(b =>
b.Must(ConstructBaseQuery(search))))
.Aggregations(a => a
.Nested("filters", n => n
.Path(p => p.Filters)
.Aggregations(aa => aa.Terms("groups", t => t.Field(f => f.Filters.Suffix("Group"))
.Aggregations(aaa => aaa
.Terms("attribute", tt => tt.Field(ff => ff.Filters.Suffix("Attribute"))))
)))));
问题是查询不进行聚合。如果我像这样进行单一聚合的事件:
var result = _elasticClient.Search<SearchItem>(x =>
x.Sort(ss => ss.Ascending(ff => ff.Sequence)).Size(48)
.Query(q => q.Bool(b =>
b.Must(ConstructBaseQuery(search))))
.Aggregations(a => a
.Nested("filters", n => n
.Path(p => p.Filters)
.Aggregations(aa => aa.Terms("groups", t => t.Field(f => f.Filters.Suffix("Group")))))));
这是原始查询:
{
"size": 48,
"sort": [
{
"sequence": {
"order": "asc"
}
}
],
"aggs": {
"filters": {
"nested": {
"path": "filters"
},
"aggs": {
"groups": {
"terms": {
"field": "filters.Group"
}
}
}
}
},
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "modern tables",
"operator": "and",
"fields": [
"description"
]
}
}
]
}
}
}
没有任何效果。所以,我有两个问题:
- 如何使用 C# NEST 进行此类聚合?
- 我想将嵌套字段(
Group
和 Attribute
)设置为 not_analyzed
,但没有找到方法。
阅读 Elastic 5.0
的文档几个小时后,我找到了答案:
1) 将 小写 用于嵌套属性很重要。所以错误在这里:"field": "filters.Group"
。应该是 "filters.group"
2) 在 Elastic 5.0 中我们没有 not_anylyzed
索引。 Elastic 团队删除了 String
类型并添加了两个新类型(文本和关键字)。默认情况下,文本类型聚合是禁用的,如果你想启用它,你应该设置 FieldData = true
。如果不想 analyze
属性,我们应该使用 keyword type
。所以,我的工作指数是:
i.Mappings(m =>
m.Map<SearchItem>(map =>
map.AutoMap().Properties(p =>
p.Text(s => s.Name(n => n.Description).Analyzer("snowball"))
.Nested<SearchFilter>(
n=>n.Name(nn=>nn.Filters)
.Properties(pr => pr
.Keyword(t => t.Name(nn => nn.Attribute))
.Keyword(t => t.Name(nn => nn.Group)))
)
))));
如果有人坚持处理响应:
class FilterGroup
{
public string Name { get; set; }
public IList<FilterAttribute> Attributes { get; set; }
public static IEnumerable<FilterGroup> CreateList(ISearchResponse<SearchItem> result)
{
return result.Aggs.Nested("filters").Terms("groups").Buckets.Select(g => new FilterGroup()
{
Name = g.Key,
Attributes = g.Terms("attributes").Buckets.Select(x => new FilterAttribute()
{
Count = (int)x.DocCount,
Name = x.Key
}).ToList()
});
}
}
class FilterAttribute
{
public string Name { get; set; }
public int Count { get; set; }
}
希望对下一个潜水员有用。
我有这样的数据结构:
class SearchItem
{
public string Category { get; set; }
public string SubCategory { get; set; }
public string Description { get; set; }
public double SalePrice { get; set; }
public IList<SearchFilter> Filters { get; set; }
}
class SearchFilter
{
public string Group { get; set; }
public string Attribute { get; set; }
}
如您所见,Filters
是 nested array
。 Filters
数据示例:
Color: Red,
Size: Medium,
Material: N/A
因此,其他项目可以具有相同的组名和属性值。
我尝试得到的结果:
Color
--Red(18)
--Yellow(5)
Size
--Medium(20)
--Large(5)
这是我的映射:
i.Mappings(m =>
m.Map<SearchItem>(map =>
map.AutoMap().Properties(p =>
p.Text(s => s.Name(n => n.Description).Analyzer("snowball"))
.Nested<SearchFilter>(
n=>n.Name(nn=>nn.Filters)
.Properties(pr => pr
.Text(t => t.Name(nn => nn.Attribute))
.Text(t => t.Name(nn => nn.Group)))
)
))));
这是我的查询:
var result = _elasticClient.Search<SearchItem>(x =>
x.Sort(ss => ss.Ascending(ff => ff.Sequence)).Size(48)
.Query(q => q.Bool(b =>
b.Must(ConstructBaseQuery(search))))
.Aggregations(a => a
.Nested("filters", n => n
.Path(p => p.Filters)
.Aggregations(aa => aa.Terms("groups", t => t.Field(f => f.Filters.Suffix("Group"))
.Aggregations(aaa => aaa
.Terms("attribute", tt => tt.Field(ff => ff.Filters.Suffix("Attribute"))))
)))));
问题是查询不进行聚合。如果我像这样进行单一聚合的事件:
var result = _elasticClient.Search<SearchItem>(x =>
x.Sort(ss => ss.Ascending(ff => ff.Sequence)).Size(48)
.Query(q => q.Bool(b =>
b.Must(ConstructBaseQuery(search))))
.Aggregations(a => a
.Nested("filters", n => n
.Path(p => p.Filters)
.Aggregations(aa => aa.Terms("groups", t => t.Field(f => f.Filters.Suffix("Group")))))));
这是原始查询:
{
"size": 48,
"sort": [
{
"sequence": {
"order": "asc"
}
}
],
"aggs": {
"filters": {
"nested": {
"path": "filters"
},
"aggs": {
"groups": {
"terms": {
"field": "filters.Group"
}
}
}
}
},
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "modern tables",
"operator": "and",
"fields": [
"description"
]
}
}
]
}
}
}
没有任何效果。所以,我有两个问题:
- 如何使用 C# NEST 进行此类聚合?
- 我想将嵌套字段(
Group
和Attribute
)设置为not_analyzed
,但没有找到方法。
阅读 Elastic 5.0
的文档几个小时后,我找到了答案:
1) 将 小写 用于嵌套属性很重要。所以错误在这里:"field": "filters.Group"
。应该是 "filters.group"
2) 在 Elastic 5.0 中我们没有 not_anylyzed
索引。 Elastic 团队删除了 String
类型并添加了两个新类型(文本和关键字)。默认情况下,文本类型聚合是禁用的,如果你想启用它,你应该设置 FieldData = true
。如果不想 analyze
属性,我们应该使用 keyword type
。所以,我的工作指数是:
i.Mappings(m =>
m.Map<SearchItem>(map =>
map.AutoMap().Properties(p =>
p.Text(s => s.Name(n => n.Description).Analyzer("snowball"))
.Nested<SearchFilter>(
n=>n.Name(nn=>nn.Filters)
.Properties(pr => pr
.Keyword(t => t.Name(nn => nn.Attribute))
.Keyword(t => t.Name(nn => nn.Group)))
)
))));
如果有人坚持处理响应:
class FilterGroup
{
public string Name { get; set; }
public IList<FilterAttribute> Attributes { get; set; }
public static IEnumerable<FilterGroup> CreateList(ISearchResponse<SearchItem> result)
{
return result.Aggs.Nested("filters").Terms("groups").Buckets.Select(g => new FilterGroup()
{
Name = g.Key,
Attributes = g.Terms("attributes").Buckets.Select(x => new FilterAttribute()
{
Count = (int)x.DocCount,
Name = x.Key
}).ToList()
});
}
}
class FilterAttribute
{
public string Name { get; set; }
public int Count { get; set; }
}
希望对下一个潜水员有用。