问题 将Arel用于嵌套集和连接查询并转换为ActiveRecord :: Relation


我有一个组织模型(嵌套集)。我有一个适合人的模特。一个人可以让另一个人担任副手。一个组织由一个人拥有。只有拥有者或其代理人才能看到组织。

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

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在组织上进行自我加入。


12291
2017-12-21 15:04


起源



答案:


似乎没有任何答案的接受者,我自己找到了一个解决方案:

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))

8
2017-12-23 20:26



所以,我认为我有一个类似的问题,但我在解决方案#2时遇到了麻烦: stackoverflow.com/questions/4522746/... - Doctor Mohawk


答案:


似乎没有任何答案的接受者,我自己找到了一个解决方案:

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))

8
2017-12-23 20:26



所以,我认为我有一个类似的问题,但我在解决方案#2时遇到了麻烦: stackoverflow.com/questions/4522746/... - Doctor Mohawk


你应该可以打电话 join_sources 在一个实例上 Arel::SelectManager,可以传递给 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")

4
2018-03-13 05:57



这个解决方案让我把Arel带回AR。 - Martin Streicher
这很有效。所以一般模式是:撰写你的 Arel::SelectManager,然后打电话 .joins_sources 然后把它变成一个“可连接的形式” .joins 在上面。 - Ziggy


你也可以这样做:

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

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


3
2018-02-18 14:40



我找不到 .join_sql 不再。 - Martin Streicher