OpenSearch -это мощная поисковая система с открытым исходным кодом, форк Elasticsearch. Если вы только начинаете работать с поисковыми системами, эта статья поможет разобраться в основах: как создавать индексы, настраивать структуру данных и использовать бустинг для улучшения релевантности результатов.
Что такое OpenSearch?
OpenSearch -это распределённая поисковая и аналитическая система, которая позволяет:
- Быстро искать по большим объёмам данных
- Индексировать структурированные и неструктурированные данные
- Выполнять сложные запросы с фильтрацией и ранжированием
- Работать с геопространственными данными
- Использовать векторный поиск для семантического поиска
Простыми словами: OpenSearch -это как Google для ваших данных. Вы загружаете документы, а система позволяет быстро находить нужные по запросу.
Основные понятия
Индекс (Index)
Индекс -это коллекция документов, имеющих схожие характеристики. Можно сравнить с таблицей в реляционной БД, но с важными отличиями:
- В БД вы определяете структуру (схему) заранее
- В OpenSearch структура определяется через маппинг (mapping), но может быть динамической
- Индекс может содержать миллионы документов и быстро находить нужные
Пример: индекс locations содержит информацию о локациях для бизнеса, индекс products -о товарах.
Документ (Document)
Документ -это базовая единица информации в OpenSearch. Это JSON-объект, который содержит данные.
{
"id": "loc_1",
"name": "Кафе на Тверской",
"address": "ул. Тверская, д. 10, Москва",
"traffic_score": 8.5,
"coordinates": {
"lat": 55.7558,
"lon": 37.6173
}
}
Маппинг (Mapping)
Маппинг -это определение структуры данных в индексе. Он описывает:
- Какие поля есть в документе
- Какой тип данных у каждого поля (текст, число, дата, геокоординаты)
- Как индексировать и искать по полям
Маппинг похож на схему таблицы в БД, но более гибкий.
Типы данных в OpenSearch
OpenSearch поддерживает множество типов данных. Рассмотрим основные:
1. Text (текст)
Используется для полнотекстового поиска. Текст анализируется и разбивается на слова (токены).
{
"name": {
"type": "text"
}
}
Особенности:
- Текст разбивается на слова при индексации
- Поддерживает поиск по части слова, синонимам
- Можно настроить анализатор (как разбивать текст)
Пример использования: названия, описания, комментарии.
2. Keyword (ключевое слово)
Используется для точного совпадения. Значение хранится как есть, без анализа.
{
"status": {
"type": "keyword"
}
}
Особенности:
- Идеально для фильтрации и сортировки
- Быстрый поиск по точному совпадению
- Подходит для enum-значений (статусы, категории)
Пример использования: статусы заказов, категории, коды.
3. Number (числа)
Поддерживает разные числовые типы: long, integer, short, byte, double, float.
{
"price": {
"type": "double"
},
"quantity": {
"type": "integer"
}
}
Особенности:
- Используется для диапазонных запросов (больше, меньше)
- Подходит для сортировки и агрегаций
- Точность зависит от типа
4. Date (дата)
Хранит дату и время в формате ISO 8601.
{
"created_at": {
"type": "date"
}
}
Особенности:
- Можно искать по диапазонам дат
- Поддерживает разные форматы
- Используется для временных фильтров
5. Boolean (логический)
Хранит true или false.
{
"is_active": {
"type": "boolean"
}
}
6. Geo Point (геокоординаты)
Хранит географические координаты (широта, долгота).
{
"location": {
"type": "geo_point"
}
}
Особенности:
- Позволяет искать объекты в радиусе от точки
- Вычислять расстояние между точками
- Сортировать по расстоянию
Пример использования: поиск локаций рядом с пользователем.
7. Object (объект)
Вложенный JSON-объект.
{
"address": {
"type": "object",
"properties": {
"street": {"type": "text"},
"city": {"type": "keyword"},
"zip": {"type": "keyword"}
}
}
}
8. Array (массив)
Массив значений одного типа.
{
"tags": {
"type": "keyword"
}
}
В JSON это будет выглядеть так:
{
"tags": ["cafe", "restaurant", "coffee"]
}
9. Dense Vector (вектор)
Используется для векторного поиска (kNN, семантический поиск).
{
"embedding": {
"type": "dense_vector",
"dims": 128
}
}
Особенности:
- Хранит числовой вектор фиксированной размерности
- Используется для поиска похожих документов
- Требует специальных запросов (kNN)
Создание индекса и маппинга
Шаг 1: Создание индекса с маппингом
Создадим индекс для локаций бизнеса:
PUT /locations
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"address": {
"type": "text"
},
"region": {
"type": "keyword"
},
"city": {
"type": "keyword"
},
"coordinates": {
"type": "geo_point"
},
"traffic_score": {
"type": "float"
},
"competition_density": {
"type": "float"
},
"business_types_suitable": {
"type": "keyword"
},
"demographics": {
"type": "object",
"properties": {
"age_group": {"type": "keyword"},
"average_income": {"type": "integer"},
"population_density": {"type": "integer"}
}
},
"embedding": {
"type": "dense_vector",
"dims": 128
}
}
}
}
Разбор маппинга
Settings (настройки индекса):
number_of_shards-количество шардов (частей индекса). Влияет на производительность и масштабируемостьnumber_of_replicas-количество реплик. Обеспечивает отказоустойчивость
Mappings (структура данных):
properties-описание полей документа
Multi-fields (множественные поля):
Обратите внимание на поле name:
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
Это позволяет:
- Искать по
nameкак по тексту (полнотекстовый поиск) - Использовать
name.keywordдля точного совпадения и сортировки
Шаг 2: Добавление документа
POST /locations/_doc
{
"id": "loc_1",
"name": "Кафе на Тверской",
"address": "ул. Тверская, д. 10, Москва",
"region": "Москва",
"city": "Москва",
"coordinates": {
"lat": 55.7558,
"lon": 37.6173
},
"traffic_score": 8.5,
"competition_density": 2.3,
"business_types_suitable": ["cafe", "restaurant"],
"demographics": {
"age_group": "26-35",
"average_income": 75000,
"population_density": 5000
},
"embedding": [0.1, 0.2, 0.3, ...] // 128 чисел
}
Поиск и запросы
Простой поиск
GET /locations/_search
{
"query": {
"match": {
"name": "кафе"
}
}
}
Поиск с фильтрацией
GET /locations/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "кафе"
}
}
],
"filter": [
{
"term": {
"city": "Москва"
}
},
{
"range": {
"traffic_score": {
"gte": 7.0
}
}
}
]
}
}
}
Разбор запроса:
must-документ должен соответствовать (влияет на релевантность)filter-документ должен соответствовать (не влияет на релевантность, но быстрее)
Геопространственный поиск
GET /locations/_search
{
"query": {
"geo_distance": {
"distance": "5km",
"coordinates": {
"lat": 55.7558,
"lon": 37.6173
}
}
},
"sort": [
{
"_geo_distance": {
"coordinates": {
"lat": 55.7558,
"lon": 37.6173
},
"order": "asc",
"unit": "km"
}
}
]
}
Этот запрос найдёт все локации в радиусе 5 км от указанной точки и отсортирует их по расстоянию.
Бустинг (Boosting)
Бустинг -это механизм увеличения релевантности документов, которые соответствуют определённым критериям. Это позволяет “поднять” более важные результаты в выдаче.
Простой бустинг
Увеличим релевантность локаций с высоким traffic_score:
GET /locations/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"name": "кафе"
}
}
],
"should": [
{
"range": {
"traffic_score": {
"gte": 7.0,
"boost": 2.0
}
}
}
]
}
}
}
Как это работает:
should-документ может соответствовать (необязательно)boost: 2.0-если документ соответствует, его релевантность умножается на 2
Функциональный бустинг (Function Score)
Более гибкий способ -использовать function_score:
GET /locations/_search
{
"query": {
"function_score": {
"query": {
"bool": {
"must": [
{
"match": {
"name": "кафе"
}
}
],
"filter": [
{
"term": {
"city": "Москва"
}
}
]
}
},
"functions": [
{
"filter": {
"range": {
"traffic_score": {
"gte": 7.0
}
}
},
"weight": 2.0
},
{
"filter": {
"range": {
"competition_density": {
"lte": 3.0
}
}
},
"weight": 1.5
}
],
"score_mode": "sum",
"boost_mode": "multiply"
}
}
}
Разбор:
functions-список функций бустингаfilter-условие для применения бустингаweight-вес бустингаscore_mode: "sum"-как объединять результаты функций (sum, multiply, avg, max, min)boost_mode: "multiply"-как применять к исходному score (multiply, replace, sum, avg, max, min)
Практический пример: комбинированный бустинг
В рекомендательной системе для бизнеса мы используем комбинированный бустинг:
GET /locations/_search
{
"query": {
"function_score": {
"query": {
"bool": {
"must": [
{
"match": {
"name": "кафе"
}
}
],
"filter": [
{
"term": {
"city": "Москва"
}
},
{
"term": {
"business_types_suitable": "cafe"
}
}
]
}
},
"functions": [
{
"filter": {
"range": {
"traffic_score": {
"gte": 7.0
}
}
},
"weight": 2.0,
"description": "Бустинг для локаций с высоким трафиком"
},
{
"filter": {
"range": {
"competition_density": {
"lte": 3.0
}
}
},
"weight": 1.5,
"description": "Бустинг для локаций с низкой конкуренцией"
}
],
"score_mode": "sum",
"boost_mode": "multiply"
}
},
"sort": [
{
"_score": {
"order": "desc"
}
},
{
"traffic_score": {
"order": "desc"
}
},
{
"competition_density": {
"order": "asc"
}
}
]
}
Логика:
- Находим все кафе в Москве
- Увеличиваем релевантность тех, у кого traffic_score >= 7.0 (в 2 раза)
- Увеличиваем релевантность тех, у кого competition_density <= 3.0 (в 1.5 раза)
- Сортируем по релевантности, затем по traffic_score (убывание), затем по competition_density (возрастание)
Анализаторы (Analyzers)
Анализатор определяет, как текст разбивается на слова при индексации и поиске.
Стандартный анализатор
По умолчанию используется стандартный анализатор, который:
- Разбивает текст по пробелам и знакам препинания
- Приводит к нижнему регистру
- Удаляет стоп-слова (опционально)
Кастомный анализатор
Можно создать свой анализатор:
PUT /locations
{
"settings": {
"analysis": {
"analyzer": {
"russian_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"russian_stop",
"russian_stemmer"
]
}
}
}
},
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "russian_analyzer"
}
}
}
}
Компоненты анализатора:
tokenizer-разбивает текст на токены (слова)filter-обрабатывает токены (нижний регистр, стемминг, синонимы)
Практические советы
1. Выбор между text и keyword
- Используйте
textдля поиска по содержимому (названия, описания) - Используйте
keywordдля точного совпадения и фильтрации (статусы, коды, категории) - Используйте multi-fields, если нужно и то, и другое
2. Оптимизация производительности
- Используйте
filterвместоmustдля условий, которые не влияют на релевантность - Фильтры кэшируются и работают быстрее
- Ограничивайте количество возвращаемых документов (
size)
3. Бустинг
- Не переусердствуйте с бустингом -слишком высокие значения могут исказить результаты
- Тестируйте разные значения весов
- Используйте
function_scoreдля сложной логики
4. Геопространственные запросы
- Используйте
geo_pointдля координат - Кэшируйте результаты для популярных локаций
- Учитывайте, что геозапросы могут быть ресурсоёмкими
Заключение
OpenSearch -мощный инструмент для поиска и анализа данных. Основные моменты:
- Индекс -коллекция документов
- Маппинг -структура данных (типы полей)
- Документ -JSON-объект с данными
- Бустинг -увеличение релевантности важных результатов
- Анализаторы -обработка текста при индексации
При работе обычно начинаем с простых запросов, постепенно усложняя их. Если вы тоже работаете с OpenSearch, экспериментируйте с бустингом и анализируйте результаты. OpenSearch -это инструмент, который требует практики, но результаты того стоят.
Полезные ссылки: