ElasticSearch - 返回唯一值

Posted

技术标签:

【中文标题】ElasticSearch - 返回唯一值【英文标题】:ElasticSearch - Return Unique Values 【发布时间】:2014-10-17 09:10:04 【问题描述】:

如何从记录中获取所有languages 的值并使其唯一。

记录

PUT items/1
 "language" : 10 

PUT items/2
 "language" : 11 

PUT items/3
 "language" : 10 

查询

GET items/_search
 ... 

# => Expected Response
[10, 11]

任何帮助都会很棒。

【问题讨论】:

fields: [languages] 将只给出给定字段的值,但在代码中使它们唯一可能更容易做到。虽然可能有一个方便的聚合可以为您完成。 对于那些研究这个话题的人,这里也有有用的讨论:Find distinct values, not distinct counts in elasticsearch 【参考方案1】:

您可以使用terms aggregation。


"size": 0,
"aggs" : 
    "langs" : 
        "terms" :  "field" : "language",  "size" : 500 
    

聚合中的size 参数指定要包含在聚合结果中的最大项数。如果您需要所有结果,请将其设置为大于数据中唯一字词数量的值。

搜索将返回如下内容:


"took" : 16,
"timed_out" : false,
"_shards" : 
  "total" : 2,
  "successful" : 2,
  "failed" : 0
,
"hits" : 
"total" : 1000000,
"max_score" : 0.0,
"hits" : [ ]
,
"aggregations" : 
  "langs" : 
    "buckets" : [ 
      "key" : "10",
      "doc_count" : 244812
    , 
      "key" : "11",
      "doc_count" : 136794
 
    , 
      "key" : "12",
      "doc_count" : 32312
        ]
    
  

【讨论】:

"fields" : ["language"] 带回相同的结果。您能否扩展您的答案以查看聚合框架是否可以仅返回语言值? #=> [10, 11, 10] @CharlesJHardy,它没有相同的结果。您要查找的数据位于“聚合”键下。我用示例结果编辑了我的答案。您还可以/应该设置 "size": 0,以便不包含任何文档,只包含您想要的聚合结果。 请注意,如果language 有许多可能的值,您可能需要添加size=0shard_size=0,以确保获得所有 值。见elasticsearch.org/guide/en/elasticsearch/reference/current/… 我认为这个答案没有解决 OP。原始问题想要不同的 values 不计数。我错过了什么吗? @BHBH,答案确实提供了不同的值。它们是“关键”值,即“10”、“11”和“12”。 (聚合 > 语言 > 存储桶 > 键...)【参考方案2】:

我也在为自己寻找这种解决方案。我在terms aggregation 中找到了参考。

所以,按照下面的方法是正确的解决方案。


"aggs" : 
    "langs" : 
        "terms" :  "field" : "language",  
                    "size" : 500 
    

但是如果你遇到以下错误:

"error": 
        "root_cause": [
            
                "type": "illegal_argument_exception",
                "reason": "Fielddata is disabled on text fields by default. Set fielddata=true on [fastest_method] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead."
            
        ]

在这种情况下,您必须在请求中添加“KEYWORD”,如下所示:

   
    "aggs" : 
        "langs" : 
            "terms" :  "field" : "language.keyword",  
                        "size" : 500 
        
    

【讨论】:

【参考方案3】:

如果您想获取每个language 字段唯一值的第一个文档,您可以这样做:


 "query": 
    "match_all": 
    
  ,
  "collapse": 
    "field": "language.keyword",
    "inner_hits": 
    "name": "latest",
      "size": 1
    
  

【讨论】:

"name": "latest",是 ES 语法中的 "name" 还是自定义用户字段? 注意:折叠字段必须是单值【参考方案4】:

Elasticsearch 1.1+ 具有 Cardinality Aggregation,它将为您提供唯一计数

请注意,它实际上是一个近似值,精度可能会随着高基数数据集而降低,但在我的测试中它通常相当准确。

您还可以使用precision_threshold 参数调整精度。权衡或当然是内存使用。

文档中的这张图表显示了更高的precision_threshold 如何导致更准确的结果。


