Добавить произвольный атрибут в запрос SQL из записи объединений без предложения WHERE (активная запись) - PullRequest
0 голосов
/ 05 марта 2019

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

Вот структура.

class Project < ApplicationRecord
  has_many :subscriptions
  has_many :users, through: :subscriptions
end

class User < ApplicationRecord
  has_many :subscriptions
  has_many :projects, through: :subscriptions
end

class Subscription < ApplicationRecord
  belongs_to :project
  belongs_to :user
end

Знание проекта,Цель запроса - вернуть ВСЕХ пользователей и включить в них новый вызов атрибута с подпиской - обозначение того, подписаны они или нет.

нерабочий код (псевдокод):

  project = Project.find_by(name: 'has_subscribers')

  query = 'users.*, (subscriptions.project_id = ?) AS subscribed'

  users = User.includes(:subscriptions).select(query, project.id)

  user.first.subscribed
  # => true or false

Я открыт для того, есть ли лучший способ сделать это.Тем не менее, информация:

  • Вы знаете запись проекта.
  • Вы запрашиваете список ВСЕХ пользователей
  • Каждая запись пользователя имеет атрибут subscribed, обозначающий, подписан ли он на данный проект

Решение:

Мне удалось найти прямое решение, используя метод агрегата bool_or.Coalesce гарантирует, что возвращаемое значение будет false вместо nil, если подписок не существует.

query = "users.*, COALESCE(bool_or(subscriptions.project_id = '#{project_id}'::uuid), false) as subscribed"

User.left_outer_joins(:subscriptions)
  .select(query)
  .group('users.id')

1 Ответ

0 голосов
/ 05 марта 2019

Да, вы можете сделать это:

User.joins(:projects).select(Arel.star, Subscription.arel_table[:project_id])

, что приведет к SQL-запросу, подобному этому:

SELECT *, "subscriptions"."project_id" FROM "users" INNER JOIN "subscriptions" ON "subscriptions"."user_ud" = "users"."id";

Если вы хотите указать конкретный проект (то есть использовать выражение), вы можете сделать это с помощью Arel следующим образом:

User.joins(:projects).select(Arel.star, Subscription.arel_table[:project_id].eq(42))

К сожалению, у вас не будет псевдонима имени столбца, и вы не можете вызвать as для Arel ::Узлы :: Эквивалентность.Я не знаю достаточно о внутренностях Арела, чтобы выбраться из этой коробки.Но вы можете сделать это, если вам нужна компоновка Ареля (например, если это то, что должно работать с несколькими моделями или столбцами):

User.joins(:projects).select(Arel.star, Subscription.arel_table[:project_id].eq(42).to_sql + " as has_project")

Это немного неуклюже, но работаети предоставляет метод user.has_project, который возвращает логическое значение.Вы можете сделать это так:

class User
  scope :with_project_status, lambda do |project_id|
    has_project = 
      Subscription.arel_table[:project_id].
        eq(project_id).to_sql + " as has_project"

    joins(:projects).select(Arel.star, has_project)
  end
end

User.with_project_status(42).where(active: true)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...