Фильтр товаров по свойствам

melnic

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

Пока придумал вариант, с использованием 5 табличек:
1 Товары
2 Свойства
3 Разделы
4 Свойства к товарам
5 Свойства к разделам

Фильтр такой есть во всех современных интернет магазинах, хочется повторить подвиг, но информации по этому вопросу не смог найти.

Интересует как сформировать фильтр для фильтрации, потом как отфильтровать элементы.
Чтобы всё ещё не обрабатывалось по 10-20 секунд при этом.

Движок Yii.
Понимаю, что вопрос задан несколько обобщённо, но пока застрял на этом этапе.

За любые подсказки, заранее спасибо!
 

Linker

Новичок
В теории это могут называть "фасетный поиск".
Часто прикручивают готовые решения и даже всякие ElasticSearch
 

md5S

Это вам не это.
Распиши структуру таблиц и дальше будет видно
 

melnic

Новичок
Если немножко упростить, то вот такая структура в табличках:

Таблица 1: Товар
Stock (stockId, categoryId, partNumber, name, alias, description, published, price)
Таблица 2: Категории для товара
StockCategory (categoryId, parentCategoryId, sectionId, name, description, published)
Таблица 3: Названия параметров
StockCategoryParameter (paramId, name, paramType)
Таблица 4: Связка Параметров к категориям
StockCategoryParameterAssignment (id, categoryId, paramId)
Таблица 5: Связка — Значение параметра с конкретным товаром
StockParameterAssignment (id, stockId, paramId, value)
 

Тугай

Новичок
Запрос по параметрам строится так, делается столько объединений сколько разных типов параметров, например:
Код:
select s.stockId
from Stock s
inner join StockParamAss spa1 on (s.stockid=spa1.stockid and spa1.paramid=123 and spa1.value='LSD')
inner join StockParamAss spa2 on (s.stockid=spa2.stockid and spa2.paramid=456 and spa2.value in ('19', '22'))
...
тут paramtype=1 - тип монитора, paramtype=2 - диагональ.
Кроме типов параметров, нужен еще тип фильтра (точный, составной, диапазон, ...), который задает условие для value.
Это большая тема и тут все не распишешь.

По оптимизации могу сказать, что у себя делаю в два этапа. На первом во временную таблицу собираются все stockid которые фильтруются по цене, категории, производителю, и т.п. И потом все расчеты по фильтрам идут с этой временной таблицей. (такой разделение у меня дает 5-10% ускорение)
 

A1x

Новичок
я бы делал на сфинксе - на каждый тип товара (набор свойств) - свой отдельный индекс
 

riff

Новичок
PHP:
function filter_products()
{
    DB::query('
        CREATE TEMPORARY TABLE filters (
         product_id int unsigned,
         property_id int unsigned,
         property_value_id int unsigned,
         status tinyint(2) unsigned,
         KEY product_id (product_id)
        ) ENGINE=MyISAM ROW_FORMAT=FIXED
    ');

    //находим все товары со всеми их свойствами в этом каталоге и его подкаталогах
    //и добавляем их в таблицу фильтров
    DB::query('
        INSERT INTO filters
        SELECT product_id, property_id, property_value_id, 0
        FROM products -- из текущего каталога и его подкаталогов
        -- inner join properties'
    );

    DB::query('
        CREATE TEMPORARY TABLE f_products (
            product_id int unsigned,
            UNIQUE KEY product_id (product_id)
        ) ENGINE=MyISAM ROW_FORMAT=FIXED
    ');


    //здесь, если есть фильтр по цене, то удаляем всё из
    //таблицы filters, где цена не совпадает
    //delete from filters where product_id ...
    //where price < filter_min_price or price > filter_max_price


    //site.com/catalog/tv/?filters=2-3,4-8,...
    //2 и 4 - property_id; 3 и 8 - property_value_id
    //$filters = [
    //    ['key' => 2, 'value' => 3],
    //    ['key' => 4, 'value' => 8],
    //];
    //проходим по всем фильтрам в запросе
    foreach ($filters as $filter)
    {
        $property_id = $filter['key'];
        $value_id = $filter['value'];
        //выбираем во временную таблицу продукты, подходящие
        //под данный фильтр
        DB::query('
            INSERT IGNORE INTO f_products
            SELECT /*! SQL_NO_CACHE */ product_id
            FROM filters
            WHERE property_id = ?i
                AND property_value_id = ?i
        ', [
            $property_id,
            $value_id,
        ]);
        //увеличиваем счётчик в таблице фильтров тем товарам,
        //которые были выбраны во временную таблицу
        DB::query('
            UPDATE filters
            INNER JOIN f_products ON filters.product_id = f_products.product_id
            SET status = status + 1
        ');

        //очищаем временную таблицу
        DB::query('DELETE FROM f_products');
    }

    //на выходе, по идее, в таблице filters, продукты, у которых status = count($filters),
    //это как раз те, что удовлетворяют условию
}
 
Последнее редактирование:

Linker

Новичок
Breeze, точно! Портированный Zend_Search_Lucene наверняка будет "тяжеловат", а вот SOLR на Java в виде отдельной инстанции под Tomcat можно попробовать.
 

Breeze

goshogun
Команда форума
Партнер клуба
Эластик же тоже java =) Но какой-то прожорливый он, в стандартной инсталляции всю раму сожрал на vds.
С solr такого не случилось и в комплекте jetty идет, так что томкат в общем-то и не нужен.

Вообще для мелких проектов пофиг кто.
 
Сверху