Pull to refresh

Comments 20

Выбрал в голосовании воздержаться, хотя надо бы 'Кому это надо'?

Лично я в принципе против высокоуровневых фреймворков поверх базы данных, достаточно универсальной прослойки типа PDO. Любой фреймворк ограничивает своего пользователя тем функционалом, что в нем заложен, и зачастую получается классическое
как согреть воду в чайнике: если в чайнике есть вода — вылить ее и перейти к готовому алгоритму
Да, бывают случаи, когда твоя задача в фреймворке решена и код получается красивым и компактным, но к сожалению не часто. Я еще молчу про накладные расходы, которые появляются из-за реализации. В sql такой подход сильно роняет производительность, особенно на синхронизацию данных.

Помним, что DDL язык 'почти ничем не отличается' от DML в sql, те же строки в программе, их так же можно генерировать, за исключением отсутствия транзакций и некоторых проблем с репликацией. Если тебе из программы нужно создать какой то объект со своей структурой, то и создавай все необходимое прямо в таблицах — нужно новое поле — добавляй либо поле либо физически таблицу с FK (в зависимости от вида нагрузки и объемов данных), заложи логику в интерфейс и получишь то же самое но эффективнее на порядок и уж точно красивее и понятнее, а главное, вместе с этим получаешь возможность возложить контроль целостности над своими данными на sql сервер, эффективное использование индексов и просто работать с такой базой напрямую удобнее чем с кашей из данных в паре таблиц

Всегда нужно соблюдать баланс между универсальными структурами и строгой типизацией. И понимать на какие жертвы вы идете.

Посмотрел бы я что вам ДБА ответит если вы предложите в рантайме по запросу пользователя менять схему БД. EAV — это неплохая концепция по своей сути для подобных задач, а если обвесить это еще сверху материализованными представлениями, то вообще неплохо смотрится и вполне себе эффективно для малых и средних проектов, коих большинство в интернете кмк.

Посмотрел бы я что вам ДБА ответит если вы предложите в рантайме по запросу пользователя менять схему БД

Почему бы и нет, если вести работу с товарным каталогом в отдельном загончике - отдельной БД. Тем более, что схема БД меняется не так часто и не любым пользователем. Состав характеристик товара меняется только при поступлении "новой коллекции", даже не каждый месяц.

Мне кажется для большинства проектов в интернете, для товарного каталога, достаточно Битрикса или Magento / OpenCart.

Потому что Битрикс тоже умеет хранить категорию в отдельной таблице, конечно это делается через цать дополнительных запросов, и у Битрикса это не так быстро как в случае библиотеки "Универсальный каталог", но не на столько что бы отказываться от админки и другой инфраструктуры которую предлагает Битрикс.

Я на днях пытался придумать где бы могла пригодиться эта библиотека - "Универсальный каталог" и особо не придумал. Возможно для каких то уникальных проектов, которым не нужна интеграция с бухгалтерией. Или там где людям нужно работать с произвольными данными, но лень заморачиваться на Битрикс.

Потому что Битрикс тоже умеет хранить категорию в отдельной таблице, конечно это делается через цать дополнительных запросов, и у Битрикса это не так быстро

А откуда вы знаете насколько Битрикс медленее? В нем как раз таки и реализован EAV : b_iblock_element (сущности), b_iblock_property (атрибуты), b_iblock_property_element (значения).

Запрос к категориям делается через 1 join, что мало похоже на "цать дополнительных запросов" :-)

Потому что я это дебажил, и да можно хранить в одной куче, что не быстро работает, а можно в отдельной, что работает быстрей, но тогда, что бы понять в которую выделенную таблицу надо направить запрос, предварительно надо ещё пару запросов выполнить.

Я не говорю что это надо делать пользователю, это скрыто за API библиотеки Битрикса, но эти запросы выполняются, поэтому это будет медленей чем выполнить полтора запроса в случае с "Универсального каталога".

Посмотрите в сторону того как фасетный индекс у Битрикс устроен, вопросы по поводу нескольких куч отпадут)

Где то в начале 200х-ых пилили проект (в забвении) по автоматической генерации целого веб-сервиса с формами просмотра, редактирования и поиска на основе описания структуры (тогда еще на основе xml и xslt, оно 'только появилось' и так красиво выглядело, сейчас бы ни за какие коврижки таким садамаза не занимался).

