ruby-on-rails - 通过查询将 has_many 和 has_many 合并在一起

标签 ruby-on-rails ruby activerecord arel

这看起来是一个典型的问题,但是很难搜索。

我想选择用户通过 has_many 拥有的项目以及用户通过 has_many through 关联的项目。

考虑以下模型:

class User < ActiveRecord::Base
  has_many :projects,
           inverse_of: :owner

  has_many :project_associations,
           class_name: 'ProjectUser',
           inverse_of: :user

  has_many :associated_projects,
           through: :project_associations,
           source: :project
end

class Project < ActiveRecord::Base
  belongs_to :owner,
             class_name: 'User',
             foreign_key: :owner_id,
             inverse_of: :projects

  has_many :user_associations,
           class_name: 'ProjectUser',
           inverse_of: :project

  has_many :associated_users,
           through: :user_associations,
           source: :user
end

class ProjectUser < ActiveRecord::Base
  belongs_to :project,
             inverse_of: :user_associations

  belongs_to :user,
             inverse_of: :project_associations
end

使用多个查询来执行此操作很简单:

user = User.find(1)
all_projects = user.projects + user.associated_projects

但我怀疑它可以使用 Arel 进行优化到单个查询中。


编辑:

我第一次尝试使用 find_by_sql 方法解决方案是:

Project.find_by_sql([
  'SELECT "projects".* FROM "projects" ' \
  'INNER JOIN "project_users" ' \
  'ON "projects"."id" = "project_users"."project_id" ' \
  'WHERE "project_users"."user_id" = :user_id ' \
  'OR "projects"."owner_id" = :user_id',
  { user_id: 1 }
])

这会产生我期望的结果,但我想避免使用 find_by_sql 而是让 Arel 构建 SQL。

最佳答案

这应该有效:

Project.joins(:user_associations)
       .where("projects.owner_id = ? OR project_users.user_id = ?", id, id)

您可以将其放入名为 User#all_projects 的方法中。

您还可以使用 UNION ,尽管这看起来有点矫枉过正。

只有一个警告:根据我的经验,这种连接模型的两条备用路径的结构(其中一条路径是连接表,另一条路径是直接外键)会导致很多麻烦。您必须始终处理这两种情况,并且在复杂的查询中,OR 可能会让数据库查询规划器感到困惑。如果可以的话,您可能需要重新考虑。我认为,如果您删除 owner_id 列并使用 project_users 上的 role 列来识别所有者,您的麻烦就会减少。

编辑或在阿雷尔:

Project.joins(:user_associations)
       .where(Project.arel_table[:owner_id].eq(id).or(
              ProjectUser.arel_table[:user_id].eq(id)))

关于ruby-on-rails - 通过查询将 has_many 和 has_many 合并在一起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36127671/

相关文章:

ruby-on-rails - Rails 3. 与 created_at 进行日期比较

ruby - 为什么 `x =! 5` 返回 false?

ruby - 什么是 Ruby File.open 模式和选项?

ruby-on-rails - 如何访问 ruby​​ on rails rvm install 生成的 sqlite3 表

ruby-on-rails - 没有 class_name 的 FactoryBot 命名空间模型

ruby-on-rails - Rails 项目 : NoMethodError: undefined method `identifier' for String

ruby-on-rails - Mongoid动态改变主机

html - 如何在每次请求时自动预编译 css.erb 文件

ruby-on-rails - RVM稳定版本的安装错误

ruby-on-rails - 在聚合函数之前对 ActiveRecord 对象执行行操作