Что-то сложнее блога с комментариями и категориями никогда не проектировал. Обучения ради интересуют вопросы проектирования.
Как правильно спроектировать базу данных для интернет-магазина с множеством категорий, в которых должна быть разная система фильтров так как товары имеют различные атрибуты, иногда пересекаются между категориями (вес товара), а иногда нет.
Самый примитив как я понял это создание под каждую категорию своей таблицы, где столбцы это атрибуты.
Но тогда, товар не может присутствовать в нескольких категориях, а если пересекаются бренды, то чтобы сделать выборку по бренду всех товаров, нужно лезть во все таблицы и фильтровать по бренду? В общем здесь пока больше вопросов.
Другой нагуглил вариант, EAV - Entity Atribute Value. Вроде всё логично и правильно, категория может являться атрибутом, название категории значением, тоже самое с брендами, бренд атрибут, название это значение. По этой модели работает Magento CMS, количество таблиц там намного больше конечно. Также гуглится что это анти-паттерн и очень плохо и медленно.
Подскажите какие есть варианты проектирование е-коммерса, в гугле и в гите встречаю либо очень сложные для понимания варианты либо примитив где товар нельзя добавлять в несколько категорий. Хотелось бы понимать плюсы и минусы того или иного варианта. Не то чтобы я хочу создать Amazon, но обучения ради.
И насколько это сложно или легко будет использовать в ORM Laravel.
Может быть какие-то уроки по разработке более-менее гибкого и-магаза в Laravel.
Монго хороша, если вы собираетесь уходить в кластер в обозримом будущем.
Иначе - смысла в ней нет.
А вот JSONB в PostgreSQL - как раз подходящее решение под эту задачу.
m0nym, Mongo хорошо решает поставленную задачу. Поэтому её тоже нужно советовать. Это всего лишь БД, а не rocket science. Не нужно боятся чего то нового.
У postgres очень специфичный синтаксис для jsonb поэтому я бы из них выбрал mongo для NoSQL. По скорости работы вроде там примерно одинаково.
EAV - штука дико медленная. Для хранения и редактирования - удобна, логична.
Но отдавать из нее информацию на запросы пользователя - это плохо, медленно.
Для работы с фильтрами лучше подходят специализированные СУБД фасеточного поиска, а не классические реляционные СУБД типа MySQL
Фасеточный (а заодно и полнотекстовый) поиск - это, к примеру, SphinxSearch, ManticoreSearch, ElasticSearch и т.п.
Пожалуй это очень сложно для новичка. Я понимаю как работает Sphinx, но по-моему он больше подходит для поиска, а не для фильтра и-магазина.
По какому принципу проектировать базу так и не ясно из этого. Всё что пока понятно это то, что фильтрация с распределением таблиц по EAV, это JOINы и поэтому это медленно.
Nikolino, по большей части вам хватит классической нормальной (от слова 3 нормальная форма) структуры, а это EAV
Все остальное -- индивидуальные решения индивидуальных задач...
Если вы хотите и ими позаниматься, вам нужно столкнуться с похожей проблемой, ну или поставить ее самому себе
Пожалуй это очень сложно для новичка. Я понимаю как работает Sphinx, но по-моему он больше подходит для поиска, а не для фильтра и-магазина.
1) Ничуть не сложнее SQL. Если не будешь стремиться преодолевать сложности - и не научишься ничего. Да тут и не сложности вовсе. Имхо, проще, чем SQL.
2) Более чем подходит. Как ни странно, но алгоритмически и фасеточный и полнотекстовый поиск - по сути одно и то же, несмотря на разницу в названиях.
Как работает алгоритм полнотекстового поиска:
1) Исходный текст разбивается на слова, от слова убираются окончания (то есть вместо "красный", "красная", "красному" - одно слово "красн"). Получается т.н. "терм".
2) Для каждого терма строится индекс вида "0101" (только длиннее гораздо), где каждый 0 и 1 в каждой позиции соответствуют какому-то вашем случае товару (в терминах полнотекстовых систем это обозначается словом "документ", но в вашем случае "документ" = "товар").
3) Если нужно найти где встречается фраза "красные вурдалаки" - ищется для терма "красн" (это "0101...") и для терма "вуралак" (это, к примеру, "1001..."). Берется пересечение и получаем ответ "0001...", то есть товар №4.
4) В случае фильтров - работает ровно так же. Только "терм" = "значение фильтра".
Поэтому что для полнотекстового, что для фасеточного эти системы пригодны одинаково. Алгоритм будет идентичен.
1), 2), 3), 4) - представлять надо, но знать не нужно. Это все делает сам SphinxSearch и пр.
Схема приведена упрощенно. В развитых системах есть индексация и чисел (то есть можно делать фильтр по числу и искать числовые диапазоны").
Главное - это все работает мгновенно даже для очень сложных фильтров.
Так как там всего навсего 2 операции: поиск по B-дереву (найти битовую маску для терма) и логические AND/OR по битовой маске.
В можете сочетать и поиск с подсказками на лету и фильтрацию - и это будет наааааааамного быстрее чем в SQL, а уж тем более с EAV.
Всё что пока понятно это то, что фильтрация с распределением таблиц по EAV, это JOINы и поэтому это медленно.
Если вам всенепременно хочется в SQL, то нужно денормализовать". То есть свести все к минимуму таблиц.
Типа такого:
Название товара Фильтр №1 Фильтр №2 Фильтр №3
Название или ID товара №1 Истина Ложь Истина
Название или ID товара №2 Ложь Ложь Истина
Название или ID товара №3 Ложь Истина Ложь
По колонкам "Фильтр №X" - естественно, нужен индекс.
Однако для СУБД со схемой, каковой является все реляционные SQL-СУБД - это неудобно.
Впрочем, в PostgreSQL, к примеру, есть тип данных JSONB, который позволяет сделать schemaless-решение. Причем, что важно - JSONB будет проиндексирован Постгресом.
Не путать с другими типами JSON, именно JSONB и именно в PostgreSQL.
Сомневаюсь, что в MySQL есть проиндексированный JSON.
JhaoDa, Полагаю, можно использую полнотекстовый поиск у MySQL как то и там умудриться.
Если он гибконастраиваемый этот поиск.
Нужно только отключить стемминг (фильтры же не склоняются и не спрягаются).
Например так:
ИД или имя товара Фильтр
Товар №1 цветзеленый размербольшой назначениедлякухни
Товар №2 материалдеревянный размербольшой назначениедлякухни
Товар №3 материалметаллический цветголубой
То есть запихать все фильтры в одно поле и понадеется, что полнотекстовый нормально отработает в MySQL.
Важно:
1) стемминг (отсечение окончаний слов) нужно бы отключить
2) сложно-составные фильтры нужно писать одним словом, чтобы токенизер полнотекстовой системы их не разбил на отдельные токены. Ну или настроить токенизер на восприятие только, к примеру, запятых и разделять ключевые слова запятыми.
Вряд ли это правильный паттерн, но я делал так:
В таблице куча столбцов вида v1, v2, v3, v4 (varchar), i1,i2,i3 (int)......
Для каждой категории массив связок типа 'color' => 'v2', 'radius' => 'i1'
В модельках через сеттеры, геттеры и скоупы это все преобразуется в человекочитаемый вид.
Это вполне себе нормальное по производительности решение, если количество фильтров заранее известно и жестко фиксировано и гарантировано не будет меняться.
Растишка, опишите пожалуйста подробнее. У вас две таблицы: категории и товары, перечисленные вами столбцы находятся в таблице категории? А значения атрибутов в таблице товаров?
ITEM_ATTRIBUTE
id | item_id | attribute_id | value (или value_id если атрибуты определены)
1 | 1 | 1 | XL
2 | 2 | 1 | M
3 | 3 | 2 | 34
4 | 3 | 3 | 32
На практике, скорее всего, пригодятся также полиморфные связи, когда в одном столбце могут храниться ссылки на строки, принадлежащие разным таблицам. Для этого в исходную таблицу добавляется столбец type, где хранится тип (к какой таблице обращаться), а в другом столбце хранится id строки в этой таблице.
В таблицу ITEM_ATTRIBUTE уже можно добавить столбец type, чтобы хранить и ссылки на строки в разных таблицах, где перечисляются значения атрибутов (например добавить таблицу TOP_SIZE_VALUE и пихать не строку, а ссылку) и строки.