Spec-Zone.ru › Elasticsearch 8
›Elasticsearch Guide [8.17] ›Агрегации ›Агрегации по корзинам

Агрегация редких терминов

Агрегация, основанная на нескольких значениях, которая находит «редкие» термины — термины, которые находятся в длинном хвосте распределения и не являются частыми. По сути, это похоже на агрегацию terms, которая сортируется по _count по возрастанию. Как указано в документации по агрегации terms, фактическая сортировка агрегации terms по количеству по возрастанию имеет неограниченную ошибку. Вместо этого следует использовать агрегацию rare_terms

Синтаксис

Агрегация rare_terms в изоляции выглядит так:

{
  "rare_terms": {
    "field": "the_field",
    "max_doc_count": 1
  }
}

Таблица 52. Параметры rare_terms

Название параметра

Описание

Обязательно

Значение по умолчанию

field

Поле, в котором мы хотим найти редкие термины

Обязательно

max_doc_count

Максимальное количество документов, в которых должен появляться термин.

Необязательно

1

precision

Точность внутренних CuckooFilters. Более низкая точность приводит к лучшему приближению, но большему использованию памяти. Не может быть меньше, чем 0.00001

Необязательно

0.001

include

Термины, которые должны быть включены в агрегацию

Необязательно

exclude

Термины, которые должны быть исключены из агрегации

Необязательно

missing

Значение, которое должно использоваться, если у документа отсутствует поле, агрегируемое в данную агрегацию

Необязательно

Пример:

resp = client.search(
    aggs={
        "genres": {
            "rare_terms": {
                "field": "genre"
            }
        }
    },
)
print(resp)
response = client.search(
  body: {
    aggregations: {
      genres: {
        rare_terms: {
          field: 'genre'
        }
      }
    }
  }
)
puts response
const response = await client.search({
  aggs: {
    genres: {
      rare_terms: {
        field: "genre",
      },
    },
  },
});
console.log(response);
GET /_search
{
  "aggs": {
    "genres": {
      "rare_terms": {
        "field": "genre"
      }
    }
  }
}

Ответ:

{
  ...
  "aggregations": {
    "genres": {
      "buckets": [
        {
          "key": "swing",
          "doc_count": 1
        }
      ]
    }
  }
}

В этом примере единственным отображаемым бакетом является бакет "swing", так как это единственный термин, который появляется в одном документе. Если увеличить max_doc_count до 2, мы увидим несколько дополнительных бакетов:

resp = client.search(
    aggs={
        "genres": {
            "rare_terms": {
                "field": "genre",
                "max_doc_count": 2
            }
        }
    },
)
print(resp)
response = client.search(
  body: {
    aggregations: {
      genres: {
        rare_terms: {
          field: 'genre',
          max_doc_count: 2
        }
      }
    }
  }
)
puts response
const response = await client.search({
  aggs: {
    genres: {
      rare_terms: {
        field: "genre",
        max_doc_count: 2,
      },
    },
  },
});
console.log(response);
GET /_search
{
  "aggs": {
    "genres": {
      "rare_terms": {
        "field": "genre",
        "max_doc_count": 2
      }
    }
  }
}

Теперь отображается термин "jazz", который имеет doc_count значение 2:

{
  ...
  "aggregations": {
    "genres": {
      "buckets": [
        {
          "key": "swing",
          "doc_count": 1
        },
        {
          "key": "jazz",
          "doc_count": 2
        }
      ]
    }
  }
}

Максимальное количество документов

Параметр max_doc_count используется для управления верхней границей количества документов, которое может иметь термин. Нет ограничений по размеру на агрегацию rare_terms, как у агрегации terms. Это означает, что термины, которые соответствуют критериям max_doc_count, будут возвращены. Агрегация работает таким образом, чтобы избежать проблем с сортировкой по возрастанию, которые затрагивают агрегацию terms.

Однако это означает, что может быть возвращено большое количество результатов при неправильном выборе параметров. Для ограничения опасности этой настройки максимальное значение max_doc_count составляет 100.

Предельное значение бакетов

Агрегация редких терминов более склонна к срабатыванию мягкого предела search.max_buckets, чем другие агрегации, из-за своего принципа работы. Мягкий предел max_bucket оценивается на основе каждого фрагмента при сборе результатов агрегации. Возможно, что термин будет «редким» на фрагменте, но станет «не редким», после объединения результатов всех фрагментов. Это означает, что отдельные фрагменты склонны собирать больше бакетов, чем действительно редких, потому что у них есть только свой локальный обзор. Этот список в конечном итоге будет обрезан до правильного, меньшего списка редких терминов на узле координации... но фрагмент может уже сработать с мягким пределом max_buckets и прервать запрос.

