Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/55.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails has_许多的附加范围条件:到_Ruby On Rails_Ruby_Postgresql_Activerecord_Ruby On Rails 5 - Fatal编程技术网

Ruby on rails has_许多的附加范围条件:到

Ruby on rails has_许多的附加范围条件:到,ruby-on-rails,ruby,postgresql,activerecord,ruby-on-rails-5,Ruby On Rails,Ruby,Postgresql,Activerecord,Ruby On Rails 5,我希望用户能够找到所有贴子有一个或多个标签。我希望标签是可添加的标准,例如,你可以搜索只有“新闻”标签的帖子,或者你可以搜索同时有“新闻”和“科学”标签的帖子 目前,我拥有的是一个Post模型、一个标记模型和一个称为Marking的连接模型。Post有许多:标记,通过::标记。我通过将标记ID数组传递给Post类方法来获得所需: post.rb def self.from_tag_id_数组 post_数组=[] Marking.where(tag_id:array)。groupby(&:pos

我希望用户能够找到所有贴子有一个或多个标签。我希望标签是可添加的标准,例如,你可以搜索只有“新闻”标签的帖子,或者你可以搜索同时有“新闻”和“科学”标签的帖子

目前,我拥有的是一个Post模型、一个标记模型和一个称为Marking的连接模型。Post
有许多:标记,通过::标记
。我通过将标记ID数组传递给Post类方法来获得所需:

post.rb

def self.from_tag_id_数组
post_数组=[]
Marking.where(tag_id:array)。groupby(&:post_id)。每个do|p|u id,m|u数组|

post_array因此,构建这类查询的一般经验法则是尽量减少在“Ruby land”中的工作量,最大限度地增加在“Database land”中的工作量。在上面的解决方案中,您正在获取一组带有集合
数组中任何标记的标记,这可能是一个非常大的集合(所有具有这些标记的帖子)。这在ruby数组中表示并进行处理(
groupby
在ruby world中,
group
在Database land中是等效的)

因此,除了难以阅读之外,对于任何一组大的标记来说,这种解决方案都是缓慢的

在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) }
}
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)
这将生成一个类似这样的查询(同样针对标记标识5和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:

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) }
}
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\u id
post\u id
的多个标记,这会使计数偏离

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


另请参见:

您可以尝试类似的方法。你的经验法则真的很有用。我有一些书要读。谢谢