Генерировалось все — sql код создания (и модификации, но до удаления не дошли, только добавление), php код форм, включая поисковые, поддерживались не только плоские атрибуты с типами (в т.ч. справочники) но и списки М-к-М и даже был задел на более сложные под-формы через html frame но не пришлось… что то похожее шло с oracle, но дико завязанное на их подсистему (дорогую и проприетарную) а тут любая база данных и красивый опенсорс, ведь код можно было включать в свой вручную написанный со своим дизайном.

Это работало но ушло в забвении по независящим от технологий причинам

работать с такой базой напрямую удобнее чем с кашей из данных в паре таблиц

В библиотеке реализованно ровно то что вы предлагаете. Для каждой сущности создаётся отдельная таблица и колонки в эту таблицу добавляются произвольно, по необходимости.

Шаблонный SQL код по работе с данными (DML) и генерации схемы данных (DDL) спрятан в методы, ни какая ORM не применяется. Библиотека это только proof of concept, мне было интересно посмотреть на сколько трудно будет автоматизировать DDL и какое быстродействие будет в итоге.

Исходники открытые, запрета на изменение и применение нет, код можно крутить куда и сколько хочется. Сделать любые оптимизации, любые изменения в алгоритмах.

В итоге как оказалось, генерация DDL это совсем не трудно, и то что получилось работает достаточно быстро. Я не вижу разницы по быстродейтсвию между работой с таблицей сгенерировнной библиотекой и таблицей сгенерированной "миграцией". Если разница и есть, то она не заметна на фоне того сколько времени СУБД выполняет запрос к данным. Плюс минус миллесекунда не имеет значения, когда у нас сами данные вычитываются 20-40 мс.

Мне кажется, вы не поняли смысл этой библиотеки, и считаете её "фреймворком поверх базы данных". Хотя это фреймворк поверх модели EAV, которая как раз-таки при работе с ней вручную совершенно неюзабельна. Не говоря уже о том что в контексте области применения EАV рекомендация "нужно новое поле — добавляй" выглядит довольно глупо. Ну или я бы хотел посмотреть на пример такой БД, которая хранит свойства товаров для классического ассортимента с телефонами, телевизорами, чайниками и флешками не в "паре таблиц", а следуя принципу "нужно поле — добавь его".

Битрик так делает. Надо поле в карточке товара ? Добавь его ! До какой то версии всё это хранилось в одной большой таблице, гоад тр иточно как можно отдельные категории хранить в отдельных таблицах.

На сколько ни будь существенных объёмах без кеширования тормозит. Но у крупных интернет магазинов хватает экспертизы добиться от Битрикса хорошей производительности.

Работать с Битриксом конечно не сахар.

Основня задача каталога со стороны покупателя - это быстрый фасетный и полнотекстовый поиск. Как каталог хранит эти данные, покупателю наплевать. С этой точки зрения в статье нет никакой полезной информации.
Сейчас этот код и метрики из предыдущей статьи не говорят ничего.

Если Вы построите запрос на поиск по 5 атрибутам и произвольному тексту одновременно и сраните результаты с аналогиными запросами в Эластике и Сфинксе, то тогда статья будет полезной.

Но что-то мне подсказывет, что после этого, Вы больше не будете разрабытывать библиотеку.

У меня не было цели сделать data access layer для eCommerce платформы. Моей целью было посмотреть как можно ускорить EAV.

Мои выводы:

  1. ускорить можно

  2. ускорить не сложно

Я вам не навязываю использование этой библиотеки, я с вами только делюсь результатами своего исследования.

На мой вкус результаты позитивные.

Полнотектовый поиск с опечатками Эластика или Сфинкса я не собираюсь повторять а уж тем более превосходить.

По части поиска по атрибутам, я всеми руками и ногами только за. Напишите бенчмарк на NoSQL или jsonb, я подготовлю данные, получим метрики, будет о чём поговорить и о чём подумать.

У меня все каникулы ушли на то что бы написать бенчмарк для EAV. Я считаю что я свою часть работы сделал. Если хотите свои слова подтвердить делом, то пожалуйста, у вас есть такая возможность.

Буду очень рад.

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

P.S.

Где реальные запросы к БД?

Реальные запросы к бд в других статьях из серии, ссылки можно у меня в профиле найти.

Прототипы запросов можно посмореть в статьях: https://habr.com/ru/post/323498/ https://habr.com/ru/post/343776/ . В действительности запросы попроще на пару джоинов.

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

Все девять мне дебажить лень, если каких то парочку, то могу посмотреть за вас.