【讨论】:

Cardinality Aggregation 是否保证如果某个术语存在,那么它将出现在结果中(计数 >= 1)?或者它可能会遗漏一些在大型数据集中只出现一次的术语? @mark 它取决于您设置的精度阈值。阈值越高,错过的机会就越小。请注意,精度阈值设置的限制为 40,000。这意味着,高于此的数据集将有一个估计值,因此可能会遗漏单个值 我相信这个答案是错误的。基数聚合是一个很好的工具。但是,任务是检索术语本身,而不是估计有多少不同的术语。【参考方案5】:

如果您想在不使用任何近似值或设置幻数 (size: 500) 的情况下获得所有唯一值,请使用 COMPOSITE AGGREGATION (ES 6.5+)

来自official documentation:

“如果您想检索嵌套术语聚合中的所有术语或所有术语组合您应该使用 COMPOSITE AGGREGATION,它允许对所有可能的术语进行分页而不是设置大小大于 terms 聚合中字段的基数。terms 聚合旨在返回顶部术语,不允许分页。”

javascript 中的实现示例:

const ITEMS_PER_PAGE = 1000;

const body =  
    "size": 0, // Returning only aggregation results: https://www.elastic.co/guide/en/elasticsearch/reference/current/returning-only-agg-results.html
    "aggs" : 
        "langs": 
            "composite" : 
                "size": ITEMS_PER_PAGE,
                "sources" : [
                     "language":  "terms" :  "field": "language"   
                ]
            
        
     
;

const uniqueLanguages = [];

while (true) 
  const result = await es.search(body);

  const currentUniqueLangs = result.aggregations.langs.buckets.map(bucket => bucket.key);

  uniqueLanguages.push(...currentUniqueLangs);

  const after = result.aggregations.langs.after_key;

  if (after) 
      // continue paginating unique items
      body.aggs.langs.composite.after = after;
   else 
      break;
  


console.log(uniqueLanguages);

【讨论】:

这应该是公认的答案。解决了size参数的问题【参考方案6】:

必须区分两个字段(derivative_id 和车辆类型)并按最便宜的汽车排序。不得不嵌套aggs。

GET /cars/_search

  "size": 0,
  "aggs": 
    "distinct_by_derivative_id": 
      "terms":  
        "field": "derivative_id"
      ,
      "aggs": 
        "vehicle_type": 
          "terms": 
            "field": "vehicle_type"
          ,
          "aggs": 
            "cheapest_vehicle": 
              "top_hits": 
                "sort": [
                   "rental":  "order": "asc"  
                ],
                "_source":  "includes": [ "manufacturer_name",
                  "rental",
                  "vehicle_type" 
                  ]
                ,
                "size": 1
              
            
          
        
      
    
  

