从不带WHERE子句的联接记录向活动查询添加任意属性(活动记录)

时间:2019-03-04 21:23:48

标签: sql ruby-on-rails postgresql activerecord

我正在尝试在select语句中创建一个属性,该属性取决于是否存在关联。我不确定是否可以使用单个查询,并且目标是不必在以后迭代列表。

这是结构。

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 :(得分:1)

是的,您可以这样做:

url / who

这将导致这样的SQL查询:

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

如果要指定特定项目(即使用表达式),则可以使用Arel进行如下操作:

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

不幸的是,您将没有列名别名,也无法在Arel :: Nodes :: Equality实例上调用User.joins(:projects).select(Arel.star, Subscription.arel_table[:project_id].eq(42)) 。我对Arel的内部知识了解不足,无法解决这个问题。但是如果您想要Arel的可组合性,就可以这样做(例如,如果这将需要与多个模型或列一起使用):

as

这有点笨拙,但是它可以工作并且提供了一个返回布尔值的User.joins(:projects).select(Arel.star, Subscription.arel_table[:project_id].eq(42).to_sql + " as has_project") 方法。您可以像这样漂亮:

user.has_project