gpt4 book ai didi

ruby-on-rails - has_many :through 的附加范围条件

转载 作者:行者123 更新时间:2023-11-29 12:57:17 25 4
gpt4 key购买 nike

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

目前我拥有的是一个 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 领域”的工作并最大限度地提高“数据库领域”的工作量。在你上面的解决方案中,你正在获取一组标记,其中包含 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

关于ruby-on-rails - has_many :through 的附加范围条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39929442/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com