ActiveRecord 通过两个关联加入

ActiveRecord join through two associations

鉴于存在两种可能的关联,我如何获得包含特定 User 的所有唯一 Jobs 的 ActiveRecord 集合?

class User
 has_many :assignments
 has_many :jobs, through: assignments
 has_many :events
 has_many :jobs_via_events, through: :events, source: :jobs
end
class Assignment
 belongs_to :user
 belongs_to :job
end
class Event
 belongs_to :user
 belongs_to :job
end
class Job
 has_many :assignments
 has_many :events
end

到目前为止,我能想到的最好的方法是使用多个连接,但它并没有返回正确的结果:

Job.joins("INNER JOIN assignments a ON jobs.id = a.job_id").joins("INNER JOIN events e ON jobs.id = e.job_id").where("a.user_id = ? OR e.user_id = ?", userid, userid)

有什么建议吗?

一种简单的方法是先收集所有作业 ID,然后获取这些 ID 的所有作业。

我们首先要求一个用户的所有工作 ID 的数组:

event_job_ids = Event.where(user_id: user).select(:job_id).map(&:job_id) 
assignment_job_ids = Assignment.where(user_id: user).select(:job_id).map(&:job_id) 
all_job_ids = (event_job_ids + assignment_job_ids).uniq  

请注意,根据您使用的 rails 版本,最好将 select(:job_id).map(&:job_id) 替换为 .pluck(:job_id)(这应该更有效,但 select.map

获取组装后的 id 的作业就很简单了:

 jobs = Job.where(id: all_job_ids)

这种幼稚的做法,我们怎么能赞成呢? 如果我要写一个查询,我会写类似下面的内容

 select * from jobs 
 where id in (
   select job_id from assignments where user_id=#{user_id}
   union
   select job_id from events where user_id=#{user_id}
 )

那么我们如何将其转换为作用域?

 class User

   def all_jobs 
     sql = <<-SQL
       select * from jobs 
       where id in (
         select job_id from assignments where user_id=#{self.id}
         union
         select job_id from events where user_id=#{self.id}
       )
     SQL  
     Job.find_by_sql(sql)
   end 

当然这是未经测试的代码,但这应该能让您入门?