Создание поиска товаров с фасетными фильтрами

Alex Chibilyaev

Alex Chibilyaev

5/1/2025

#tutorial#faceted-search#ecommerce#filters
Создание поиска товаров с фасетными фильтрами

Фасетная фильтрация — это разница между «я не могу найти то, что ищу» и «именно то, что мне нужно». Клиенты, использующие фильтры, конвертируются в 2–3 раза чаще тех, кто их не использует. В этом руководстве вы создадите готовый к продакшену фасетный поиск товаров с помощью AACsearch.

Что такое фасетные фильтры?

Фасетные фильтры позволяют пользователям сужать результаты поиска по атрибутам товаров: ценовой диапазон, бренд, размер, цвет, категория, рейтинг — любые параметры, важные для ваших клиентов. В отличие от простого просмотра категорий, фасеты комбинируются. Пользователь может отфильтровать по «Nike» + «кроссовки» + «до $150» + «размер 10» и увидеть только подходящие товары.

AACsearch поддерживает фасеты нативно. Никакого кастомного индексирования, никакого внешнего поискового сервиса. Объявите, какие поля являются фасетными, и поисковая система вернёт доступные варианты фильтров с каждым запросом.

Шаг 1: Настройка индекса с фасетными полями

Войдите в панель управления AACsearch и создайте новый индекс. При определении схемы отметьте эти поля как facet: true:

{
	"name": "string",
	"brand": { "type": "string", "facet": true },
	"category": { "type": "string", "facet": true },
	"price": { "type": "float", "facet": true },
	"color": { "type": "string", "facet": true },
	"size": { "type": "string", "facet": true },
	"rating": { "type": "float", "facet": true },
	"in_stock": { "type": "bool", "facet": true },
	"tags": { "type": "string[]", "facet": true }
}

AACSearch индексирует значения фасетов в битовой карте — фильтрация во время запроса выполняется за субмиллисекунды независимо от размера коллекции. Ценовые фасеты возвращаются в виде группировки гистограммы с количеством по диапазонам.

Шаг 2: Импорт товаров

Загрузите свой каталог товаров через CSV, JSON или API. Каждый документ товара должен содержать значения фасетов:

[
	{
		"id": "101",
		"name": "Ultra Running Shoes",
		"brand": "Nike",
		"category": "Footwear",
		"price": 129.99,
		"color": "Black",
		"size": "10",
		"rating": 4.5,
		"in_stock": true,
		"tags": ["running", "outdoor", "men"]
	},
	{
		"id": "102",
		"name": "Trail Hiker Boots",
		"brand": "Merrell",
		"category": "Footwear",
		"price": 159.99,
		"color": "Brown",
		"size": "11",
		"rating": 4.7,
		"in_stock": true,
		"tags": ["hiking", "outdoor", "unisex"]
	}
]

Совет: Держите документы плоскими. AACSearch работает лучше всего с денормализованными данными. Если у вас есть товары с вариантами (размер, цвет), создавайте один документ на вариант с полем parent_id для их группировки.

Шаг 3: Подключение Widget с фасетами

Widget AACsearch отображает фасеты автоматически. Инициализируйте его с конфигурацией фасетов:

const search = new AACsearchWidget({
	apiKey: "ss_search_your_api_key",
	index: "products",
	facets: [
		{ field: "brand", type: "string", label: "Brand" },
		{ field: "category", type: "string", label: "Category" },
		{ field: "price", type: "range", label: "Price Range" },
		{ field: "color", type: "string", label: "Color" },
		{ field: "rating", type: "range", label: "Rating" },
		{ field: "in_stock", type: "boolean", label: "In Stock" },
	],
	searchableFields: ["name", "brand", "category", "tags"],
	resultsPerPage: 24,
	sortOptions: [
		{ label: "Relevance", value: "_text_match:desc" },
		{ label: "Price: Low to High", value: "price:asc" },
		{ label: "Price: High to Low", value: "price:desc" },
		{ label: "Rating", value: "rating:desc" },
		{ label: "Newest", value: "id:desc" },
	],
});

search.mount("#search-container");

Widget обрабатывает:

  • Множественный выбор фасетов (пользователь выбирает Nike И Adidas)
  • Ползунки ценового диапазона (перетаскиваемая гистограмма)
  • Отображение цветовых образцов (фасеты типа color показывают кружки)
  • Теги активных фильтров с кнопками удаления
  • Адаптивную панель фасетов для мобильных устройств

Шаг 4: Настройка правил отображения

Фасеты должны выглядеть естественно, а не перегружать. Настройте их в панели управления AACsearch:

Свёрнуты по умолчанию: Категории с более чем 10 значениями (показать ссылку «Показать ещё») Развёрнуты по умолчанию: Ценовой диапазон, рейтинг, наличие на складе Поиск внутри фасетов: Для брендов с 50+ значениями пользователи могут искать в списке фасетов Порядок фасетов: Поднимите «Категорию» и «Бренд» наверх, опустите «Теги» вниз

Настройте лимиты отображения фасетов:

| Фасет | Макс. значений | Поведение разворачивания | | -------- | -------------- | ------------------------ | | Brand | 8 | Показать ещё +N | | Category | 10 | Показать ещё +N | | Color | Все | Сетка с образцами | | Price | Диапазон | Ползунок-гистограмма | | Rating | 5 | Выбор по звёздам |

Шаг 5: Добавление повышения релевантности для фильтрованных поисков

Когда пользователь фильтрует по бренду, поднимите товары этого бренда выше в результатах. AACsearch поддерживает динамические правила ранжирования:

Повышение категории: Если пользователь выбрал «Footwear» → повысить всю обувь в 1.5x Повышение новинок: Товары, добавленные за последние 30 дней, получают повышение 1.2x Приоритет наличия: Товары не в наличии всегда отображаются после товаров в наличии (при одинаковом уровне релевантности)

Настройте это в панели управления в разделе Relevance Tuning → Ranking Rules.

Пример: Полная страница интернет-магазина

Вот полная реализация с использованием widget AACsearch в приложении Next.js:

// app/products/page.tsx
import dynamic from "next/dynamic";

const SearchPage = dynamic(() => import("./search-client"), { ssr: false });

export default function ProductsPage() {
	return (
		<div className="max-w-7xl mx-auto px-4 py-8">
			<h1 className="text-3xl font-bold mb-8">All Products</h1>
			<SearchPage />
		</div>
	);
}
// app/products/search-client.tsx
"use client";

import { useEffect, useRef } from "react";

export default function SearchClient() {
	const containerRef = useRef<HTMLDivElement>(null);

	useEffect(() => {
		if (!containerRef.current || typeof window === "undefined") return;

		const widget = new AACsearchWidget({
			apiKey: process.env.NEXT_PUBLIC_AACSEARCH_API_KEY!,
			index: "products",
			facets: [
				{ field: "category", type: "string", label: "Category" },
				{ field: "brand", type: "string", label: "Brand" },
				{ field: "price", type: "range", label: "Price Range" },
			],
			searchableFields: ["name", "brand", "description"],
		});

		widget.mount(containerRef.current);

		return () => widget.destroy();
	}, []);

	return <div ref={containerRef} className="min-h-[600px]" />;
}

Производительность при масштабировании

  • 50 000 товаров, 50 фасетов: < 50 мс время ответа (P95)
  • 500 000 товаров, 100 фасетов: < 120 мс время ответа
  • Множественный выбор фасетов: Нет снижения производительности — пересечение битовых карт выполняется за O(1)
  • Одновременные фильтрованные поиски: 500+ QPS на тарифе Scale

Следующие шаги

Готовы создать свой поиск? Начните бесплатно →