При агрегации по полям, которые могут содержать много «редких» терминов, может потребоваться увеличить мягкий предел max_buckets. В качестве альтернативы, может потребоваться найти способ отфильтровать результаты, чтобы вернуть меньше редких значений (меньший временной интервал, фильтрация по категории и т. д.), или переоценить определение «редких» (например, если что-то появляется 100 000 раз, это действительно «редко»?)

Числа документов являются приближенными

Примитивный способ определения «редких» терминов в наборе данных заключается в размещении всех значений в карте, увеличивая счетчики при посещении каждого документа, а затем возвращении нижних n строк. Это не масштабируется за пределы даже умеренно больших наборов данных. Разделенный подход, где сохраняются только «верхние n» значения от каждого фрагмента (аналогично агрегации terms), терпит неудачу, потому что длинный хвост проблемы означает, что невозможно найти «верхние n» нижних значений без простого сбора всех значений со всех фрагментов.

Вместо этого агрегация Rare Terms использует другой приближенный алгоритм:

  1. Значения помещаются в карту при первом их появлении.
  2. Каждое последующее появление термина увеличивает счетчик в карте.
  3. Если счетчик > порога max_doc_count, термин удаляется из карты и помещается в CuckooFilter.
  4. CuckooFilter используется для проверки каждого термина. Если значение содержится в фильтре, оно известно как превышающее порог и пропускается.

После выполнения карта значений представляет собой карту «редких» терминов, которые не превышают порог max_doc_count. Затем эта карта и CuckooFilter объединяются со всеми другими фрагментами. Если есть термины, которые превышают порог (или появляются в CuckooFilter другого фрагмента), термин удаляется из объединенного списка. Конечная карта значений возвращается пользователю как «редкие» термины.

CuckooFilters могут возвращать ложноположительные результаты (они могут сказать, что значение существует в их коллекции, когда это на самом деле не так). Поскольку CuckooFilter используется для проверки, превышает ли термин порог, это означает, что ложноположительный результат CuckooFilter ошибочно укажет, что значение часто встречается, когда оно не так (и, таким образом, исключит его из конечного списка бакетов). Практически это означает, что агрегация демонстрирует ложноотрицательное поведение, так как фильтр используется «в обратном» порядке по сравнению с тем, как люди обычно думают о приближенных схемах членства в наборе.

CuckooFilters подробно описаны в статье:

Fan, Bin, et al. «Cuckoo filter: Практически лучше, чем Bloom». Труды 10-й международной конференции ACM по экспериментам и технологиям в области новых сетей. ACM, 2014.

Точность

Хотя внутренний CuckooFilter приблизительный по своей природе, частоту ложноотрицательных результатов можно контролировать с помощью параметра precision. Это позволяет пользователю обменять больше оперативной памяти на более точные результаты.

По умолчанию точность равна 0.001, а наименьшая (т. е. самая точная и большая потребность в памяти) равна 0.00001. Ниже приведены некоторые графики, демонстрирующие, как точность агрегации зависит от точности и количества различных терминов.

Ось X показывает количество различных значений, которые видела агрегация, а ось Y показывает процентную ошибку. Каждая линия ряда представляет одно состояние «редкости» (от одного редкого элемента до 100 000 редких элементов). Например, оранжевая линия «10» означает, что десять значений были «редкими» (doc_count == 1) из 1–20 млн различных значений (где остальные значения имели doc_count > 1)

Этот первый график показывает точность 0.01:

accuracy 01

И точность 0.001 (значение по умолчанию):

accuracy 001

И, наконец, precision 0.0001:

accuracy 0001

По умолчанию точность 0.001 поддерживает точность < 2,5 % для протестированных условий, и точность медленно и контролируемо уменьшается линейно по мере увеличения количества различных значений.

По умолчанию точность 0.001 имеет профиль использования памяти 1.748⁻⁶ * n байт, где n — это количество различных значений, которые видела агрегация (его также можно приблизительно оценить, например, 20 миллионов уникальных значений примерно соответствует 30 МБ памяти). Использование памяти линейно зависит от количества различных значений независимо от выбранной точности, точность только влияет на наклон профиля использования памяти, как показано на этом графике:

memory

Для сравнения, эквивалентная агрегация terms с 20 миллионами бакетов будет примерно 20m * 69b == ~1.38gb (при условии, что 69 байт — очень оптимистичная оценка стоимости пустого бакета, намного меньше, чем учтено в блоке защиты от перегрузки). Таким образом, хотя агрегация rare_terms относительно ресурсоемкая, она все же на несколько порядков меньше, чем эквивалентная агрегация terms.

Фильтрация значений

