Rails - как объединить несовместимые операторы с OR (Rails 5) - PullRequest
0 голосов
/ 19 февраля 2020

Существует модель проекта и модель оплаты:

class Project < ApplicationRecord
  has_many :payments
end

class Payment < ApplicationRecord
  belongs_to :project
end

Мне довольно легко комбинировать запросы для столбцов с or ИЛИ and.

Project.where(projects[:created_at].gt(6.months.ago).or(projects[:updated_at].gt(1.month.ago)))

Причина использования Arel заключается в том, что точные столбцы, операторы и значения исходят от пользователя, и операторы Arel очень хорошо соответствуют тому, что пользователь может выбрать.

Однако, если я хочу объединить это с данными из другого связанную таблицу, то я просто не могу понять, что делать. ActiverRecord требует OR для структурной совместимости, поэтому я не могу сделать:

Project.where(projects[:created_at].gt(6.months.ago)).or(Project.joins(:payments))

Что-то вроде этого: Project.where(projects[:created_at].gt(6.months.ago).or(projects.join(payments).on(projects[:id].eq(payments[:project_id]))))

вызывает исключение: ActiveRecord :: StatementInvalid : PG :: SyntaxError: ERROR: подзапрос должен возвращать только один столбец.

Один из способов, которым я смог это сделать, был что-то вроде: Project.where(projects[:created_at].gt(6.months.ago)).or(Project.where(id: Project.joins(:payments).select(:id)))

, который сгенерировал SQL SELECT "projects".* FROM "projects" WHERE (("projects"."created_at" > '2019-08-19 09:11:22.064298') OR "projects"."id" IN (SELECT "projects"."id" FROM "projects" INNER JOIN "payments" ON "payments"."project_id" = "projects"."id"))

Это лучший способ сделать это или есть лучший способ? Пожалуйста, можно объединить более 10 таких предложений, включающих несколько таблиц, с использованием include во всех возможных таблицах, чтобы сделать все отдельные предложения совместимыми, вероятно, будет слишком дорого.

1 Ответ

1 голос
/ 19 февраля 2020

Есть ли какая-то причина, по которой вы не можете поддерживать совместимость своих соединений в любых условиях? Например, в Rails 5+ вы можете написать:

Project.where(some_field: '12').joins(:payments)
       .or(
           Project.where(payments: {id: '1'}).joins(:payments)
       )

Что будет генерировать SQL, например:

SELECT projects.* FROM projects
INNER JOIN payments ON (payments.project_id = project.id)
WHERE projects.some_field = 12 OR payments.id = 1

Примечание:

Используя ваш структурно несовместимый пример в OP, вы можете просто добавить соединение к первому условию, чтобы сделать его совместимым:

Project.where(projects[:created_at].gt(6.months.ago)).joins(:payments).or(Project.joins(:payments))
...