Для тех, кто не знает, Sequel — это ORM, очень похожий на ActiveRecord, в том смысле, что он также реализует шаблон Active Record. На момент написания статьи ему 9 лет. Я уже писал о том, почему Сиквел — это круто.

Я использую Sequel уже более года и считаю, что он неизменно лучше, чем ActiveRecord. Но это только мое мнение, верно? Нельзя сказать, что один инструмент объективно лучше другого, у каждого инструмента есть свои недостатки.

Ну, иногда просто можно. Что я заметил, так это то, что всякий раз, когда появляется новая блестящая функция ActiveRecord, у Sequel уже есть такая же функция в течение достаточно долгого времени. Все бы ничего, если бы это были единичные случаи, но на самом деле это не так. ActiveRecord, похоже, постоянно изобретает Sequel заново.

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

Что я сделаю, так это познакомлю вас с наиболее заметными обновлениями ActiveRecord и поищу эквиваленты Sequel. Я также сравню время, когда функция появилась в обоих ORM. Я перечислю функции примерно в обратном хронологическом порядке (от самых новых к самым старым), чтобы мы начали со свежих воспоминаний.

АктивРекорд 5

Or

Метод запроса ActiveRecord::Relation#or позволяет использовать оператор ИЛИ (ранее вам приходилось писать строки SQL):

Post.where(id: 1).or(Post.where(id: 2)) # => SELECT * FROM posts WHERE (id = 1) OR (id = 2)

Реализация этой функции потребовала много обсуждений. В конце концов эта функция появилась в ActiveRecord (фиксация), всего на 8 лет отстав от Sequel (код).

Левые соединения

Метод запроса ActiveRecord::Relation#left_joins генерирует ЛЕВОЕ ВНЕШНЕЕ СОЕДИНЕНИЕ (ранее возможное через #eager_load):

User.left_joins(:posts) # => SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"

Функция появилась в ActiveRecord в 2015 году (PR). С другой стороны, Sequel поддерживает все типы JOIN с 2008, а в 2014 были добавлены ассоциативные соединения (commit).

API атрибутов

API атрибутов позволяет указывать/переопределять типы столбцов/аксессоров в ваших моделях, а также запрашивать экземпляры этих типов и многое другое. Шону Гриффину потребовалось около 1 года, чтобы полностью реализовать его.

Трудно указать конкретный эквивалент в Sequel, поскольку область API атрибутов ActiveRecord очень широка. На мой взгляд, примерно таких же возможностей можно добиться с помощью плагинов serialization, serialization_modification_detection, composition, typecast_on_load и defaults_setter.

Взгляды

Метод ActiveRecord::ConnectionAdapters::AbstractAdapter#views, определенный для адаптеров подключения, возвращает массив имен представлений базы данных:

ActiveRecord::Base.connection.views #=> ["recent_posts", "popular_posts", ...]

Сиквел реализовал #views в 2011 году (фиксация), за 4 года до ActiveRecord (фиксация).

Одновременное индексирование

Эта функция PostgreSQL имеет решающее значение для миграции с нулевым временем простоя в больших таблицах. В ActiveRecord индексы добавляются одновременно с 2013 года (фиксация) и одновременно удаляются с 2015 года (фиксация).

Sequel поддерживает одновременное добавление и удаление индексов с 2012 (коммит).

В партиях

ActiveRecord::Relation#in_batches выдает пакеты отношений, подходящие для пакетного обновления или удаления:

Person.in_batches { |people| people.update_all(awesome: true) }

У сиквела нет аналога, потому что нет единственно правильного способа делать пакетные обновления, все зависит от ситуации. Например, следующая реализация Sequel в моих тестах оказалась в 2 раза быстрее, чем ActiveRecord:

(Person.max(:id) / 1000).times do |i| Person.where(id: (i*1000 + 1)..((i+1) * 1000)).update(awesome: true) end

Прерывающие хуки

До Rails 5 возврат false в любом обратном вызове before_* приводил к остановке цепочки обратного вызова. Новая версия удаляет это поведение и требует от вас явного указания на это:

class Person < ActiveRecord::Base before_save do throw(:abort) if some_condition end end

