Используйте Arel для вложенного запроса set & join и конвертируйте в ActiveRecord :: Relation - PullRequest
11 голосов
/ 21 декабря 2010

У меня есть модель для организации (вложенный набор).У меня есть модель для людей.Человек может иметь другого человека в качестве заместителя.Организация принадлежит человеку.Организация видна только владельцу или его заместителю.

Я хотел бы получить все организации, видимые данному человеку, т.е.все организации, принадлежащие этому лицу или принадлежащие людям, для которых данное лицо является депутатом:

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 не удается выполнить самостоятельное объединение в организациях.

Ответы [ 3 ]

8 голосов
/ 23 декабря 2010

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

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))
4 голосов
/ 18 февраля 2014

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

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

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

4 голосов
/ 13 марта 2013

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