结果:


  "took" : 3,
  "timed_out" : false,
  "_shards" : 
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  ,
  "hits" : 
    "total" : 
      "value" : 8,
      "relation" : "eq"
    ,
    "max_score" : null,
    "hits" : [ ]
  ,
  "aggregations" : 
    "distinct_by_derivative_id" : 
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        
          "key" : "04",
          "doc_count" : 3,
          "vehicle_type" : 
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [
              
                "key" : "CAR",
                "doc_count" : 2,
                "cheapest_vehicle" : 
                  "hits" : 
                    "total" : 
                      "value" : 2,
                      "relation" : "eq"
                    ,
                    "max_score" : null,
                    "hits" : [
                      
                        "_index" : "cars",
                        "_type" : "_doc",
                        "_id" : "8",
                        "_score" : null,
                        "_source" : 
                          "vehicle_type" : "CAR",
                          "manufacturer_name" : "Renault",
                          "rental" : 89.99
                        ,
                        "sort" : [
                          89.99
                        ]
                      
                    ]
                  
                
              ,
              
                "key" : "LCV",
                "doc_count" : 1,
                "cheapest_vehicle" : 
                  "hits" : 
                    "total" : 
                      "value" : 1,
                      "relation" : "eq"
                    ,
                    "max_score" : null,
                    "hits" : [
                      
                        "_index" : "cars",
                        "_type" : "_doc",
                        "_id" : "7",
                        "_score" : null,
                        "_source" : 
                          "vehicle_type" : "LCV",
                          "manufacturer_name" : "Ford",
                          "rental" : 99.99
                        ,
                        "sort" : [
                          99.99
                        ]
                      
                    ]
                  
                
              
            ]
          
        ,
        
          "key" : "01",
          "doc_count" : 2,
          "vehicle_type" : 
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [
              
                "key" : "CAR",
                "doc_count" : 1,
                "cheapest_vehicle" : 
                  "hits" : 
                    "total" : 
                      "value" : 1,
                      "relation" : "eq"
                    ,
                    "max_score" : null,
                    "hits" : [
                      
                        "_index" : "cars",
                        "_type" : "_doc",
                        "_id" : "1",
                        "_score" : null,
                        "_source" : 
                          "vehicle_type" : "CAR",
                          "manufacturer_name" : "Ford",
                          "rental" : 599.99
                        ,
                        "sort" : [
                          599.99
                        ]
                      
                    ]
                  
                
              ,
              
                "key" : "LCV",
                "doc_count" : 1,
                "cheapest_vehicle" : 
                  "hits" : 
                    "total" : 
                      "value" : 1,
                      "relation" : "eq"
                    ,
                    "max_score" : null,
                    "hits" : [
                      
                        "_index" : "cars",
                        "_type" : "_doc",
                        "_id" : "2",
                        "_score" : null,
                        "_source" : 
                          "vehicle_type" : "LCV",
                          "manufacturer_name" : "Ford",
                          "rental" : 599.99
                        ,
                        "sort" : [
                          599.99
                        ]
                      
                    ]
                  
                
              
            ]
          
        ,
        
          "key" : "02",
          "doc_count" : 2,
          "vehicle_type" : 
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [
              
                "key" : "CAR",
                "doc_count" : 2,
                "cheapest_vehicle" : 
                  "hits" : 
                    "total" : 
                      "value" : 2,
                      "relation" : "eq"
                    ,
                    "max_score" : null,
                    "hits" : [
                      
                        "_index" : "cars",
                        "_type" : "_doc",
                        "_id" : "4",
                        "_score" : null,
                        "_source" : 
                          "vehicle_type" : "CAR",
                          "manufacturer_name" : "Audi",
                          "rental" : 499.99
                        ,
                        "sort" : [
                          499.99
                        ]
                      
                    ]
                  
                
              
            ]
          
        ,
        
          "key" : "03",
          "doc_count" : 1,
          "vehicle_type" : 
            "doc_count_error_upper_bound" : 0,
            "sum_other_doc_count" : 0,
            "buckets" : [
              
                "key" : "CAR",
                "doc_count" : 1,
                "cheapest_vehicle" : 
                  "hits" : 
                    "total" : 
                      "value" : 1,
                      "relation" : "eq"
                    ,
                    "max_score" : null,
                    "hits" : [
                      
                        "_index" : "cars",
                        "_type" : "_doc",
                        "_id" : "5",
                        "_score" : null,
                        "_source" : 
                          "vehicle_type" : "CAR",
                          "manufacturer_name" : "Audi",
                          "rental" : 399.99
                        ,
                        "sort" : [
                          399.99
                        ]
                      
                    ]
                  
                
              
            ]
          
        
      ]
    
  

【讨论】:

【参考方案7】:

默认情况下,aggs 将在数字值上工作,如果你想在字符串字段上工作,你应该在 fie 上启用它

【讨论】:

正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于ElasticSearch - 返回唯一值的主要内容,如果未能解决你的问题,请参考以下文章

使用 Elasticsearch 查询字段的所有唯一值

ElasticSearch系列之什么是ElasticSearch?

如何使弹性搜索多匹配模糊搜索始终返回最小数量的结果

如何在 Elasticsearch 中返回每个发布者的唯一用户数?

ElasticSearch系列之什么是ElasticSearch?

使用Elasticsearch查询字段的所有唯一值