Можно отфильтровать значения, для которых будут созданы корзины. Это можно сделать с помощью параметров include и exclude, которые основаны на строках регулярных выражений или массивах точных значений. Кроме того, include-предложения могут фильтровать с помощью partition-выражений.

Фильтрация значений с помощью регулярных выражений

resp = client.search(
    aggs={
        "genres": {
            "rare_terms": {
                "field": "genre",
                "include": "swi*",
                "exclude": "electro*"
            }
        }
    },
)
print(resp)
response = client.search(
  body: {
    aggregations: {
      genres: {
        rare_terms: {
          field: 'genre',
          include: 'swi*',
          exclude: 'electro*'
        }
      }
    }
  }
)
puts response
const response = await client.search({
  aggs: {
    genres: {
      rare_terms: {
        field: "genre",
        include: "swi*",
        exclude: "electro*",
      },
    },
  },
});
console.log(response);
GET /_search
{
  "aggs": {
    "genres": {
      "rare_terms": {
        "field": "genre",
        "include": "swi*",
        "exclude": "electro*"
      }
    }
  }
}

В приведенном выше примере корзины будут созданы для всех тегов, начинающихся с swi, за исключением тех, которые начинаются с electro (так что тег swing будет агрегирован, но не electro_swing). Регулярное выражение include определит, какие значения «разрешены» для агрегирования, а exclude — значения, которые не должны агрегироваться. Когда оба определены, у exclude имеет приоритет, то есть include оценивается сначала, а только затем exclude.

Синтаксис аналогичен запросам регулярных выражений.

Фильтрация значений с точными значениями

Для сопоставления на основе точных значений параметры include и exclude могут просто принимать массив строк, представляющих термины, как они встречаются в индексе:

resp = client.search(
    aggs={
        "genres": {
            "rare_terms": {
                "field": "genre",
                "include": [
                    "swing",
                    "rock"
                ],
                "exclude": [
                    "jazz"
                ]
            }
        }
    },
)
print(resp)
response = client.search(
  body: {
    aggregations: {
      genres: {
        rare_terms: {
          field: 'genre',
          include: [
            'swing',
            'rock'
          ],
          exclude: [
            'jazz'
          ]
        }
      }
    }
  }
)
puts response
const response = await client.search({
  aggs: {
    genres: {
      rare_terms: {
        field: "genre",
        include: ["swing", "rock"],
        exclude: ["jazz"],
      },
    },
  },
});
console.log(response);
GET /_search
{
  "aggs": {
    "genres": {
      "rare_terms": {
        "field": "genre",
        "include": [ "swing", "rock" ],
        "exclude": [ "jazz" ]
      }
    }
  }
}

Пропущенное значение

Параметр missing определяет, как должны обрабатываться документы, в которых отсутствует значение. По умолчанию они будут игнорироваться, но также можно рассматривать их так, как если бы у них было значение.

resp = client.search(
    aggs={
        "genres": {
            "rare_terms": {
                "field": "genre",
                "missing": "N/A"
            }
        }
    },
)
print(resp)
response = client.search(
  body: {
    aggregations: {
      genres: {
        rare_terms: {
          field: 'genre',
          missing: 'N/A'
        }
      }
    }
  }
)
puts response
const response = await client.search({
  aggs: {
    genres: {
      rare_terms: {
        field: "genre",
        missing: "N/A",
      },
    },
  },
});
console.log(response);
GET /_search
{
  "aggs": {
    "genres": {
      "rare_terms": {
        "field": "genre",
        "missing": "N/A" 
      }
    }
  }
}

Документы без значения в поле tags попадут в ту же корзину, что и документы, имеющие значение N/A.

Вложенные, RareTerms и под-агрегации с оценкой

Агрегация RareTerms должна работать в режиме breadth_first, так как ей необходимо обрезать термины по мере превышения порогов количества документов. Это требование означает, что агрегация RareTerms несовместима с определенными комбинациями агрегаций, которые требуют depth_first. В частности, под-агрегации с оценкой, которые находятся внутри nested, вынуждают всю древовидную структуру агрегации работать в режиме depth_first. Это вызовет исключение, так как RareTerms не может обработать depth_first.

Как конкретный пример, если агрегация rare_terms является дочерней для агрегации nested, и одна из дочерних агрегаций rare_terms требует оценок документов (например, агрегация top_hits), это вызовет исключение.

© 2023-2025 Elasticsearch
As of September 2024, Elasticsearch is available under a choice of three licenses: the Server Side Public License (SSPL), the Elastic License, or the AGPLv3 (OSI approved).
Elasticsearch and the Elasticsearch logo are trademarks of Elasticsearch B.V., registered in the U.S. and in other countries.
https://www.elastic.co/guide/en/elasticsearch/reference/8.17/search-aggregations-bucket-rare-terms-aggregation.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API