Розробка фільтрації товарів за параметрами для інтернет-магазину

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

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

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка фільтрації товарів за параметрами для інтернет-магазину
Середня
~3-5 робочих днів
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розробка фільтрації товарів по параметрам для інтернет-магазину

Фільтрація — один з головних інструментів навігації в каталозі. Користувач з 500 ноутбуками в категорії не буде переглядати сторінки — він відфільтрує за брендом, RAM та діагоналлю. Погана фільтрація втрачає ці продажі. Хороша фільтрація — це faceted search: доступні значення фільтрів оновлюють залежно від уже вибраних, і користувач завжди знає, скільки товарів за кожним значенням.

Типи фільтрів

Тип UX-компонент Приклад Технічно
Множинний вибір Чекбокси Бренд: Apple, Samsung WHERE brand IN (...)
Одиничний вибір Radio buttons Стан: новий/б/у WHERE condition = ...
Числовий діапазон Slider з двома ручками Ціна: 5000–30000 руб. WHERE price BETWEEN ...
Діапазон через інпути Поля «від» та «до» Діагональ: 13–15.6 дюйм WHERE diagonal BETWEEN ...
Булевий Переключатель Тільки в наявності WHERE stock > 0
Рейтинг Зірочки (≥N) Рейтинг від 4 WHERE rating >= 4
Колір Кольорові свотчи Колір: чорний, срібло WHERE color IN (...)

Faceted search з SQL

Найпростіший підхід — фільтрація через PostgreSQL. Працює до ~100 000 товарів при правильній індексації.

-- Основний запит з фільтрами
SELECT p.* FROM products p
WHERE p.category_id = :cat
  AND (:brands IS NULL OR p.brand = ANY(:brands::text[]))
  AND (:price_min IS NULL OR p.price >= :price_min)
  AND (:price_max IS NULL OR p.price <= :price_max)
  AND (:in_stock IS NULL OR p.stock > 0)
ORDER BY p.sort_order
LIMIT 48 OFFSET :offset;

-- Агрегації для лічильників (окремий запит на кожен фільтр)
SELECT brand, COUNT(*) FROM products p
WHERE p.category_id = :cat
  -- Всі фільтри КРІМ brand
  AND (:price_min IS NULL OR p.price >= :price_min)
GROUP BY brand;

Проблема SQL-підходу: для коректних лічильників потрібен окремий запит агрегації для кожного фільтра, виключаючи цей фільтр з умов. При 10 активних фільтрах — 10 додаткових запитів. На реальному навантаженні це не масштабується.

Faceted search з Elasticsearch

Elasticsearch вирішує завдання за один запит через aggregations:

{
  "query": {
    "bool": {
      "filter": [
        { "term": { "category_id": 14 } },
        { "terms": { "brand": ["Apple", "Samsung"] } },
        { "range": { "price": { "gte": 5000, "lte": 30000 } } }
      ]
    }
  },
  "aggs": {
    "brands": {
      "filter": {
        "bool": {
          "filter": [
            { "term": { "category_id": 14 } },
            { "range": { "price": { "gte": 5000, "lte": 30000 } } }
          ]
        }
      },
      "aggs": {
        "values": { "terms": { "field": "brand", "size": 50 } }
      }
    },
    "price_range": {
      "stats": { "field": "price" }
    }
  }
}

Кожна агрегація (brands, ram, screen_size) використовує фільтр без своєї власної умови — це й є faceted search. Один запит повертає й товари, й всі лічильники для всіх фільтрів.

URL-схема для фільтрів

URL повинен відображати стан фільтрів для шарингу та SEO:

/noutbuki?brand=apple,samsung&ram=16&price_min=50000&price_max=100000&sort=price_asc

При змінені фільтра — pushState або replaceState без перезагрузки сторінки. При прямому входу по URL — інціалізація стану фільтрів з параметрів.

SEO-підхід: популярні комбінації фільтрів (бренд + категорія) оформляються як окремі статичні сторінки з унікальним контентом та canonical. Сторінки з рідкими комбінаціями — <meta name="robots" content="noindex">.

Клієнтська реалізація

Стан фільтрів зберігається в URL (source of truth) та дзеркалюється в React state:

type FilterState = {
  brands: string[];
  ram: number | null;
  priceMin: number | null;
  priceMax: number | null;
  inStock: boolean;
  sort: 'price_asc' | 'price_desc' | 'popularity' | 'rating';
};

function useFilters() {
  const [searchParams, setSearchParams] = useSearchParams();

  const filters = useMemo(() => parseFilters(searchParams), [searchParams]);

  const setFilter = (key: keyof FilterState, value: unknown) => {
    const next = { ...filters, [key]: value };
    setSearchParams(buildParams(next), { replace: true });
  };

  return { filters, setFilter };
}

При кожній змінені фільтра — debounce 300ms, потім запит до API. Результати оновлюються без перезагрузки сторінки.

Ценовий слайдер

Компонент діапазону цін — окрема задача. Вимоги:

  • Два handle (min та max), які не можуть перетнутися
  • При введенні з клавіатури — валідація та clamp
  • Гістограма розподілу цін за слайдером (показує, де сконцентровані товари)

Гістограма: Elasticsearch aggregation histogram з interval = (max_price - min_price) / 20. Відображується через SVG path або крихітний bar chart.

Готові компоненти: @radix-ui/react-slider, rc-slider, noUiSlider. Radix-варіант переважний при Tailwind-стеку.

Оптимізація продуктивності

Кеш агрегацій: результати підрахунку фасетів не змінюються при кожному запиті. Кешуємо агрегації для категорії з типовим набором фільтрів у Redis на 5–10 хвилин. При обновленні товара інвалідуємо кеш категорії.

Індекси PostgreSQL:

-- Складений індекс для типового запиту
CREATE INDEX ON products (category_id, brand, price)
  WHERE status = 'active';

-- GIN-індекс для JSONB-атрибутів
CREATE INDEX ON products USING GIN (attributes);

Lazy loading фасетів: показуємо перші 5–7 значень, кнопка «Показати все» підгружає решту окремим запитом.

Мобільна адаптація

На мобайле фільтри сховані за кнопкою «Фільтри» → відкривається нижній drawer (bottom sheet) на весь екран. Усередині — ті ж компоненти, але з збільшеними touch targets. Кнопка «Застосувати» зафіксована внизу. При застосуванні drawer закривається, список обновляється.

Терміни

  • Базова фільтрація (SQL, чекбокси по 3–4 атрибутам, діапазон цін): 1–2 тижні
  • Faceted search на Elasticsearch (динамічні лічильники, всі типи фільтрів, URL-синхронізація): 3–4 тижні
  • Додавання гістограми цін та кешування агрегацій: +1 тиждень

Вибір між SQL та Elasticsearch визначається розміром каталогу. До 50 000 товарів добре спроектований SQL справиться. Вище — Elasticsearch дає якісну різницю в швидкості та багатстві фасетів.