编写一个没有 `find_by_sql` 的 Arel 内连接查询

Write an Arel inner join query without `find_by_sql`

在一个 Rails 5 项目中,我有一个查询方法用于当前使用 SQL 字符串的关联,我正在尝试将其重构为仅使用 Arel。我想知道是否可以在不使用 to_sqlfind_by_sql.

的情况下编写整个查询方法

查询是一个 INNER_JOIN 和一个连接 table。

这是旧的 SQL 字符串方法:

class User < AbstractModel
  ...

  # Return an Array of Projects that this User is an admin for.
  def projects_admin
    @projects_admin ||= Project.find_by_sql(%(
      SELECT projects.* FROM projects, user_groups_users
      WHERE projects.admin_group_id = user_groups_users.user_group_id
        AND user_groups_users.user_id = #{id}
    ))
  end

这是我的 Arel 重写,得到相同的查询结果:

  # Return an Array of Projects that this User is an admin for.
  def projects_admin
    projects = Project.arel_table
    # for join tables with no model, need to create an Arel::Table object
    # so we can use Arel methods on it, eg access columns
    u_g_users = Arel::Table.new("user_groups_users")

    arel = projects.project(Arel.star).join(u_g_users).
           on(projects[:admin_group_id].eq(u_g_users[:user_group_id]).
                 and(u_g_users[:user_id].eq(id)))

    # is there a way to write this without going to_sql and back?
    Project.find_by_sql(arel.to_sql)
  end

我想做的是更改我的新 projects_admin 方法的最后一行,以便它直接调用关系,而无需在 arel.to_sql 上使用 find_by_sql

如果我只是 return arel,它不是我期望的 Project 数组。

但也许这是一种荒谬的愿望,必须这样说。许多例子表明情况就是如此。

我已经尝试了所有我能找到的例子,但我认为问题的根源是我误解了连接如何与我生成的对象 class 交互,Arel::SelectManager.

这里是有问题的模型关联。 “user_groups_users”是“user_groups”和“users”之间的联接table - 这里只有“User”、“UserGroup”和“Project”模型。

class User < AbstractModel
  has_many :projects_created, class_name: "Project"
  has_and_belongs_to_many :user_groups,
                          class_name: "UserGroup",
                          join_table: "user_groups_users"
class UserGroup < AbstractModel
  has_and_belongs_to_many :users
  has_one :project
  has_one :admin_project, class_name: "Project", foreign_key: "admin_group_id"
class Project < AbstractModel
  belongs_to :admin_group, class_name: "UserGroup",
                           foreign_key: "admin_group_id"
  belongs_to :user
  belongs_to :user_group

我相信您忽略了一个更简单的解决方案,即添加间接关联并执行 LEFT JOIN:

class Project < AbstractModel
  belongs_to :admin_group, class_name: "UserGroup",
                           foreign_key: "admin_group_id"
  has_many :admin_group_users, 
    through: :admin_group,
    source: :users # the name of the assocation on UserGroup
  # ...
end
Project.joins(:admin_group_users)
       .where(users: { id: id })