На самом деле это один из редких случаев, когда Sequel добавил эквивалентный метод cancel_action, вдохновленный изменением ActiveRecord.

.

АктивРекорд 4

Адекватная запись

Adequate Record — это набор улучшений производительности в ActiveRecord, которые делают общие вызовы find и find_by, а также некоторые ассоциативные запросы до 2 раз быстрее. Аарон Паттерсон работал над Adequate Record около 3 лет.

Однако запуск теста ORM показывает, что Sequel по-прежнему намного, намного быстрее, чем ActiveRecord, даже после слияния Adequate Record.

Postgres JSON, массив и hstore

В ActiveRecord 4 добавлена ​​поддержка столбцов Postgres JSON, array и hstore, а также автоматическое приведение типов. Глядя на коммиты, мы можем сказать, что ActiveRecord получил эти функции примерно в то же время, что и Sequel (pg_json, pg_array, pg_hstore), то есть примерно в то же время, когда эти функции были добавлены в Postgres. Обратите внимание, что Sequel помимо этого также имеет API для запросов этих типов столбцов (pg_json_ops, pg_array_ops, pg_hstore_ops), что значительно улучшает читабельность.

Обнаружение мутаций

ActiveRecord 4.2+ автоматически обнаруживает изменения значений столбцов на месте и помечает запись как грязную. Сиквел добавил эту функцию через плагин modification_detection после ActiveRecord. Но обратите внимание, что в Sequel это опционально, так что пользователи могут решить, хотят ли они снижения производительности.

Где нет

Конструкция запроса where.not позволяет отрицать предложение where, избавляя от необходимости писать строки SQL:

Person.where.not(name: "John")

Оно было добавлено в 2012 году (коммит), когда эквивалент исключения из Sequel существовал уже 5 лет (код).

Переместить

В 2013 году был добавлен ActiveRecord::Relation#rewhere, позволяющий перезаписывать все существующие условия WHERE новыми:

Person.where(name: "Mr. Anderson").rewhere(name: "Neo")

В Sequel были нефильтрованные, которые удаляют существующие условия WHERE и HAVING, с 2008 года, 5 лет до этого обновления ActiveRecord (фиксация).

перечисление

ActiveRecord::Base#enum был добавлен в ActiveRecord 4.1 (commit), дающий возможность сопоставлять имена с целочисленными столбцами:

class Conversation < ActiveRecord::Base enum status: [:active, :archived] end

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

Автоматические обратные ассоциации

В ActiveRecord 4.1 добавлена ​​функция автоматического обнаружения обратных ассоциаций вместо необходимости всегда использовать :inverse_of (фиксация).

В Sequel это было в основном с тех пор, как в 2008 году были добавлены ассоциации, то есть примерно за 5 лет до обновления ActiveRecord.

Контекстные проверки

Контекстные проверки позволяют передавать символ при проверке и выполнять проверки в зависимости от наличия или отсутствия данного символа.

В Sequel нет этой функции, так как ее наличие в модели — это неприятный запах кода, но проверки на уровне экземпляра в Sequel позволяют вам проверять записи из сервисных объектов, что является гораздо лучшим способом выполнения контекстной проверки.

Улучшения обратимости

ActiveRecord 4.0 улучшил запись обратимых миграций, позволив деструктивным методам, таким как remove_column, быть обратимыми, а также добавив очень удобный метод ActiveRecord::Migration#reversible, позволяющий вам записывать все изменения, не переключаясь вверх и вниз.

Возможностей реверсирования Sequel немного не хватает по сравнению с ActiveRecord, в настоящее время они примерно такие же, как у ActiveRecord до этого изменения.

Нулевое отношение

ActiveRecord 4.0 добавил удобный ActiveRecord::Relation#none, который представляет пустое отношение, эффективно реализуя шаблон нулевого объекта для отношений.

Сиквел добавил плагин null_dataset в качестве вдохновения для функции ActiveRecord.

АктивРекорд 3

ОБЪЯСНЯТЬ

В 2011 году ActiveRecord 3.2 добавил ActiveRecord::Relation#explain для запросов EXPLAIN (коммит). В Sequel поддержка EXPLAIN для Postgres появилась с 2007 года (код), а для MySQL была добавлена ​​лишь позже, в 2012 году (коммит).

