Active Record: 从 PostgreSQL 数组中删除元素

5
假设有一个名为Job的活动记录模型,其中包含一个数组列follower_ids。我已经创建了一个范围(scope),可以获取用户关注的所有工作:
# 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)

有没有一种方法可以使用数据库删除follower_ids列中的特定元素(即不通过循环遍历活动记录对象并手动调用delete / save来完成)?
例如,希望能够像这样做:Job.followed_by(1).remove_follower(1),以便在一个查询中从所有这些工作的follower_ids中删除id = 1的用户。
2个回答

4
我使用了 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)

感谢您提供这段代码片段,它可能会提供一些有限的、即时的帮助。一个适当的解释将极大地提高其长期价值,因为它可以展示为什么这是一个好的问题解决方案,并使其对未来读者有其他类似问题的人更有用。请[编辑]您的答案以添加一些解释,包括您所做的假设。 - Ismael Padilla

2
我认为这实际上是一个 XY 问题,原因在于您在使用数组列时应该使用连接表。
不使用数组的主要原因有:
- 如果用户被删除,您将不得不更新作业表中的每一行,而不是只需通过级联或删除回调来删除连接表中的行。 - 没有引用完整性。 - 查询难以阅读。它真的只是比逗号分隔的字符串略微好一些。 - 连接并不真的很昂贵。 "过早地优化是万恶之源"。 - 您不能在数组列上使用 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

要获取未跟随的工作,您可以在关注中进行外部连接,其中用户ID为nil或不等于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:选项自动执行此操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接