ElasticSearch - возвращает уникальные значения


122

Как мне получить значения всех 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

Ответы:


165

Вы можете использовать термины агрегирование .

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

Поиск вернет что-то вроде:

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

sizeПараметр в пределах агрегации указывает максимальное число членов включить в результате агрегации. Если вам нужны все результаты, установите для него значение, превышающее количество уникальных терминов в ваших данных.


2
"fields" : ["language"]возвращает тот же результат. Можете ли вы расширить свой ответ, чтобы увидеть, может ли структура агрегирования возвращать только языковые значения? #=> [10, 11, 10]
ChuckJHardy

1
@CharlesJHardy, такого результата нет. Данные, которые вы ищете, находятся под ключом «агрегаты». Я отредактировал свой ответ примером результата. Вы также можете / должны установить «size»: 0, чтобы не включать какие-либо документы, а только желаемые агрегированные результаты.
Антон

1
Обратите внимание: если у вас есть много возможных значений, languageвы можете добавить size=0и shard_size=0, чтобы убедиться, что вы получили все значения. См. Elasticsearch.org/guide/en/elasticsearch/reference/current/…
Dror

3
Я думаю, что этот ответ не касается OP. Исходный вопрос требует, чтобы различные значения не учитывались. Я что-то упускаю?
bhurlow

4
@BHBH, ответ действительно дает разные значения. Это «ключевые» значения, то есть «10», «11» и «12». (агрегаты> langs> buckets> key ...)
Антон

9

Elasticsearch 1.1+ имеет агрегирование мощности, которое даст вам уникальный счет

Обратите внимание, что на самом деле это приближение, и точность может снизиться с наборами данных с высокой мощностью, но в целом в моем тестировании это довольно точно.

Вы также можете настроить точность с помощью precision_thresholdпараметра. Компромисс, конечно же, - использование памяти.

Этот график из документации показывает, как более высокое значение precision_thresholdприводит к гораздо более точным результатам.


Относительная ошибка против порога


2
Гарантирует ли агрегирование по количеству элементов, что если термин существует, то он появится в результатах (с количеством> = 1)? Или он мог пропустить некоторые термины, которые появляются только один раз в большом наборе данных?
Марк

2
@mark это зависит от установленного вами порога точности. Чем выше порог, тем меньше вероятность его пропуска. Обратите внимание, что существует ограничение в 40 000 настроек порога точности. Это означает, что при более высоком наборе данных будет оценка, и, следовательно, единственное значение может быть пропущено
Сундар

12
Я считаю, что это неверный ответ. Агрегация мощности - отличный инструмент. Однако задача заключалась в том, чтобы получить сами термины, а не оценить их количество.
Антон

4

Я ищу такое решение и для себя. Я нашел ссылку в терминах агрегирования .

Итак, в соответствии с этим правильным решением является следующее.

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

4

если вы хотите получить первый документ для каждого languageуникального значения поля, вы можете сделать это:

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

1

Если вы хотите , чтобы получить все уникальные значения без какого - либо приближения или установив магическое число ( 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);

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.