Информация о пользователе

Ник: Vorchun
Страна: Russia
Статус: Активен
С нами с:
18.01.2019 10:31
Лайки
Лайки: +0
Лайки
0
Дизлайки
0
Я лайкал: +0
Я лайкал
0
Я дизлайкал
0
Комментарии
Комментировал: 1
Посты
1
Комментарии
0
Ответы: 1
Посты
0
Комментарии
1
Rails + Postgres + bindings
Vorchun писал(а):
Так и не понял в чем проблема. Чем запросы вида: News.where(News.arel_table[:order_date].between(Date.current-2.year..Date.current-1.year)).where.not(owner_id: nil).pluck(:id) не подходят? ведь они будут преобразованы в один sql запрос. При желании пожно и join таблице сделать и еще where добавить.
В данном примитивном примере все вы правильно написали. Хотя такой запрос с ходу не читается и нужно прилично напрячься. А теперь попробуйте собрать вот такой запрос:
SELECT (SELECT json_agg(row_to_json(agg_notes))
FROM (SELECT n.id, n.title, n.body, to_char(n.created_at, 'DD.MM.YYYY HH24:MI') as created
FROM data.notes n
WHERE n.user_id = u.id) agg_notes) as notes,
(SELECT json_agg(row_to_json(agg_projects))
FROM (SELECT p.id,
p.name,
p.description,
to_char(p.created_at, 'DD.MM.YYYY HH24:MI') as created,
(SELECT true WHERE EXISTS(SELECT p1.id FROM data.projects p1
WHERE p1.id = p.id AND p1.report IS NOT NULL)) as report
FROM data.projects p
WHERE p.user_id = u.id) agg_projects) as projects,
(SELECT json_agg(row_to_json(agg_contacts))
FROM (SELECT c.id, c.message, c.ip, to_char(c.created_at, 'DD.MM.YYYY HH24:MI') as created
FROM data.contacts c
WHERE c.user_id = u.id) agg_contacts) as contacts
FROM data.users u
WHERE u.id = :user_id;
И прошу заметить, это нисколько несложный запрос. А очень даже самый простой, по сравнению с теми, которые встречаются в проекте (по 50 строк и более). А есть запросы, где нужно вставить с десяток биндингов.

А есть еще куча запросов с такими вещами, как полнотекстовый поиск to_tsvector, to_tsquery и т.д., работа с полями (включая логику поиска) по пересечению значений из JSONB по массиву и так далее.

Никакая ORM просто не сможет собрать такой запрос, потратит кучу серверного времени на это, а что еще хуже, то может и вовсе неправильно его собрать, делая вид, что все хорошо, что приведет к скрытым и тяжело отлавливаемым глюкам. AR же очень круто и легко использовать для загрузки и сохранения разных форм. А вот для логики в БД она вообще ни разу не подходит.

При моем же подходе, я создаю папку репозитарий, где храню статические классы с методами, которые возвращают подготовленный запрос. Например:
# Repository for mailings
class MailingRepository
# Get all emails with LIMIT and OFFSET
def self.emails_limit
'SELECT
e.id,
e.email,
e.status,
e.action,
e.updated_at::timestamp,
(SELECT json_agg(row_to_json(agg_groups))
FROM (SELECT eg.group_id
FROM mailing.emails_in_group eg
WHERE eg.email_id = e.id) agg_groups) as groups
FROM mailing.emails e
ORDER BY e.id DESC LIMIT :limit::int OFFSET :offset::int;'
end
end
Этот запрос забирает с базы все адреса (с лимитом) и создает поле с JSON, куда поместятся все группы, к которым привязан адрес.

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

Чем такой подход хорош. Все ваши запросы полностью отделены от кода и логики. Вы в любой момент можете его просмотреть и отрефакторить. А сам код не перегружен десятками методов из AR, что делает его более чистым и читаемым.

Почему я так делаю. Все просто. Я более 15 лет занимаюсь разработкой и уже давно дошел до того состояния просвещения, когда стал четко понимать, что все рекомендации и бест вэй от разных ЯП и фреймворков - это не более, чем дань моде и трендам. Самое главное - это архитектура. И она должна быть слабосвязанная, разбитая на разные законченные логические части (модули/классы). С простым и читаемым кодом, и при использовании фреймворков нужно по максимуму использовать возможности нативного ЯП, а не фреймворка, так как это даст в последствии возможность перенести свой код на другую платформу без потрясений.

Например, завтра понадобится перенести проект с Рельсов на что-то другое, и у меня с этим проблем не возникнет, так как вся логика запросов написана на нативном Postgres. Тем же, кто полностью отдался AR, придумывая для нее тонны костылей, поимеет с миграцией громадный геморрой, в виде полного переписывания всех запросов под новые реальности платформы.

У такого подхода есть и недостатки. Это необходимость глубокого понимания работы БД, и большой практический опыт построения слабосвязанных модульных систем (то есть глубокое понимание архитектуры разных процессов), что очень сильно повышает порог вхождения. Но тут уже все будет зависеть от уровня и сложности построения проекта. Если это новостной сайт или блог, то может можно и не запарываться с этим. А вот если это сложный аналитический комплекс, с кучей математических вычислений, то без построения абстрактной архитектуры, проект уже через месяц превратится в не читаемый набор из тысяч файлов, где любое исправление в коде может привести к падению всего проекта.

Надеюсь, что я донес свою мысль :)