我正在尝试在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')
答案 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