2

我试图弄清楚某些类型的文章的数量。我有一个非常低效的查询:

Article.where(status: 'Finished').select{|x| x.tags & Article::EXPERT_TAGS}.size

在我追求成为一个更好的程序员的过程中,我想知道如何使它成为一个更快的查询。tags是 中的一个字符串数组ArticleArticle::EXPERT_TAGS是另一个字符串数组。我想找到数组的交集,并得到结果记录数。

编辑:Article::EXPERT_TAGS并被article.tags定义为 Mongo 数组。这些数组包含字符串,我相信它们是序列化的字符串。例如:Article.first.tags = ["Guest Writer", "News Article", "Press Release"]。不幸的是,这没有正确设置为单独的标签表。

第二次编辑:我使用的是 MongoDB,所以实际上它使用的是 MongoWrapper,如 MongoMapper 或 mongoid,而不是 ActiveRecord。这是我的一个错误,对不起!由于这个错误,它搞砸了对这个问题的分析。感谢 PinnyM 指出错误!

4

3 回答 3

1

由于您使用的是 MongoDB,因此您还可以考虑针对数组交集使用 MongoDB 特定的解决方案(聚合框架),以便在获取最终结果之前让数据库完成所有工作。

请参阅此 SO 线程如何检查数组字段是否是 MongoDB 中另一个数组的一部分?

于 2013-09-16T15:46:49.297 回答
0

我在这里回答了关于 ActiveRecord 中的查询等一般交叉点的问题。

摘录如下:


以下是我用于在 ActiveRecord 中构建类似查询的交集的一般方法:

class Service < ActiveRecord::Base
  belongs_to :person

  def self.with_types(*types)
    where(service_type: types)
  end
end

class City < ActiveRecord::Base
  has_and_belongs_to_many :services
  has_many :people, inverse_of: :city
end

class Person < ActiveRecord::Base
  belongs_to :city, inverse_of: :people

  def self.with_cities(cities)
    where(city_id: cities)
  end

  # intersection like query
  def self.with_all_service_types(*types)
    types.map { |t|
      joins(:services).merge(Service.with_types t).select(:id)
    }.reduce(scoped) { |scope, subquery|
      scope.where(id: subquery)
    }
  end
end

Person.with_all_service_types(1, 2)
Person.with_all_service_types(1, 2).with_cities(City.where(name: 'Gold Coast'))

它将生成以下形式的 SQL:

SELECT "people".*
  FROM "people"
 WHERE "people"."id" in (SELECT "people"."id" FROM ...)
   AND "people"."id" in (SELECT ...)
   AND ...

只要每个子查询在其结果集中返回匹配人员的 id,您就可以根据任何条件/连接等使用上述方法创建任意数量的子查询。

每个子查询结果集将被“与”在一起,从而将匹配集限制为所有子查询的交集。

于 2013-09-16T15:44:32.990 回答
0

假设整个tags列表存储在单个数据库字段中并且您希望保持这种方式,我看不出有太大的改进空间,因为您需要将所有数据放入 Ruby 中进行处理。

但是,您的数据库查询存在一个问题

Article.where(status: 'Finished')

# This translates into the following query
SELECT * FROM articles WHERE status = 'Finished'

本质上,您正在获取所有列,而您tags的流程只需要该列。所以,你可以pluck这样使用:

Article.where(status: 'Finished').pluck(:tags)

# This translates into the following query
SELECT tags FROM articles WHERE status = 'Finished'
于 2013-09-16T15:29:35.480 回答