Rails:通过关联聚合查询

Rails: Aggregate queries through association

在 rails 中,是否有通过 has_many :through 关联进行聚合查询的 ActiveRecord 方法?

编辑:我正在寻找一个数据库驱动的解决方案,而不是一堆 Ruby iterating/munging.

编辑#2:我搞砸了。 Venue模型与Show也是多对多的。

比如。想象这样的事情(人为的):

# Venue<->Show is many-to-many.
# Show<->Performer is many-to-many.

class Venue < ActiveRecord::Base
  has_many :bookings
  has_many :shows, through: :bookings
end

# Note "show" here would be "The Lion King," not a specific
# performance on a specific date.
class Show < ActiveRecord::Base
  has_many :bookings
  has_many :venues, through: :bookings

  has_many :engagements
  has_many :performers, through: :engagements
end

class Performer < ActiveRecord::Base
  has_many :engagements
  has_many :shows, through: :engagements
end

# Just a simple join model.
class Engagement < ActiveRecord::Base
  belongs_to :show
  belongs_to :performer
end

# Also a join model.
class Booking < ActiveRecord::Base
  belongs_to :venue
  belongs_to :show
end

如何对 Venue 执行查询,其中 returns 曾经在那里执行过的所有 Performer 的唯一集合?

谢谢!

我想你可以这样做:

venue = Venue.find(?)

# Select only unique Performer ids
unique_performer_ids = Performer.joins(:engagements).where(show_id: venue.show_id).distinct(:id)

# Select unique Performer objects
performers = Performer.where(id: unique_performer_ids)

可以将这两个查询合并为一个使用子查询。

由于您最后想要一组 Performers,因此请从该模型开始。我们知道我们正在寻找一个特定的场地,所以让我们看看我们可以使用哪些协会来到达那里。

# Performer
has_many :shows, through: :engagements

# Show
has_many :venues, through: :bookings

宾果!我们可以从表演者到演出再到场地(Rails 将自动计算出从参与到演出和预订到场地的连接)。

ActiveRecord 支持使用关联名称加入关联(注意:这仅适用于 INNER 联接):http://guides.rubyonrails.org/active_record_querying.html#using-array-hash-of-named-associations

阅读该查询指南后,我们知道我们可以使用哈希语法加入嵌套关联。这让我们

Performer.joins(shows: :venues)

但我们不能止步于此,因为那样我们将获得在至少一个场馆至少表演过一场表演的所有表演者(在不止一场表演、订婚、预订或场馆中有任何表演者的重复——我们稍后会处理这些)。为了缩小集合范围,我们必须添加一个 WHERE 子句。链接指南的第 12.3 节解释了我们可以将哈希条件添加到连接表中。 缩小场地看起来像

venue = Venue.find(params[:venue_id])
Performer.joins(shows: :venues).where(venues: {id: venue.id})

现在我们有所有参加过所有演出的表演者!但是等等,如果他们参加过不止一场表演(或参与),我们仍然有重复的表演者。为了删除重复项,我们使用 uniq.

把它们放在一起给我们

venue = Venue.find(params[:venue_id])
Performer.joins(shows: :venues).where(venues: {id: venue.id}).uniq