Используйте 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)
Я тоже получил это после поиска. Это позволит вам складывать любую другую область.