Налаштування Elasticsearch для фасетного пошуку з агрегаціями

Налаштування Elasticsearch для фасетного пошуку з агрегаціями

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Налаштування Elasticsearch для фасетного пошуку з агрегаціями
Складний
~3-5 днів

Наші компетенції:

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1419
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    983
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1244
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    983
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    998

Налаштування Elasticsearch для фасетного пошуку з агрегаціями

Уявіть каталог з мільйоном товарів. Без фасетного пошуку користувач гортає 200 сторінок. З ним — за секунду знаходить потрібний бренд і ціновий діапазон. Але лічильники брешуть, якщо не налаштувати post_filter і global aggregations. У цій статті розберемо, як зібрати коректні фасетні лічильники на Elasticsearch, уникнувши типових помилок. Економія часу розробки при використанні готового рішення — до 30%.

Ми інтегруємо Elasticsearch з фасетним пошуком у каталогах та інтернет-магазинах. Це фільтри-лічильники: «Бренд: Samsung (143), Apple (89)», «Ціна: до 30 000 (234), 30–60 000 (145)». При виборі фільтра список товарів звужується, а лічильники інших значень перераховуються. У реляційних базах такі операції дорогі — GROUP BY з COUNT(*) по відфільтрованій вибірці. В Elasticsearch задача вирішується агрегаціями часто за єдиний запит. Оцінимо ваш проект і реалізуємо під ключ за 2–4 робочі дні.

Як реалізувати коректні лічильники фасетів?

Типовий запит для каталогу: пошуковий запит + активні фільтри + агрегації для підрахунку фасетів. Приклад базового запиту:

POST /products/_search { "query": { "bool": { "must": [ { "match": { "title": "ноутбук" } } ], "filter": [ { "term": { "is_active": true } }, { "range": { "price": { "gte": 20000, "lte": 80000 } } } ] } }, "aggs": { "by_brand": { "terms": { "field": "brand", "size": 20, "order": { "_count": "desc" } } }, "price_ranges": { "range": { "field": "price", "ranges": [ { "key": "до 30 000", "to": 30000 }, { "key": "30–60 000", "from": 30000, "to": 60000 }, { "key": "60–100 000", "from": 60000, "to": 100000 }, { "key": "від 100 000", "from": 100000 } ] } }, "price_stats": { "stats": { "field": "price" } }, "by_category": { "terms": { "field": "category", "size": 10 } }, "by_rating": { "histogram": { "field": "rating", "interval": 1, "min_doc_count": 1 } } }, "size": 20, "from": 0 } 

Проблема зникаючих лічильників при виборі фасета

Якщо користувач обирає бренд «Samsung» і додає його в filter, агрегація by_brand рахує тільки всередині відфільтрованої вибірки. Лічильники інших брендів обнуляються — користувач не бачить, скільки товарів в інших брендів. Рішення — Post Filter у поєднанні з Global Aggregation:

POST /products/_search { "query": { "bool": { "must": [ { "match": { "title": "ноутбук" } } ], "filter": [ { "term": { "is_active": true } } ] } }, "post_filter": { "bool": { "filter": [ { "term": { "brand": "Samsung" } } ] } }, "aggs": { "all_brands": { "global": {}, "aggs": { "filtered_brands": { "filter": { "bool": { "must": [ { "match": { "title": "ноутбук" } } ], "filter": [ { "term": { "is_active": true } } ] } }, "aggs": { "brands": { "terms": { "field": "brand", "size": 20 } } } } } }, "price_ranges": { "range": { "field": "price", "ranges": [ { "key": "до 30 000", "to": 30000 }, { "key": "30–60 000", "from": 30000, "to": 60000 }, { "key": "від 60 000", "from": 60000 } ] } } }, "size": 20 } 

Згідно з документацією Elasticsearch, post_filter застосовується до результатів після агрегації, а global агрегація виходить за межі контексту query. post_filter застосовується до результатів після агрегації. Агрегації рахуються по всій вибірці (до post_filter), тому лічильники брендів залишаються коректними. global агрегація виходить за межі контексту query — дозволяє рахувати агрегати по всіх документах індексу з додатковими фільтрами.

Як працювати з вкладеними атрибутами?

Якщо атрибути (розмір, колір, матеріал) зберігаються як nested об'єкти:

"mappings": { "properties": { "attributes": { "type": "nested", "properties": { "name": { "type": "keyword" }, "value": { "type": "keyword" } } } } } 

Агрегація по вкладеним атрибутам:

