Активная запись: удалить элемент из массива PostgreSQL - PullRequest
2 голосов
/ 23 января 2020

Предположим, есть активная модель записи с именем Job, в которой есть столбец массива follower_ids. Я уже создал область, которая позволяет извлекать все задания, за которыми следует пользователь:

# Returns all jobs followed by the specified user id
def self.followed_by(user_id)
  where(
    Arel::Nodes::InfixOperation.new(
      '@>',
      Job.arel_table[:follower_ids],
      Arel.sql("ARRAY[#{user_id}]::bigint[]")
    )
  )
end
# Retrieve all jobs followed by user with id=1
Job.followed_by(1)

Есть ли способ удалить указанные c элементы из столбца follower_ids с помощью базы данных ( то есть, не перебирая объекты активной записи и вручную вызывая delete / save для каждого из них)?

Например, было бы неплохо сделать что-то вроде Job.followed_by(1).remove_follower(1), чтобы удалить пользователя с id=1 из всех этих заданий 'follower_ids всего одним запросом.

Ответы [ 2 ]

3 голосов
/ 23 января 2020

Я закончил, используя функцию PostgreSQL array_remove , которая позволяет удалить значение из массива следующим образом:

user_id = 1
update_query = <<~SQL
  follower_ids = ARRAY_REMOVE(follower_ids, :user_id::bigint),
SQL
sql = ActiveRecord::Base.sanitize_sql([update_query, { user_id: user_id }])
Job.followed_by(user_id).update_all(sql)
2 голосов
/ 23 января 2020

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

Основные причины, по которым вы не хотите использовать массив, это :

  • Если пользователь удален, вам придется обновлять каждую строку в таблице заданий, а не просто удалять строки в соединительной таблице с помощью каскада или обратного вызова удаления.
  • Нет ссылок целостность.
  • Ужасные нечитаемые запросы. На самом деле это всего лишь незначительный шаг вперед от строки, разделенной запятыми.
  • Объединения не так уж дороги. «Преждевременная оптимизация - это root всего зла».
  • Нельзя использовать ассоциации ActiveRecord со столбцами массива.

Создать модель соединения с помощью rails g model following user:references job:references. А затем настройте связи:

class Job < ApplicationRecord
  has_many :followings
  has_many :followers,
    through: :followings,
    source: :user
end

class User < ApplicationRecord
  has_many :followings
  has_many :followed_jobs,
    source: :job,
    through: :followings,
    class_name: 'Job'
end

Чтобы выбрать задания, за которыми следует пользователь, просто выполните внутреннее объединение:

user.followed_jobs

Чтобы получить задания, которые не выполняются, вы выполняете внешнее объединение в следующих случаях, когда идентификатор пользователя равен нулю или не равен user_id.

fui = Following.arel_table[:user_id]
Job.left_joins(:followings)
   .where(fui.eq(nil).or(fui.not_eq(1)))

Если вы хотите отменить задание, просто удалите строку из followings:

Following.find_by(job: job, user: user).destroy
# or 
job.followings.find_by(user: user).destroy
# or
user.followings.find_by(job: job).destroy

Вы можете автоматически сделать это, когда задание или пользователь уничтожены с опцией dependent:.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...