使用 Arel 进行嵌套集和连接查询并转换为 ActiveRecord::Relation

Posted

技术标签:

【中文标题】使用 Arel 进行嵌套集和连接查询并转换为 ActiveRecord::Relation【英文标题】:Use Arel for a nested set & join query and convert to ActiveRecord::Relation 【发布时间】:2011-05-28 21:20:16 【问题描述】:

我有一个组织模型(嵌套集)。我有一个人的模型。一个人可以有另一个人作为代理人。一个组织归一个人所有。组织仅对所有者或其代理人可见。

我想检索给定人员可见的所有组织,即。该人拥有或由该人拥有的所有组织,该人是其代表:

o = Arel::Table.new(:organisations)
p = Arel::Table.new(:people)
pd = p.where(p[:id].eq(3).or(p[:deputy_id].eq(3))).project(:id)
op = o.join(p).where(o[:person_id].in(pd)).project("distinct organisations.*)

可能有更好的方法来制定最后一个联接,但我想将人员及其副手的查询与人员及其副手可见的组织的查询分开。

最后一个连接返回一个 Arel::SelectManager(似乎没有任何有用的文档)。

有没有办法将 SelectManager 转换回 ActiveRecord::Relation 以从“组合下的闭包”的整个概念中受益?

如何再次自行加入上述关于组织的查询,以获取个人或其代理人可见的组织的所有后代?我知道 SQL,但总是无法使用 SelectManager 对组织进行自我加入。

【问题讨论】:

【参考方案1】:

似乎没有人接受任何答案,我自己找到了解决方法:

1.将最后一个连接转换为ActiveRecord::Relation

Organisation.where(o[:id].in(op))

唯一的问题是它调用了Arel::SelectManager.to_a,它带有一个弃用警告(也是一个昂贵的操作)。我还没有找到替代方案(怀疑没有,这个弃用警告只是 Arel 中观察到的不一致之一,它在 ActiveRecord 中被采用)。

2.嵌套集合上的自连接以获取所有后代

o = Organisation.scoped.table
op = Organisation.where(o[:person_id].in(Person.self_and_deputies(person_id).project(:id))).arel
o1 = Arel::Table.new(:organisations, :as => "o1")
o2 = Arel::Table.new(:organisations, :as => "o2")
o3 = o1.join(o2).on(
     o1[:lft].gteq(o2[:lft]).and(
     o1[:rgt].lteq(o2[:rgt]))).where(
     o2[:id].in(op)).project("distinct o1.id")
Organisation.where(o[:id].in(o3))

【讨论】:

所以,我想我有一个类似的问题,但在应用解决方案 #2 时遇到了麻烦:***.com/questions/4522746/…【参考方案2】:

您应该能够在Arel::SelectManager 的实例上调用join_sources,该实例可以传递给ActiveRecord::Relation#joins。您的查询将如下所示(未经测试):

o = Organisation.scoped.table
op = Organisation.where(o[:person_id].in(Person.self_and_deputies(person_id).project(:id))).arel
o1 = Arel::Table.new(:organisations, :as => "o1")
o2 = Arel::Table.new(:organisations, :as => "o2")
o3 = Organization.joins(
  o1.join(o2).
    on(o1[:lft].gteq(o2[:lft]).and(o1[:rgt].lteq(o2[:rgt]))).join_sources).
  where(o2[:id].in(op)).
  project("distinct o1.id")

【讨论】:

这个解决方案帮助我将 Arel 带回了 AR。 这行得通。所以一般的模式是:写你的Arel::SelectManager,然后调用.joins_sources,把它变成一个“可连接的形式”,然后.joins就可以了。【参考方案3】:

你也可以这样做:

Organisation.joins(op.join_sql).where(op.where_sql)

我也搜索了一段时间后得到了这个。这将允许您在其上堆叠任何其他范围。

【讨论】:

我再也找不到.join_sql了。

以上是关于使用 Arel 进行嵌套集和连接查询并转换为 ActiveRecord::Relation的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Rails 中对 AREL 中的子查询进行连接

Arel 中的嵌套查询

Rails:将复杂的 SQL 查询转换为 Arel 或 ActiveRecord

Arel、联接和 Rails 查询

如何使用 Arel 跨多态 has_one 关系进行查询

如何在 Arel 和 Rails 中进行 LIKE 查询?