"aggs": { "attributes": { "nested": { "path": "attributes" }, "aggs": { "attribute_names": { "terms": { "field": "attributes.name", "size": 50 }, "aggs": { "attribute_values": { "terms": { "field": "attributes.value", "size": 20 } } } } } } } 

Це дворівневі вкладені агрегації: спочатку групуємо за ім'ям атрибута («Колір», «Розмір»), всередині кожної групи — значення («Чорний», «Білий»).

Cardinality — підрахунок унікальних значень

Для відображення «Знайдено 1 247 товарів від 89 брендів» використовуйте агрегацію cardinality з полем brand і precision_threshold 100. Це гарантує точність до 100 унікальних значень, вище — похибка 1–6%.

Налаштування фасетного пошуку за 5 кроків

  1. Визначте поля для фільтрації: бренд, ціна, категорія, рейтинг, атрибути. Типовий набір — 5–10 полів.
  2. Налаштуйте маппінг: для ключових полів використовуйте тип keyword, для вкладених — nested. Переконайтеся, що поля для агрегацій не аналізуються.
  3. Побудуйте запит з агрегаціями: включіть всі потрібні агрегації (terms, range, histogram, nested). Для коректних лічильників використовуйте post_filter і global агрегації.
  4. Застосуйте post_filter: відокремте фільтри від основного query. Це збереже лічильники агрегацій.
  5. Оптимізуйте продуктивність: використовуйте execution_hint: map для полів з низькою кардинальністю, кешування для агрегацій без пошукового запиту, обмежте size до 20.

Оптимізація агрегацій

terms агрегація з великим size — дорога операція: кожен шард повертає top-N значень, координуючий вузол мержить результати. Для висококардинальних полів (тисячі унікальних значень) продуктивність падає. Способи оптимізації:

Таблиця методів оптимізації
Метод Опис Коли застосовувати
execution_hint: map Для полів з низькою кардинальністю (<1000) працює швидше за ordinals Поля типу статус, категорія
Кешування агрегацій Агрегації без пошукового запиту кешуються в shard request cache Сторінки каталогу без пошуку
Обмеження size Для фасетів достатньо top-20 значень; глибока пагінація не потрібна Завжди

Порівняння типів агрегацій

Тип Приклад використання Продуктивність
Terms Групування за брендом Швидко при кардинальності <10k
Range Цінові діапазони Дуже швидко
Histogram Рейтинг з кроком 1 Швидко
Nested Вкладені атрибути Середньо (залежить від кількості об'єктів)
Cardinality Унікальні бренди Швидко (алгоритм HyperLogLog)

Реалізація на стороні застосунку

PHP клас для побудови фасетних запитів:

class FacetedSearchService { public function search(array $params): array { $query = $this->buildQuery($params); $response = $this->client->search([ 'index' => 'products', 'body' => $query, ]); return [ 'hits' => $response['hits']['hits'], 'total' => $response['hits']['total']['value'], 'facets' => $this->extractFacets($response['aggregations']), ]; } private function extractFacets(array $aggs): array { $facets = []; if (isset($aggs['by_brand'])) { $facets['brand'] = array_map(fn($b) => [ 'value' => $b['key'], 'count' => $b['doc_count'], ], $aggs['by_brand']['buckets']); } if (isset($aggs['price_ranges'])) { $facets['price'] = array_map(fn($r) => [ 'label' => $r['key'], 'count' => $r['doc_count'], ], $aggs['price_ranges']['buckets']); } return $facets; } } 

Що входить в налаштування фасетного пошуку під ключ

  • Проектування схеми маппінгів та індексів.
  • Налаштування агрегацій усіх типів (terms, range, nested, cardinality).
  • Реалізація логіки post_filter + global aggregations для коректних лічильників.
  • Оптимізація продуктивності (execution_hint, кешування).
  • Написання серверного коду для побудови запитів та обробки відповідей.
  • Документація, навчання команди, підтримка після впровадження.

Терміни

  • Базова реалізація (3–5 типів агрегацій) — 2 робочі дні.
  • Складний сценарій (post_filter, global, nested) — 3–4 дні.
  • Оптимізація на реальних обсягах (>5 млн документів) — додатковий день.

Наша команда має 6+ років досвіду в Elasticsearch та сертифікованих інженерів. Ми реалізували понад 50 проектів з фасетним пошуком, гарантуючи точність лічильників та продуктивність. Зв'яжіться з нами для консультації — допоможемо уникнути дорогих помилок. Вартість розраховується індивідуально, а економія від впровадження може досягати 40 000 ₽ на етапі розробки та до 20 000 ₽ на місяць на інфраструктурі. Отримайте готове рішення з повною документацією та підтримкою.