Elasticsearch聚合获取Brand唯一值异常,如何得到正确结果?
解决Elasticsearch聚合Brand字段返回拆分值的问题
我来帮你分析下问题根源,以及对应的解决方案:
为什么会返回拆分的"Super"、"brand"、"A"、"B"?
你的Brand字段应该是text类型,Elasticsearch默认会对text字段执行分词处理——把像"Super brand A"这样的短语拆分成独立的词语,所以terms聚合会基于这些拆分后的词来统计,自然得到了四个零散的值。
为什么加.Suffix("keyword")返回空列表?
这说明你的索引里,Brand字段并没有定义keyword类型的子字段。text字段要支持精确聚合,必须显式配置keyword子字段,否则Elasticsearch找不到对应的字段,就会返回空结果。
解决方案
方案1:修改字段映射,添加keyword子字段(推荐)
这是最标准的做法,适合可以重新创建索引并导入数据的场景:
- 先删除旧索引(如果数据可以重新导入),然后创建带有正确映射的新索引:
PUT /index { "mappings": { "type": { "properties": { "Brand": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } } } } } }
- 重新导入所有产品数据到这个新索引。
- 然后用你修改后的代码(加上
.Suffix("keyword"))执行聚合:
var response = new ElasticClient().Search<DTO>(s => s .Index("index") .Type("type") .Aggregations(a => a .Terms("unique", t => t .Field(f => f.Brand.Suffix("keyword")) .Size(1000) ) ) ); var brands = (((BucketAggregate)response.Aggregations.First().Value).Items) .Cast<KeyedBucket<object>>() .Select(x => x.Key.ToString()) .ToList();
这样就能得到"Super brand A"和"Super brand B"两个正确的唯一值了。
方案2:临时启用fielddata(不推荐,仅应急用)
如果无法重新创建索引或导入数据,你可以临时开启text字段的fielddata来实现精确聚合,但注意这会消耗大量内存,不适合大数据量索引:
var response = new ElasticClient().Search<DTO>(s => s .Index("index") .Type("type") .Aggregations(a => a .Terms("unique", t => t .Field(f => f.Brand) .Size(1000) .Fielddata(true) ) ) );
方案3:直接将Brand字段设为keyword类型(适合无需分词的场景)
如果你的品牌名称不需要做分词搜索,只需要精确匹配和聚合,那可以直接把Brand字段的类型设为keyword,这样默认就能支持精确聚合,不需要子字段:
PUT /index { "mappings": { "type": { "properties": { "Brand": { "type": "keyword" } } } } }
之后直接用原代码(不需要加.Suffix("keyword"))就能得到正确结果。
内容的提问来源于stack exchange,提问作者Jan Kowalski
相关产品推荐
相关产品推荐

