ElasticSearch - 返回唯一值

175

如何获取记录中所有languages的值并将它们唯一化。

记录

PUT items/1
{ "language" : 10 }

PUT items/2
{ "language" : 11 }

PUT items/3
{ "language" : 10 }

查询

GET items/_search
{ ... }

# => Expected Response
[10, 11]

任何帮助都将是极好的。


1
fields: [languages] 只会给出指定字段的值,但在代码中使它们唯一可能更容易。虽然也许有一个方便的聚合可以为您完成这项工作。 - Ashalynd
1
对于那些研究这个主题的人,这里也有有用的讨论:在elasticsearch中查找不同的值,而不是不同的计数 - blong
7个回答

241

您可以使用terms聚合

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

2
"fields" : ["language"] 返回相同的结果。您能否详细说明一下聚合框架是否可以仅返回语言值? #=> [10, 11, 10] - ChuckJHardy
1
@CharlesJHardy,它的结果不一样。你要找的数据在"aggregations"键下面。我已经在我的答案中编辑了一个示例结果。为了只包含你想要的聚合结果,你还可以设置"size": 0,这样就不会包含任何文档了。 - Anton
1
注意,如果您有许多可能的“language”值,则可能希望添加size = 0shard_size = 0,以确保获取所有值。请参见http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-aggregations-bucket-terms-aggregation.html。 - Dror
4
我认为这个回答没有解决原帖的问题。原问题需要不同的“值”,而不是计数。我是否漏掉了什么? - bhurlow
5
@BHBH,这个答案提供了不同的值。它们是“key”值,即“10”,“11”和“12”。(聚合>语言>存储桶>键...) - Anton
显示剩余7条评论

22

我也在寻找这种解决方案。我在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."
            }
        ]}
在这种情况下,您需要在请求中添加"关键词",就像以下示例一样:
   {
    "aggs" : {
        "langs" : {
            "terms" : { "field" : "language.keyword",  
                        "size" : 500 }
        }
    }}

17

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

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

1
“name”: “latest”是来自ES语法还是自定义用户字段? - user1769790
1
作为注意事项:折叠字段必须是单值的。 - ceyun

16

如果您想在没有任何近似值或设定魔数(size: 500)的情况下获取所有独特的值,请使用复合聚合(ES 6.5+)。

来自官方文档

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

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);


1
这应该是被接受的答案。它解决了“size”参数的问题。 - DollarAkshay

10

Elasticsearch 1.1+具有基数聚合,可以给出唯一计数。

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

您还可以使用precision_threshold参数调整精度。权衡的是内存使用情况。

文档中的此图显示了更高的precision_threshold会导致更准确的结果。


相对误差 vs 阈值


2
Cardinality Aggregation是否保证如果一个术语存在,那么它会在结果中出现(计数>=1)?或者它可能会错过一些只在大数据集中出现一次的术语吗? - mark
2
@mark 这取决于您设置的精度阈值。阈值越高,错过的可能性就越小。请注意,精度阈值设置的上限为40,000。这意味着,对于超过该值的数据集,将会进行估计,因此可能会错过单个值。 - Sundar
18
我认为这个答案是错误的。Cardinality聚合是一个很好的工具。然而,任务是检索术语本身,而不是估计有多少不同的术语。 - Anton

4
需要按照两个字段(衍生品ID和车辆类型)进行区分,并按最便宜的汽车排序。必须嵌套聚合。
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
                        ]
                      }
                    ]
                  }
                }
              }
            ]
          }
        }
      ]
    }
  }
}

-2

默认情况下,aggs将适用于数字值,如果您想在字符串字段上工作,则应在字段上启用它


1
目前你的回答不够清晰,请编辑并添加更多细节以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接