如何在 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; }
}

如您所见,Filtersnested arrayFilters 数据示例:

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"
            ]
          }
        }
      ]
    }
  }
}

没有任何效果。所以,我有两个问题:

  1. 如何使用 C# NEST 进行此类聚合?
  2. 我想将嵌套字段(GroupAttribute)设置为 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; }
}

希望对下一个潜水员有用。