Данные технологии были актуальные лет 5 назад. Сейчас в СУБД можно использовать колонки не ограниченой длинны плюс позволяют навешивать на них идексы. Дальше выбор на вкус JSON, XML, AVRO и т.п. Как показывает практика СУБД надо подбирать под данные. Подобные инструменты хороши для молодых команд на начальном этапе реализовать идею не погружаясь в подробности. 1с так живет и развивается.

Меня смущает, что, судя по всему, все сущности привязываются по строковому значению. Ну вот пример

$attribute3 = $operator->createKind(
    'дискретный_числовой_атрибут',
    'number',
    'discrete',
);

/* зададим новый атрибут для сущности */
$operator->attachKind(
    'сущность',
    'дискретный_числовой_атрибут',
);

/* объект для управления данными сущности */
$schema = new Schema($pdo, 'сущность');

Почему, например, attachKind не принимает вместо строковых значений две сущности, в духе

/* зададим новый атрибут для сущности */
$operator->attachKind(
    entity,
    attribute3
);

А сейчас, получается, что createKind возвращает объект, который нигде дальше не используется.

Ну и вдогонку, чтоб IDE помогала, не лучше ли сделать addAttribute(Entity, Attribute) вместо attachKind(string, string)?
И так по всему коду, завязка на строки. Можете сказать, почему так?

И ещё вот такая штука смутила

$wordsFilter = new DiscreteFilter(
    'дискретный_символьный_атрибут',
    ['красный']
);

Почему фильтр создаётся для конкретного атрибута? По идее, фильтр не должен быть привязан к атрибуту, а только к его типу, чтоб его можно было переюзать.
Сейчас, насколько я понял, если у меня будет два строковых атрибута и мне нужно будет их фильтрануть по одинаковому условию, то придётся создавать два одинаковых фильтра.

Почему бы не сделать фильтр вида new DiscreteFilter(['красный'])?

Спасибо за вопросы.

Библиотека это наследние разработки 2016 года.

Изначально был потрачен месяц на низкоуровневый код и у меня было время на подумать, и написать "по красоте".

Позже в несколько заходов были добавлены абстракции и на их добавление уходили считанные часы, как вы можете догадаться, на красоту времени не было, важно было добавить фукционал.

Поэтому в коде где то используются DTO, где то строки. Просто ещё не успел причесать код.

Сейчас я считаю, что DTO это представление данных для внутренней работы библиотеки и на высоком уровне, для внешних интерфейсов нужны строки.

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

Про нейминг, вы вопроса не задали, но я отвечу.

На каждом уровне абстракции свой язык. Атрибуты это низкий уровень, на высоком уровне - характеристики. Характеристика слово длинное, поэтому используется короткий аналог - kind.

Аналогично с глаголами, на каждом уровне свой: insert для уровня sql; combine скомбинировать для уровня хэлпера / attach - стыковать для верхнего уровня внешнего интерфейса.

Про фильтры.

API библиотеки разработано исходя из работы с фронт-эндом. Мы на страничке категории должны отобразить список доступных фильтров по каждой характеристике отдельно, включая допустимые значения фильтров по этой характеристике.

Агалогично фильтры возвращаются на бек-энд: какая характеристика, какие значения ищем.

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

Фильтры отдаются массивом, поэтому переиспользовать не получиться, надо создавать новый экземпляр на каждое условие отбора.

Переиспользование это отдельный вопрос для холивара. Иммутабельность была заложена в логику, сейчас в каких то классах я хочу её убрать.

Кстати, результат создания нового атрибута $attribute3 = $operator::createKind(..); может быть использован для обновления источника данных: $schema->setup($attribute3);

/ создаём новый атрибут */
$attribute3 = $operator->createKind(
    'дискретный_числовой_атрибут',
    'number',
    'discrete',
);

/* зададим новый атрибут для сущности */
$operator->attachKind(
    'сущность',
    'дискретный_числовой_атрибут',
);

/* Добавим колонку для нового атрибута */
$schema->setup($attribute3);
/* в таблице добавиться новая колонка */

В примере кода это есть, но сначала идёт пример для материализованного представления, и там DTO нового атрибута можно не передавать, а потом идёт пример для таблицы.

Пользоваться - а зачем, когда есть готовые MDM системы? Каталог - это не только информация о сущностях и их атрибутах, но и общирная функциональность, необходимая для управления, включая процессы, хранение истории, DQ, etc.

Sign up to leave a comment.

Articles