【问题标题】:Additive scope conditions for has_many :throughhas_many 的附加范围条件:通过
【发布时间】:2023-04-04 20:24:01
【问题描述】:

我希望用户能够找到具有一个或多个标签的所有帖子。而且我希望标签是附加条件,例如,您可以搜索只有“新闻”标签的帖子,或者您可以搜索同时有“新闻”和“科学”标签的帖子。

目前我所拥有的并且可以正常工作的是一个 Post 模型、一个 Tag 模型和一个名为 Marking 的连接模型。发布has_many :tags, through: :markings。我通过将一组标签 ID 传递给 Post 类方法来获得所需的内容:

post.rb

def self.from_tag_id_array array
  post_array = []
  Marking.where(tag_id: array).group_by(&:post_id).each do |p_id,m_array|
    post_array << p_id if m_array.map(&:tag_id).sort & array.sort == array.sort
  end
  where id: post_array
end

这似乎是一种笨拙的方式。有没有办法通过关联或类似的范围来做到这一点?

【问题讨论】:

标签: ruby-on-rails ruby postgresql activerecord ruby-on-rails-5


【解决方案1】:

因此,构建此类查询的一般经验法则是尽量减少“Ruby 领域”的工作量,并最大限度地提高“数据库领域”的工作量。在您上面的解决方案中,您正在获取一组带有array 中任何标签的标记,这可能是一个非常大的集合(所有帖子都具有这些标签中的任何一个)。这在 ruby​​ 数组中表示并进行处理(group_by 在 Ruby 世界中,group 在数据库领域中是等价的)。

因此,除了难以阅读之外,该解决方案对于任何大型标记集都会很慢。

在 Ruby 世界中,有几种方法可以在不做任何繁重工作的情况下解决问题。一种方法是使用子查询,如下所示:

scope :with_tag_ids, ->(tag_ids) {
  tag_ids.map { |tag_id|
    joins(:markings).where(markings: { tag_id: tag_id })
  }.reduce(all) { |scope, subquery| scope.where(id: subquery) }
}

这会生成这样的查询(同样针对 tag_ids 5 和 8)

SELECT "posts".*
FROM "posts"
WHERE "posts"."id" IN (SELECT "posts"."id" FROM "posts" INNER JOIN "markings" ON "markings"."post_id" = "posts"."id" WHERE "markings"."tag_id" = 5)
  AND "posts"."id" IN (SELECT "posts"."id" FROM "posts" INNER JOIN "markings" ON "markings"."post_id" = "posts"."id" WHERE "markings"."tag_id" = 8)

请注意,由于此处的所有内容都是直接在 SQL 中计算的,因此不会在 Ruby 中生成或处理数组。这通常会更好地扩展。

或者,您可以使用 COUNT 并在没有子查询的单个查询中执行此操作:

scope :with_tag_ids, ->(tag_ids) {
  joins(:markings).where(markings: { tag_id: tag_ids }).
  group(:post_id).having('COUNT(posts.id) = ?', tag_ids.count)
}

生成这样的 SQL:

SELECT "posts".*
FROM "posts"
INNER JOIN "markings" ON "markings"."post_id" = "posts"."id"
WHERE "markings"."tag_id" IN (5, 8)
GROUP BY "post_id"
HAVING (COUNT(posts.id) = 2)

这假设您没有多个标记具有同一对 tag_idpost_id,这会导致计数错误。

我认为最后一种解决方案可能是最有效的,但您应该尝试不同的解决方案,看看哪种解决方案最适合您的数据。

另请参阅:Query intersection with activerecord

【讨论】:

  • 你的经验法则真的很有帮助。我有一些阅读要做。谢谢!
猜你喜欢
  • 2023-03-10
  • 1970-01-01
  • 1970-01-01
  • 2014-07-05
  • 2011-07-18
  • 1970-01-01
  • 2014-02-25
  • 2017-10-09
  • 1970-01-01
相关资源
最近更新 更多