Elasticsearch стал стандартом де-факто для быстрого полнотекстового поиска в 2026 году. Я внедрял его на десятках проектов — от небольших интернет-магазинов до крупных корпоративных порталов, и результаты всегда впечатляющие: поиск работает за 10-50мс против 1-3 секунд на обычных SQL-запросах.
Зачем нужен Elasticsearch и когда пора его внедрять
Честно говоря, стандартный поиск по базе через LIKE в MySQL начинает «задыхаться» уже на 10-20 тысячах записей. У меня был клиент с каталогом на 50 тысяч товаров — поиск по названию занимал 2-4 секунды, а по описанию вообще до 8 секунд. Пользователи просто уходили со страницы.
После внедрения Elasticsearch 8.11 тот же поиск стал работать за 15-30 миллисекунд. И это не просто скорость — появились возможности нечёткого поиска (опечатки исправляются автоматически), поиск по синонимам, автодополнение в реальном времени.
Elasticsearch критически важен когда:
- В базе больше 10 тысяч записей для поиска
- Нужен поиск по нескольким полям одновременно
- Требуется фасетный поиск (фильтры по категориям, ценам, брендам)
- Важна скорость отклика — менее 100мс
- Нужна аналитика по поисковым запросам
На практике я рекомендую внедрять Elasticsearch на всех проектах с каталогами от 5000+ товаров. Да, это усложняет архитектуру, но пользовательский опыт становится на порядок лучше.
Установка и базовая настройка Elasticsearch 8.x
Я всегда использую официальные репозитории Elastic — они самые стабильные. На Ubuntu 22.04 процесс выглядит так:
# Добавляем GPG ключ
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg
# Добавляем репозиторий
echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list
# Устанавливаем
sudo apt update && sudo apt install elasticsearch
# Запускаем и добавляем в автозагрузку
sudo systemctl enable elasticsearch
sudo systemctl start elasticsearch
После установки нужно обязательно настроить конфигурацию в `/etc/elasticsearch/elasticsearch.yml`. Вот мой стандартный конфиг для продакшена:
# Базовые настройки кластера
cluster.name: production-search
node.name: search-node-1
# Пути к данным и логам
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch
# Сетевые настройки
network.host: 127.0.0.1
http.port: 9200
# Безопасность
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
# Память (не больше 50% от RAM)
bootstrap.memory_lock: true
# Настройки индексов по умолчанию
action.auto_create_index: false
Критически важно настроить память правильно. В файле `/etc/elasticsearch/jvm.options` я всегда указываю heap size:
# Для сервера с 8GB RAM
-Xms2g
-Xmx2g
# Для сервера с 16GB RAM
-Xms4g
-Xmx4g
Правило простое: heap не должен превышать 50% от доступной RAM, и никогда не больше 32GB (из-за особенностей JVM).
Создание индекса и настройка маппинга
Маппинг в Elasticsearch — это схема данных, которая определяет как именно индексировать поля. Я всегда создаю маппинг вручную, а не полагаюсь на автоматическое определение типов.
Вот пример маппинга для каталога товаров, который я использую в большинстве проектов:
PUT /products
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0,
"analysis": {
"analyzer": {
"russian_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"russian_morphology",
"english_morphology",
"russian_stop"
]
}
}
}
},
"mappings": {
"properties": {
"id": {
"type": "integer"
},
"title": {
"type": "text",
"analyzer": "russian_analyzer",
"fields": {
"keyword": {
"type": "keyword"
}
}
},
"description": {
"type": "text",
"analyzer": "russian_analyzer"
},
"price": {
"type": "float"
},
"category_id": {
"type": "integer"
},
"category_name": {
"type": "keyword"
},
"brand": {
"type": "keyword"
},
"in_stock": {
"type": "boolean"
},
"created_at": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"tags": {
"type": "keyword"
}
}
}
}
Здесь несколько важных моментов из моей практики:
Анализатор для русского языка — обязательно нужен плагин `analysis-morphology`. Без него поиск по словоформам работать не будет. Устанавливается командой:
sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install analysis-morphology
Поля типа text vs keyword — text индексируется для полнотекстового поиска, keyword для точного совпадения. Для названий товаров я делаю оба типа через multi-field.
Количество шардов — для большинства проектов достаточно 1 шарда. Больше имеет смысл только при объёме данных 50GB+ или при распределённой установке.
У меня был проект с миллионом товаров — там я использовал 3 шарда и производительность была отличная. Но на проектах до 100 тысяч записей множественные шарды только замедляют работу.
Индексирование данных из базы
Самая частая задача — перенести данные из MySQL в Elasticsearch. Я делаю это через PHP-скрипты с использованием официального клиента. Вот рабочий пример:
setHosts(['localhost:9200'])
->setBasicAuthentication('elastic', 'your_password')
->build();
// Подключение к MySQL
$pdo = new PDO('mysql:host=localhost;dbname=shop;charset=utf8mb4', $user, $pass);
// Получаем данные порциями по 1000 записей
$limit = 1000;
$offset = 0;
while (true) {
$stmt = $pdo->prepare("
SELECT
p.id,
p.title,
p.description,
p.price,
p.category_id,
c.name as category_name,
p.brand,
p.in_stock,
p.created_at
FROM products p
LEFT JOIN categories c ON p.category_id = c.id
WHERE p.active = 1
LIMIT :limit OFFSET :offset
");
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
$stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
$products = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (empty($products)) {
break;
}
// Формируем bulk-запрос
$bulkParams = ['body' => []];
foreach ($products as $product) {
// Индекс документа
$bulkParams['body'][] = [
'index' => [
'_index' => 'products',
'_id' => $product['id']
]
];
// Данные документа
$bulkParams['body'][] = [
'id' => (int)$product['id'],
'title' => $product['title'],
'description' => $product['description'],
'price' => (float)$product['price'],
'category_id' => (int)$product['category_id'],
'category_name' => $product['category_name'],
'brand' => $product['brand'],
'in_stock' => (bool)$product['in_stock'],
'created_at' => $product['created_at']
];
}
// Отправляем данные
try {
$response = $client->bulk($bulkParams);
if ($response['errors']) {
echo "Ошибки при индексации:\n";
foreach ($response['items'] as $item) {
if (isset($item['index']['error'])) {
print_r($item['index']['error']);
}
}
} else {
echo "Проиндексировано " . count($products) . " товаров\n";
}
} catch (Exception $e) {
echo "Ошибка: " . $e->getMessage() . "\n";
break;
}
$offset += $limit;
// Небольшая пауза чтобы не нагружать сервер
usleep(100000); // 0.1 секунды
}
echo "Индексация завершена\n";
На практике я всегда использую bulk API — он в разы быстрее одиночных запросов. При индексации 100 тысяч товаров разница составляет 5 минут против часа.
Для автоматического обновления индекса при изменении данных в MySQL я настраиваю триггеры или использую очереди. Вот пример через Redis Queue:
prepare("UPDATE products SET title = ?, price = ? WHERE id = ?");
$stmt->execute([$data['title'], $data['price'], $productId]);
// Добавляем задачу в очередь на обновление в Elasticsearch
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->lPush('elasticsearch_updates', json_encode([
'action' => 'update',
'index' => 'products',
'id' => $productId,
'data' => $data
]));
}
Настройка поисковых запросов
Правильно настроенный поисковый запрос — это 80% успеха всей системы. Я использую несколько типов запросов в зависимости от задач.
Простой поиск по одному полю:
GET /products/_search
{
"query": {
"match": {
"title": {
"query": "телефон samsung",
"operator": "and"
}
}
}
}
Мультиполевый поиск с разными весами:
GET /products/_search
{
"query": {
"multi_match": {
"query": "телефон samsung",
"fields": [
"title^3",
"description^1",
"brand^2"
],
"type": "best_fields",
"operator": "and"
}
}
}
Здесь title имеет вес 3, brand — вес 2, а description — вес 1. Это значит, что совпадения в названии будут ранжироваться выше.
Продвинутый поиск с фильтрами и сортировкой:
GET /products/_search
{
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "телефон",
"fields": ["title^3", "description"]
}
}
],
"filter": [
{
"range": {
"price": {
"gte": 10000,
"lte": 50000
}
}
},
{
"term": {
"in_stock": true
}
},
{
"terms": {
"brand": ["Samsung", "Apple", "Xiaomi"]
}
}
]
}
},
"sort": [
{"_score": {"order": "desc"}},
{"price": {"order": "asc"}}
],
"from": 0,
"size": 20
}
В разделе `must` указываются условия, которые влияют на релевантность. В `filter` — условия, которые просто фильтруют результаты без влияния на score.
Для автодополнения я использую completion suggester. Сначала нужно добавить поле в маппинг:
"suggest": {
"type": "completion",
"analyzer": "russian_analyzer"
}
А при индексации заполнять его вариантами:
{
"title": "Samsung Galaxy S24",
"suggest": [
"Samsung Galaxy S24",
"Galaxy S24",
"Samsung S24",
"S24"
]
}
Тогда автодополнение работает молниеносно:
GET /products/_search
{
"suggest": {
"product_suggest": {
"prefix": "sam",
"completion": {
"field": "suggest",
"size": 10
}
}
}
}
Оптимизация производительности поиска
На одном из проектов у меня поиск стал работать медленно после достижения 500 тысяч товаров. Пришлось серьёзно оптимизировать — в итоге скорость выросла в 3 раза.
Настройки индекса для производительности:
PUT /products/_settings
{
"index": {
"refresh_interval": "30s",
"number_of_replicas": 0,
"translog.durability": "async",
"translog.sync_interval": "5s"
}
}
`refresh_interval` по умолчанию 1 секунда — это означает, что новые документы становятся доступными для поиска каждую секунду. Для большинства сайтов достаточно 30 секунд.
Кеширование на уровне приложения:
$query,
'filters' => $filters,
'page' => $page,
'size' => $size
]));
// Проверяем кеш
$cached = $this->redis->get($cacheKey);
if ($cached) {
return json_decode($cached, true);
}
// Выполняем поиск
$searchParams = [
'index' => 'products',
'body' => $this->buildQuery($query, $filters, $page, $size)
];
$response = $this->client->search($searchParams);
// Кешируем на 5 минут
$this->redis->setex($cacheKey, 300, json_encode($response));
return $response;
}
private function buildQuery($query, $filters, $page, $size)
{
$from = ($page - 1) * $size;
$body = [
'from' => $from,
'size' => $size,
'query' => [
'bool' => [
'must' => [
[
'multi_match' => [
'query' => $query,
'fields' => ['title^3', 'description', 'brand^2']
]
]
]
]
]
];
// Добавляем фильтры
if (!empty($filters['price_from'])) {
$body['query']['bool']['filter'][] = [
'range' => [
'price' => ['gte' => $filters['price_from']]
]
];
}
if (!empty($filters['category_id'])) {
$body['query']['bool']['filter'][] = [
'term' => [
'category_id' => $filters['category_id']
]
];
}
return $body;
}
}
Кеширование критически важно для популярных запросов. На том проекте топ-100 запросов составляли 80% от общего трафика поиска.
Использование routing для больших индексов:
Если у вас мультитенантное приложение или данные логически разделены (например, по регионам), используйте routing:
'products',
'id' => $productId,
'routing' => $categoryId, // Документы одной категории попадут в один шард
'body' => $document
];
// При поиске
$params = [
'index' => 'products',
'routing' => $categoryId, // Поиск только в нужном шарде
'body' => $query
];
Это может ускорить поиск в разы, особенно при большом количестве шардов.
Настройка безопасности
В Elasticsearch 8.x безопасность включена по умолчанию, но её нужно правильно настроить. Я никогда не оставляю кластер с дефолтными настройками в продакшене.
Создание пользователя для приложения:
# Создаём роль с ограниченными правами
curl -X POST "localhost:9200/_security/role/search_app_role" \
-H 'Content-Type: application/json' \
-u elastic:your_password \
-d '{
"cluster": ["monitor"],
"indices": [
{
"names": ["products", "categories"],
"privileges": ["read", "write", "create_index", "delete_index"]
}
]
}'
# Создаём пользователя
curl -X POST "localhost:9200/_security/user/search_app" \
-H 'Content-Type: application/json' \
-u elastic:your_password \
-d '{
"password": "strong_app_password",
"roles": ["search_app_role"],
"full_name": "Search Application User"
}'
Настройка nginx для проксирования:
Никогда не открывайте Elasticsearch напрямую в интернет. Всегда используйте nginx как прокси:
server {
listen 443 ssl http2;
server_name search.yourdomain.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# Ограничиваем доступ только к поисковым эндпоинтам
location ~ ^/(products|categories)/_search$ {
proxy_pass http://127.0.0.1:9200;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Ограничиваем размер запроса
client_max_body_size 1M;
# Только GET и POST
limit_except GET POST {
deny all;
}
# Базовая аутентификация
auth_basic "Search API";
auth_basic_user_file /etc/nginx/.htpasswd;
}
# Блокируем все остальные запросы
location / {
return 403;
}
}
Настройка файрвола:
# Разрешаем подключения только с локального интерфейса
sudo ufw allow from 127.0.0.1 to any port 9200
sudo ufw allow from 10.0.0.0/8 to any port 9200 # Если есть внутренняя сеть
# Блокируем внешние подключения
sudo ufw deny 9200
У меня был случай, когда клиент оставил Elasticsearch открытым в интернете без аутентификации. За ночь злоумышленники удалили все данные и потребовали выкуп. С тех пор безопасность — первое, что я настраиваю.
Мониторинг и диагностика проблем
Elasticsearch требует постоянного мониторинга. Я использую комбинацию встроенных API и внешних инструментов.
Основные метрики для мониторинга:
# Здоровье кластера
curl -X GET "localhost:9200/_cluster/health?pretty"
# Статистика по индексам
curl -X GET "localhost:9200/_cat/indices?v&s=store.size:desc"
# Использование памяти узлами
curl -X GET "localhost:9200/_cat/nodes?v&h=name,heap.percent,ram.percent,cpu,load_1m"
# Медленные запросы
curl -X GET "localhost:9200/_cat/pending_tasks?v"
Скрипт для автоматической проверки:
#!/bin/bash
# Проверка здоровья кластера
HEALTH=$(curl -s "localhost:9200/_cluster/health" | jq -r '.status')
if [ "$HEALTH" != "green" ]; then
echo "ALERT: Elasticsearch cluster status is $HEALTH" | mail -s "ES Alert" admin@yourdomain.com
fi
# Проверка использования диска
DISK_USAGE=$(df /var/lib/elasticsearch | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$DISK_USAGE" -gt 85 ]; then
echo "ALERT: Elasticsearch disk usage is ${DISK_USAGE}%" | mail -s "ES Disk Alert" admin@yourdomain.com
fi
# Проверка памяти
HEAP_USAGE=$(curl -s "localhost:9200/_cat/nodes?h=heap.percent" | head -1)
if [ "$HEAP_USAGE" -gt 85 ]; then
echo "ALERT: Elasticsearch heap usage is ${HEAP_USAGE}%" | mail -s "ES Memory Alert" admin@yourdomain.com
fi
Настройка логирования для диагностики:
В файле `/etc/elasticsearch/log4j2.properties` я увеличиваю детализацию для медленных запросов:
# Логируем медленные запросы поиска (больше 1 секунды)
logger.index_search_slowlog_index.name = index.search.slowlog
logger.index_search_slowlog_index.level = info
logger.index_search_slowlog_index.appenderRef.index_search_slowlog_rolling.ref = index_search_slowlog_rolling
logger.index_search_slowlog_index.additivity = false
# Логируем медленные запросы индексации (больше 10 секунд)
logger.index_indexing_slowlog.name = index.indexing.slowlog.index
logger.index_indexing_slowlog.level = info
logger.index_indexing_slowlog.appenderRef.index_indexing_slowlog_rolling.ref = index_indexing_slowlog_rolling
logger.index_indexing_slowlog.additivity = false
А в настройках индекса устанавливаю пороги:
PUT /products/_settings
{
"index.search.slowlog.threshold.query.warn": "2s",
"index.search.slowlog.threshold.query.info": "1s",
"index.search.slowlog.threshold.fetch.warn": "1s",
"index.indexing.slowlog.threshold.index.warn": "10s"
}
На практике это помогает быстро выявлять проблемные запросы. Недавно у клиента поиск начал тормозить — в логах нашёл запросы с wildcard по всему тексту, которые разработчики добавили "для удобства".
Интеграция с популярными CMS
Большинство моих проектов работают на WordPress или Битрикс, поэтому интеграция с этими CMS — частая задача.
Интеграция с WordPress:
Для WordPress я использую плагин ElasticPress или пишу кастомную интеграцию. Вот базовый класс для работы с Elasticsearch в WordPress:
client = \Elasticsearch\ClientBuilder::create()
->setHosts([get_option('elasticsearch_host', 'localhost:9200')])
->setBasicAuthentication(
get_option('elasticsearch_user', 'elastic'),
get_option('elasticsearch_pass', '')
)
->build();
// Хуки WordPress
add_action('save_post', [$this, 'index_post']);
add_action('delete_post', [$this, 'delete_post']);
add_filter('posts_search', [$this, 'elasticsearch_search'], 10, 2);
}
public function index_post($post_id)
{
$post = get_post($post_id);
if (!$post || $post->post_status !== 'publish') {
return;
}
$document = [
'id' => $post->ID,
'title' => $post->post_title,
'content' => wp_strip_all_tags($post->post_content),
'excerpt' => $post->post_excerpt,
'post_type' => $post->post_type,
'post_date' => $post->post_date,
'categories' => wp_get_post_categories($post->ID),
'tags' => wp_get_post_tags($post->ID, ['fields' => 'names'])
];
$this->client->index([
'index' => 'wordpress_posts',
'id' => $post_id,
'body' => $document
]);
}
public function elasticsearch_search($search, $wp_query)
{
global $wpdb;
if (empty($wp_query->query_vars['s'])) {
return $search;
}
$query = $wp_query->query_vars['s'];
try {
$response = $this->client->search([
'index' => 'wordpress_posts',
'body' => [
'query' => [
'multi_match' => [
'query' => $query,
'fields' => ['title^3', 'content', 'excerpt^2']
]
],
'size' => get_option('posts_per_page', 10)
]
]);
$post_ids = array_map(function($hit) {
return $hit['_source']['id'];
}, $response['hits']['hits']);
if (empty($post_ids)) {
return " AND 1=0 "; // Ничего не найдено
}
return " AND {$wpdb->posts}.ID IN (" . implode(',', $post_ids) . ") ";
} catch (Exception $e) {
error_log('Elasticsearch error: ' . $e->getMessage());
return $search; // Возвращаем стандартный поиск при ошибке
}
}
}
Интеграция с Битрикс:
В Битрикс я обычно создаю компонент для поиска и настраиваю обработчики событий:
addEventHandler('iblock', 'OnAfterIBlockElementAdd', 'indexProductToElasticsearch');
$eventManager->addEventHandler('iblock', 'OnAfterIBlockElementUpdate', 'indexProductToElasticsearch');
$eventManager->addEventHandler('iblock', 'OnAfterIBlockElementDelete', 'deleteProductFromElasticsearch');
function indexProductToElasticsearch(&$arFields)
{
if ($arFields['IBLOCK_ID'] != 1) { // ID каталога товаров
return;
}
$element = CIBlockElement::GetByID($arFields['ID'])->Fetch();
$properties = CIBlockElement::GetProperty($arFields['IBLOCK_ID'], $arFields['ID']);
$document = [
'id' => $element['ID'],
'title' => $element['NAME'],
'description' => strip_tags($element['DETAIL_TEXT']),
'price' => 0,
'category_id' => $element['IBLOCK_SECTION_ID'],
'active' => $element['ACTIVE'] === 'Y'
];
// Добавляем свойства
while ($prop = $properties->Fetch()) {
if ($prop['CODE'] === 'PRICE') {
$document['price'] = (float)$prop['VALUE'];
}
if ($prop['CODE'] === 'BRAND') {
$document['brand'] = $prop['VALUE'];
}
}
// Отправляем в Elasticsearch
$elasticsearch = new ElasticsearchService();
$elasticsearch->indexDocument('products', $element['ID'], $document);
}
Честно говоря, интеграция с Битрикс всегда сложнее из-за особенностей архитектуры, но результат стоит того. На одном проекте каталог на 80 тысяч товаров стал работать в разы быстрее.
Для интеграции с другими CMS процесс похожий — главное правильно отловить события создания/изменения/удаления контента и синхронизировать с Elasticsearch. Более подробно о различных интеграциях я писал в статье о настройке API интеграций.
Частые проблемы и их решения
За годы работы с Elasticsearch я сталкивался с десятками проблем. Вот самые частые и способы их решения.
Проблема: OutOfMemoryError
Симптомы: кластер падает с ошибкой памяти, поиск не работает.
Решение: увеличить heap size или оптимизировать запросы. Проверьте настройки в `/etc/elasticsearch/jvm.options`:
# Посмотреть текущее использование памяти
curl -X GET "localhost:9200/_cat/nodes?v&h=name,heap.percent,heap.current,heap.max"
# Очистить кеши
curl -X POST "localhost:9200/_cache/clear"
# Принудительная сборка мусора
curl -X POST "localhost:9200/_nodes/_local/jvm"
Проблема: Медленные запросы
Часто виноваты wildcard-запросы или отсутствие фильтров. Я всегда проверяю explain API:
GET /products/_search
{
"explain": true,
"query": {
"match": {
"title": "медленный запрос"
}
}
}
Или использую профайлер:
GET /products/_search
{
"profile": true,
"query": {
"match": {
"title": "телефон"
}
}
}
Проблема: Индексы становятся read-only
Elasticsearch автоматически блокирует запись при нехватке места на диске. Решение:
# Проверить статус индексов
curl -X GET "localhost:9200/_cat/indices?v"
# Разблокировать индексы
curl -X PUT "localhost:9200/_all/_settings" -H 'Content-Type: application/json' -d '{
"index.blocks.read_only_allow_delete": null
}'
# Освободить место и изменить настройки watermark
curl -X PUT "localhost:9200/_cluster/settings" -H 'Content-Type: application/json' -d '{
"transient": {
"cluster.routing.allocation.disk.watermark.low": "85%",
"cluster.routing.allocation.disk.watermark.high": "90%"
}
}'
Проблема: Неточные результаты поиска
Обычно проблема в неправильном анализаторе или маппинге. Тестирую анализатор:
GET /products/_analyze
{
"analyzer": "russian_analyzer",
"text": "телефоны Samsung"
}
И проверяю, как индексируется конкретное поле:
GET /products/_analyze
{
"field": "title",
"text": "Samsung Galaxy S24"
}
На одном проекте поиск по слову "телефон" не находил "телефоны" — оказалось, забыли подключить морфологический анализатор.
Проблема: Высокая нагрузка на CPU
Часто причина в слишком частом refresh индекса или большом количестве мелких сегментов:
# Принудительное слияние сегментов
curl -X POST "localhost:9200/products/_forcemerge?max_num_segments=1"
# Изменение частоты refresh
curl -X PUT "localhost:9200/products/_settings" -H 'Content-Type: application/json' -d '{
"index": {
"refresh_interval": "30s"
}
}'
Elasticsearch — мощный инструмент, но требует понимания внутренней архитектуры. Не пытайтесь настроить всё сразу — начните с базовой конфигурации и постепенно оптимизируйте под ваши задачи. Правильно настроенный Elasticsearch кардинально улучшает пользовательский опыт и может стать конкурентным преимуществом вашего проекта.
Если у вас возникают сложности с настройкой поиска или нужна помощь с оптимизацией производительности сайта, обращайтесь за поддержкой Битрикс или доработкой сайта — поможем настроить всё правильно с первого раза.
Нужна помощь с настройкой поиска на сайте?
Наши эксперты помогут внедрить и настроить Elasticsearch для максимальной скорости поиска на вашем проекте.