terms

Поиск документов, содержащих один или несколько указанных терминов в заданном поле

Terms Query (Поиск по нескольким значениям)

Основное назначение

Запрос terms позволяет искать документы, содержащие одно или несколько указанных значений в заданном поле. Документ возвращается в результатах, если значение его поля точно соответствует хотя бы одному термину из списка.

Базовый синтаксис

GET shakespeare/_search
{
  "query": {
    "terms": {
      "line_id": [
        "61809",
        "61810"
      ]
    }
  }
}

Ключевые особенности

  1. Лимит терминов:
    По умолчанию максимальное количество терминов в запросе - 65,536. Настраивается через параметр index.max_terms_count.

  2. Производительность:
    Для оптимизации производительности с длинными списками терминов рекомендуется:

    • Передавать термины в отсортированном порядке (по возрастанию UTF-8 byte values)
    • Использовать механизм Terms Lookup для больших наборов терминов
  3. Подсветка результатов:
    Возможность подсветки результатов зависит от:

    • Типа highlighter’а
    • Количества терминов в запросе

Параметры запроса

Параметр Тип данных Описание По умолчанию
<field> String Поле для поиска (точное соответствие хотя бы одному термину) -
boost Float Вес поля в расчете релевантности (>1 - увеличивает, 0-1 - уменьшает) 1.0
_name String Имя запроса для тегирования -
value_type String Тип значений для фильтрации (default или bitmap) default

Terms Lookup (Поиск терминов из другого документа)

Общие принципы

Механизм Terms Lookup позволяет:

  1. Извлекать значения полей из указанного документа
  2. Использовать эти значения как условия поиска
  3. Работать с большими наборами терминов

Требования:

  • Поле _source должно быть включено (включено по умолчанию)
  • Для уменьшения сетевого трафика рекомендуется использовать индекс с:
    • Одним первичным шардом
    • Полными репликами на всех узлах данных

Пример использования

  1. Создаем индекс студентов:
PUT students
{
  "mappings": {
    "properties": {
      "student_id": { "type": "keyword" }
    }
  }
}
  1. Добавляем данные студентов:
PUT students/_doc/1
{
  "name": "Jane Doe",
  "student_id" : "111"
}

PUT students/_doc/2
{
  "name": "Mary Major",
  "student_id" : "222"
}

PUT students/_doc/3
{
  "name": "John Doe",
  "student_id" : "333"
}
  1. Создаем индекс классов с информацией о зачисленных студентах:
PUT classes/_doc/101
{
  "name": "CS101",
  "enrolled" : ["111" , "222"]
}
  1. Поиск студентов, зачисленных на CS101:
GET students/_search
{
  "query": {
    "terms": {
      "student_id": {
        "index": "classes",
        "id": "101",
        "path": "enrolled"
      }
    }
  }
}
  1. Результат содержит соответствующих студентов:
{
  "took": 13,
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "hits": [
      {
        "_index": "students",
        "_id": "1",
        "_source": {
          "name": "Jane Doe",
          "student_id": "111"
        }
      },
      {
        "_index": "students",
        "_id": "2",
        "_source": {
          "name": "Mary Major",
          "student_id": "222"
        }
      }
    ]
  }
}

Работа с вложенными полями

  1. Добавляем документ с вложенной структурой:
PUT classes/_doc/102
{
  "name": "CS102",
  "enrolled_students" : {
    "id_list" : ["111" , "333"]
  }
}
  1. Поиск с указанием пути к вложенному полю:
GET students/_search
{
  "query": {
    "terms": {
      "student_id": {
        "index": "classes",
        "id": "102",
        "path": "enrolled_students.id_list"
      }
    }
  }
}
  1. Результат поиска:
{
  "took": 18,
  "hits": {
    "total": {
      "value": 2,
      "relation": "eq"
    },
    "hits": [
      {
        "_index": "students",
        "_id": "1",
        "_source": {
          "name": "Jane Doe",
          "student_id": "111"
        }
      },
      {
        "_index": "students",
        "_id": "3",
        "_source": {
          "name": "John Doe",
          "student_id": "333"
        }
      }
    ]
  }
}

Параметры Terms Lookup

