Используйте Arel для вложенного набора & join query и конвертируйте в 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 для самостоятельного объединения в организации.

Ответ 1

Кажется, нет никаких ответчиков за любой ответ, и я сам нашел подход к решению:

1. Преобразуйте последнее соединение в ActiveRecord::Relation

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

Единственная проблема заключается в том, что это вызывает Arel::SelectManager.to_a, который поставляется с предупреждением об устаревании (и также является дорогостоящей операцией). Я не нашел альтернативы, хотя (подозревают, что их нет, и это предупреждение об утомлении является лишь одним из несоответствий, наблюдаемых в Ареле, и это принятие в ActiveRecord).

2. Self-join на вложенном наборе для получения всех потомков

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

Вы должны иметь возможность вызвать 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")

Ответ 3

Вы также можете сделать:

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

Я тоже получил это после поиска. Это позволит вам складывать любую другую область.