Настройка индексов и маппингов Elasticsearch
Маппинг в Elasticsearch — это схема данных индекса: типы полей, параметры индексации, анализаторы. В отличие от реляционных баз, Elasticsearch поддерживает динамический маппинг — автоматическое определение типов. Но в продакшене полагаться на него нельзя: динамический маппинг приводит к неожиданным типам полей, раздуванию индекса и невозможности изменить схему без переиндексации.
Типы полей и когда их использовать
text — полнотекстовый поиск. Поле анализируется (токенизируется, нормализуется). Нельзя использовать для сортировки и агрегаций без keyword sub-field.
keyword — точное значение. Не анализируется. Для фильтров, сортировки, агрегаций, ID, статусов, тегов.
integer, long, float, double — числа. Для диапазонных запросов, сортировки, агрегаций.
date — дата/время. Поддерживает форматы ISO 8601 и unix timestamp.
boolean — булев тип.
object — вложенный JSON-объект. Все поля объекта «плоско» сохраняются в документ. Если объект массив — запросы по нескольким полям объекта работают некорректно.
nested — массив объектов, где нужны корректные запросы по связанным полям внутри объекта. Каждый вложенный объект индексируется как отдельный скрытый документ. Дороже по памяти и скорости, чем object.
geo_point — координаты (latitude, longitude). Для гео-поиска и агрегаций.
dense_vector — вектор фиксированной размерности. Для семантического поиска (kNN).
Создание индекса с явным маппингом
PUT /products
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1,
"analysis": {
"analyzer": {
"product_search": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "stop", "snowball"]
}
}
}
},
"mappings": {
"dynamic": "strict",
"_source": {
"enabled": true
},
"properties": {
"id": {
"type": "keyword"
},
"title": {
"type": "text",
"analyzer": "product_search",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"description": {
"type": "text",
"analyzer": "product_search",
"index_options": "positions"
},
"category": {
"type": "keyword"
},
"tags": {
"type": "keyword"
},
"price": {
"type": "scaled_float",
"scaling_factor": 100
},
"stock": {
"type": "integer"
},
"is_active": {
"type": "boolean"
},
"created_at": {
"type": "date",
"format": "strict_date_optional_time||epoch_millis"
},
"attributes": {
"type": "nested",
"properties": {
"name": { "type": "keyword" },
"value": { "type": "keyword" }
}
},
"location": {
"type": "geo_point"
}
}
}
}
"dynamic": "strict" — запрещает добавление неописанных полей. Документ с неизвестным полем вызовет ошибку маппинга. Альтернативы: "true" (добавляет автоматически), "false" (игнорирует неизвестные поля, не индексирует).
Index Templates
Для автоматического применения маппинга к новым индексам — index templates. Незаменимо при работе с data streams и rolling-индексами (logs-2025.01.01, logs-2025.01.02).
PUT _index_template/logs-template
{
"index_patterns": ["logs-*"],
"priority": 100,
"template": {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"index.lifecycle.name": "logs-policy",
"index.lifecycle.rollover_alias": "logs"
},
"mappings": {
"dynamic": "false",
"properties": {
"@timestamp": { "type": "date" },
"level": { "type": "keyword" },
"service": { "type": "keyword" },
"message": { "type": "text" },
"trace_id": { "type": "keyword" },
"duration_ms": { "type": "integer" }
}
}
},
"data_stream": {}
}
Изменение маппинга существующего индекса
Большинство изменений маппинга невозможны без переиндексации. Можно только добавлять новые поля или расширять параметры (ignore_above, добавление fields). Нельзя изменить тип существующего поля.
Добавление нового поля:
PUT /products/_mapping
{
"properties": {
"brand": {
"type": "keyword"
}
}
}
Для изменения типа существующего поля — нужна переиндексация: создать новый индекс с правильным маппингом, запустить _reindex, переключить алиас.
Алиасы индексов
Алиасы позволяют абстрагировать приложение от физического имени индекса:
POST _aliases
{
"actions": [
{ "add": { "index": "products_v2", "alias": "products", "is_write_index": true } },
{ "remove": { "index": "products_v1", "alias": "products" } }
]
}
Приложение всегда работает с алиасом products. При переиндексации переключение происходит атомарно — без изменения кода.
_source и оптимизация хранения
_source хранит оригинальный JSON документа. Отключение экономит место, но теряется возможность использовать update, reindex и highlight без оригинала. В большинстве случаев отключать не нужно.
Для экономии места при большом числе полей — использовать store: false для полей, которые точно не нужны в ответе, и включить их только в индекс:
"heavy_description": {
"type": "text",
"store": false,
"_source_excludes": ["heavy_description"]
}
Или через _source.excludes на уровне индекса — исключить тяжёлые поля из _source, но оставить в индексе для поиска.
Проверка маппинга и статистика
# Просмотр маппинга
curl -X GET "localhost:9200/products/_mapping?pretty"
# Статистика индекса (размер, количество документов)
curl -X GET "localhost:9200/products/_stats?pretty"
# Анализ как конкретный анализатор обрабатывает текст
curl -X POST "localhost:9200/products/_analyze?pretty" -H 'Content-Type: application/json' -d'
{
"analyzer": "product_search",
"text": "Running shoes for marathon"
}'
Сроки
Проектирование маппинга для нового индекса с учётом требований поиска — 4–8 часов. Если включает переиндексацию существующих данных и переключение алиаса — ещё 2–4 часа. Для сложных схем с nested объектами и кастомными анализаторами — до 2 рабочих дней.







