Типичный разговор на третьем месяце жизни магазина: «добавьте фильтр по цвету и мощности». Открываешь каталог — и выясняется, что фильтровать нечем. В поле «Цвет» лежат значения «синий», «Синий», «синий », «тёмно-синий» и «синий матовый RAL 5002» — пять разных строк, которые для базы данных не имеют друг к другу никакого отношения. Фильтр по такому полю технически сделать можно, пользоваться им нельзя.
Это самая дорогая ошибка в каталоге, потому что она обнаруживается после наполнения. Разберём, как устроены свойства, какие решения принимаются до загрузки первого товара и что чинится потом, а что — нет.
Свойство — это тип данных, а не просто поле
В любом каталоге свойство заводится с указанием типа. Набор примерно одинаковый во всех движках:
- Список (справочник) — значение выбирается из заранее заданного набора
- Число — целое или с точкой, допускает диапазоны и сортировку
- Строка — произвольный текст
- Флаг — да или нет
- Привязка к элементу — например, к бренду как к отдельной сущности
- Файл — изображение, инструкция, сертификат
И дальше одно правило, из которого растёт всё остальное: всё, по чему будут фильтровать или сортировать, должно быть списком или числом. Никогда строкой. Строка годится только для того, что показывается в карточке и не участвует в отборе: комплектация, примечание, страна в свободной форме.
Строковое свойство — это бесконечный справочник, в котором каждое значение уникально. Фильтр по нему либо не собирается вообще, либо собирается с сотней вариантов, где «синий» и «Синий» стоят рядом двумя отдельными пунктами.
Почему строка не работает
Три причины, и все три проявляются сразу.
Нет нормализации. Регистр, лишний пробел, другая раскладка буквы «е», дефис против тире — и это уже разные значения. Когда товары заводят три человека или они приезжают из учётной системы, расхождения появляются гарантированно.
Нет диапазонов. Строка «1,5 кВт» не сравнивается с «2 кВт»: для базы это текст, и «10» меньше «9». Ползунок «мощность от и до» на строковом свойстве невозможен в принципе.
Нет связей. У значения-элемента справочника есть идентификатор, символьный код, порядок сортировки и, при желании, картинка и описание. У строки нет ничего — только сама строка. Отсюда же невозможность сделать по значению отдельную страницу с человеческим адресом.
Единицы измерения живут в названии, а не в значении
Правило, которое экономит потом неделю работы: свойство называется «Вес, кг», а в значении лежит число 0.5. Не «0,5 кг», не «500 г», не «полкило». Как только единица измерения попала в значение, свойство перестало быть числовым, и диапазоны закончились.
Отсюда же следствие: одно свойство — одна единица. Если часть товаров весит граммы, а часть килограммы, приводим всё к одной единице при загрузке, а не держим два свойства и не пишем единицу в значение.
Свойство товара или свойство предложения
Второе по цене решение. Если у товара есть варианты — размеры, цвета, фасовки, — часть характеристик относится к товару, а часть к конкретному варианту.
Признак простой: если характеристика влияет на цену или остаток, она принадлежит предложению. Размер футболки — предложению: у размера L свой остаток и, возможно, своя цена. Состав ткани — товару: он одинаков для всех размеров.
Что бывает при ошибке: фильтр по цвету отдаёт товар, у которого синий вариант кончился, потому что цвет записан на уровне товара и означает «бывает в синем», а не «есть синий». Человек заходит в карточку, выбирает синий — и видит «нет в наличии». Про то, как это соотносится с остатками из учёта, подробно писали здесь: почему на сайте и в 1С разные остатки.
Наборы свойств по типам товара
Третья ошибка — заводить все свойства общим списком на весь каталог. В магазине инструмента и крепежа это даёт двести-триста свойств, из которых у каждого товара заполнено пятнадцать. Карточка превращается в таблицу с пустыми строками, фильтр — в список параметров, которые ни к чему не применимы.
Правильно — привязывать набор свойств к типу товара или к разделу. Тогда в разделе «Шуруповёрты» фильтр показывает напряжение, крутящий момент и тип патрона, а в «Кабеле» — сечение, число жил и материал изоляции. Технически это делается либо типами товаров, либо настройкой фильтра на уровне раздела — в разных движках по-разному, но решение принимается одно и то же.
Как это хранится и почему тормозит
Свойства почти всегда хранятся по схеме EAV — «сущность, атрибут, значение». В 1С-Битрикс это таблицы значений свойств инфоблока, в WooCommerce — таксономии pa_* плюс метаполя, в собственных решениях — тройка таблиц: атрибуты, значения атрибутов и связь товара со значением.
Схема гибкая: добавить свойство — одна строка, миграции не нужны. Но у неё есть цена. Отбор «мощность от 1 до 2 кВт И патрон быстрозажимной И бренд из трёх вариантов» превращается в несколько соединений или подзапросов к таблице, в которой лежат миллионы строк: пятьдесят тысяч товаров на двадцать свойств — это уже миллион значений.
И главное: самое дорогое в фильтре — не отбор товаров, а счётчики рядом с каждым значением. «Быстрозажимной (124)» требует посчитать, сколько товаров останется при выборе этого значения с учётом всех остальных уже выбранных условий. Для фильтра на десять параметров с сорока значениями это десятки агрегатных запросов на одну отрисовку страницы.
Чем это лечится
- Денормализованная таблица фасетов: товар — свойство — значение, одной плоской строкой, с составным индексом под нужный порядок полей
- Предпосчёт счётчиков при изменении каталога, а не при каждом открытии страницы
- Фасетный индекс умного фильтра в Битриксе — ровно этот механизм, его надо включить и перестраивать после импорта
- Перенос фасетов в поисковый движок: агрегации по полям считаются там на порядок быстрее, чем соединениями в SQL
- Отказ от счётчиков там, где они не нужны, — вместо них блокировка значений, которые дадут нулевой результат
- Курсорная пагинация вместо глубоких смещений: страница с OFFSET в двадцать тысяч читает и выбрасывает все предыдущие строки
Порядок работы при жалобе «фильтр тормозит» такой: сначала смотрим, включён ли фасетный индекс и перестраивается ли он после обмена; потом — счётчики; и только потом обсуждаем вынос в отдельный движок. В половине случаев хватает первого шага. Остальное относится уже к ускорению сайта, где каталог и так самая тяжёлая часть.
Свойства приезжают из учётной системы
Если каталог наполняется обменом, структуру свойств задаёт не сайт, а учёт. И тут есть несколько правил эксплуатации, которые нарушают регулярно.
Сопоставление идёт по идентификатору свойства, а не по названию. Переименовали свойство в учёте — на сайте, если сопоставление было по имени, появится второе свойство, а старое останется с прежними значениями. Поэтому перед первым обменом фиксируется список свойств, которые уезжают на сайт, и дальше он меняется только осознанно.
Второй вопрос — что делать с новым значением, которого на сайте ещё нет. Вариантов два: создавать автоматически или игнорировать и писать в лог. Автоматическое создание удобно, но при опечатке в учёте в справочнике появляется «Быстрозажимнй», и фильтр обрастает мусором. Мы обычно включаем создание на этапе первичного наполнения и выключаем после — дальше новые значения добавляются руками после проверки. Как это устроено со стороны выгрузки, разбирали в статьях про обмен по CommerceML и импорт прайс-листа.
Третье: характеристики в учёте часто ведутся одной текстовой строкой — «Мощность 1,5 кВт, патрон быстрозажимной». На сайте такую строку надо разбирать на свойства парсером, и это всегда хрупко. Дешевле один раз завести в учёте нормальные свойства, чем годами поддерживать разбор строк. Это решение принимает не программист, а тот, кто ведёт номенклатуру, — и лучше обсудить его до начала работ, а не после.
Что делать, если каталог уже наполнен строками
Это чинится, но не за вечер. Порядок такой:
- Выгрузить уникальные значения каждого проблемного свойства с количеством товаров
- Нормализовать механически: регистр, пробелы по краям, двойные пробелы, «ё», разные виды дефисов и кавычек
- Собрать карту соответствий «как написано — как должно быть» и прогнать её глазами: машина не отличит «синий матовый» от «синий»
- Завести свойство-список с чистыми значениями и порядком сортировки
- Перенести значения по карте, оставив старое свойство нетронутым до проверки
- Сверить итог: товары без значения, значения без товаров, расхождение количеств
- Только после сверки переключить фильтр на новое свойство и убрать старое
Главное на этом пути — не трогать старые данные до того, как новые проверены. И закрыть источник: если каталог наполняется обменом, а в учёте свойства остались строками, через неделю всё вернётся как было.
Про SEO — коротко
Структура свойств определяет, можно ли вообще сделать страницы под запросы вида «шуруповёрт аккумуляторный 18в». Значение-справочник имеет символьный код, из которого собирается адрес страницы фильтра; у строки кода нет, и страницу делать не из чего. То есть решение «свойство списком или строкой» — это ещё и решение о том, будет ли у каталога десяток страниц или двести. Подробно эту механику разбирали отдельно: SEO-страницы из фильтров каталога.
Чек-лист перед наполнением каталога
- Список свойств зафиксирован и разделён: фильтруемые, показываемые, служебные
- У каждого фильтруемого свойства тип — список или число
- Единицы измерения вынесены в название свойства
- Решено, какие характеристики относятся к предложению, а какие к товару
- Наборы свойств привязаны к типам товаров, а не ко всему каталогу
- Справочники значений заполнены заранее, с порядком сортировки
- Определён источник истины: сайт или учётная система
- Решено, что делать с новым значением из обмена: создавать или в лог
- Включён фасетный индекс или спроектирована таблица фасетов
- Проверено поведение фильтра на реальном объёме, а не на двадцати тестовых товарах
Последний пункт стоит отдельного упоминания. Фильтр, который на демо-данных отвечает мгновенно, на пятидесяти тысячах товаров может думать несколько секунд. Проверять надо на боевом объёме — хотя бы на сгенерированном, если реального каталога ещё нет.
Мы проектируем структуру свойств до наполнения — это часть работы над интернет-магазином и каталогом или порталом, где от неё зависит и фильтр, и поиск, и страницы под запросы. Если каталог уже живёт и фильтра в нём фактически нет, начинаем с выгрузки значений и карты соответствий: обычно становится видно, что две трети свойств чистятся механически, а руками надо разобрать два-три. Дальше это стыкуется с SEO интернет-магазина — структура свойств и есть основа для посадочных страниц каталога.