Параметр Тип данных Описание Обязательность
index String Индекс, из которого извлекаются значения Обязательно
id String ID документа-источника Обязательно
path String Путь к полю (для вложенных полей - точечная нотация) Обязательно
routing String Кастомное значение маршрутизации документа Опционально*
store Boolean Использовать stored fields вместо _source Опционально

*Обязателен, если при индексации использовалось кастомное routing

Важные замечания

  1. Для полей типа text используйте match вместо terms
  2. При работе с большими наборами терминов (>10,000) рассмотрите возможность использования Bitmap Filtering
  3. Убедитесь, что поле _source включено для документов-источников

Bitmap Filtering (Фильтрация с использованием битовых карт)

Введение

Начиная с версии 2.17, OpenSearch предлагает механизм bitmap filtering для эффективной фильтрации по большому количеству терминов (10,000+). Этот подход решает проблему высокой нагрузки на сеть и память при работе с обычными terms-запросами.

Основные концепции

  1. Проблема:
    Обычные terms-запросы становятся неэффективными при большом количестве терминов из-за:

    • Высокого сетевого трафика
    • Чрезмерного потребления памяти
  2. Решение:
    Использование roaring bitmap для кодирования условий фильтрации:

    • Эффективное сжатие данных
    • Быстрые битовые операции
    • Оптимизированное использование памяти

Пример реализации

1. Подготовка индексов

Индекс продуктов:

PUT /products
{
  "mappings": {
    "properties": {
      "product_id": { "type": "integer" }
    }
  }
}

Добавление продуктов:

PUT /products/_doc/1
{
  "name": "Product 1",
  "product_id" : 111
}

PUT /products/_doc/2
{
  "name": "Product 2",
  "product_id" : 222
}

PUT /products/_doc/3
{
  "name": "Product 3",
  "product_id" : 333
}

Индекс клиентов с bitmap-полем:

PUT /customers
{
  "mappings": {
    "properties": {
      "customer_filter": {
        "type": "binary",
        "store": true
      }
    }
  }
}

2. Генерация bitmap

Пример кода на Python с использованием PyRoaringBitMap:

from pyroaring import BitMap
import base64

bm = BitMap([111, 222, 333])  # ID продуктов клиента
encoded = base64.b64encode(BitMap.serialize(bm))
encoded_bm_str = encoded.decode('utf-8')

print(f"Encoded Bitmap: {encoded_bm_str}")

3. Индексация bitmap

POST customers/_doc/customer123
{
  "customer_filter": "OjAAAAEAAAAAAAIAEAAAAG8A3gBNAQ==" 
}

Использование bitmap в запросах

Вариант 1: Lookup из документа

POST /products/_search
{
  "query": {
    "terms": {
      "product_id": {
        "index": "customers",
        "id": "customer123",
        "path": "customer_filter",
        "store": true
      },
      "value_type": "bitmap"
    }
  }
}

Вариант 2: Прямая передача bitmap

POST /products/_search
{
  "query": {
    "terms": {
      "product_id": [
        "OjAAAAEAAAAAAAIAEAAAAG8A3gBNAQ=="
      ],
      "value_type": "bitmap"
    }
  }
}

Ключевые параметры

Параметр Тип данных Описание Обязательность
value_type String Должно быть bitmap для активации этого режима Обязательно
store Boolean true для поиска в stored field вместо _source Опционально
index String Индекс-источник bitmap (для lookup) Для lookup
id String ID документа с bitmap Для lookup
path String Поле с bitmap данными Для lookup

Преимущества подхода

  1. Эффективность:

    • Снижение сетевого трафика до 10 раз
    • Уменьшение использования памяти на 50-90%
  2. Гибкость:

    • Поддержка динамического обновления фильтров
    • Возможность комбинирования условий
  3. Производительность:

    • Быстрое выполнение даже для 100,000+ терминов
    • Оптимизированные битовые операции на уровне ядра

Рекомендации

  1. Используйте для сложных фильтров с >10,000 значений
  2. Регулярно обновляйте bitmap при изменении данных
  3. Тестируйте производительность для вашего конкретного сценария
  4. Рассмотрите использование сжатых форматов bitmap для экономии места