Срывать

ActiveRecord 3.2 добавил ActiveRecord::Relation#pluck в 2011 году (фиксация) и добавил поддержку нескольких столбцов в 2012 году (фиксация).

Эквивалент Sequel::Dataset#map существует с 2007 года (код), а поддержка нескольких столбцов была добавлена ​​в 2011 году (фиксация). К моему первоначальному удивлению, карта Sequel не выбирает автоматически только те столбцы, которые ей нужны. Но потом я понял, что на самом деле он обеспечивает большую гибкость, поскольку его можно использовать с пользовательскими SELECT, а не ограничивать только столбцы.

Уникальный

ActiveRecord 3.2 добавил SELECT DISTINCT через ActiveRecord::Relation#uniq в 2011 году (коммит). Sequel имеет эквивалент Sequel::Dataset#distinct с 2007 года (код), на 4 года раньше, чем ActiveRecord.

Обновить столбец

АктивРекорд 3.1. добавлен ActiveRecord::Base#update_column для обновления атрибутов без выполнения проверок или обратных вызовов (коммит). Аналогичное поведение в Sequel, user.this.update(…), на тот момент уже существовало 4 года.

Обратимые миграции

В ActiveRecord 3.1 добавлена ​​поддержка обратимых миграций через изменение (коммит). Вскоре после этого, вдохновленный ActiveRecord, Sequel добавил поддержку обратимых миграций (commit).

Арел

Наконец, мы подошли, вероятно, к самому большому обновлению ActiveRecord: цепочке интерфейсов запросов и извлечению Arel. Для тех, кто не знает, в ActiveRecord до версии 3.0 не было интерфейса запросов с цепочкой.

В Sequel уже был этот цепочный интерфейс запросов, прежде чем Ник Каллен начал работать над Arel (источник), что означает, что он явно был вдохновлен Sequel. Кроме того, построение запросов с помощью Arel выглядит совсем иначе, чем с помощью моделей (возможно, это более неуклюже), в то время как низкоуровневый интерфейс Sequel предоставляет вам точно такой же API, как и с помощью моделей.

Альтернативой Arel для построения сложных запросов является Squeel. Помимо очевидного вдохновения, на которое указывает анаграмма в названии (хотя в README об этом нет упоминания), интерфейс явно имитирует виртуальные блоки строк ​​Sequel.

последствия

В этом подробном обзоре, несмотря на то, что Sequel опережала ActiveRecord в подавляющем большинстве случаев, было несколько случаев, когда ActiveRecord лидировал:

  • Обратимые миграции
  • Прерывающие хуки
  • Обнаружение мутаций
  • перечисление (вроде)
  • Нулевое отношение

Мы видим, что Sequel не отставал от ActiveRecord, но ActiveRecord отставал от Sequel. Обратите внимание, что на GitHub Sequel поддерживает 0 открытых проблем, в то время как ActiveRecord содержит около 300 открытых проблем. Стоит также отметить, что Sequel поддерживается в основном одним разработчиком, а ActiveRecord разрабатывается большей частью команды Rails.

Вывод

Я хочу, чтобы вы подумали об этом. ActiveRecord в основном реализовывал функции, которые уже были в Sequel. Это можно было бы оправдать, если бы у ActiveRecord были какие-то другие преимущества перед Sequel, но я их не вижу. Я не отношу интеграцию с Rails к преимуществам (для этого можно просто сделать sequel-rails), я имею в виду преимущества, которые реально помогают взаимодействовать с базами данных.

Я хотел, чтобы я использовал Sequel с первого дня, вместо того, чтобы начинать с ActiveRecord и постепенно понимать, что Sequel лучше. Единственная причина, по которой ActiveRecord так популярен, заключается в том, что он является частью Rails, а не потому, что он лучше. Есть причина, по которой hanami-model и ROM под капотом используют Sequel, а не ActiveRecord. Мне больно, что в ActiveRecord вложено столько часов разработчика, и я не понимаю, с какой целью; лучший инструмент уже существует и отлично поддерживается. Давайте направим нашу энергию на эти лучшие инструменты.

Первоначально опубликовано на twin.github.io.