2013-09-16 107 views
2

我想弄清楚某些类型的文章的数量。我有一个非常低效的查询:ActiveRecord查询数组交集?

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

在我的追求成为一个更好的程序员,我不知道如何使这个更快的查询。 tags是一个字符串在Article阵列,并且Article::EXPERT_TAGS是字符串的另一个阵列。我想找到数组的交集,并得到结果记录数。编辑:Article::EXPERT_TAGSarticle.tags定义为Mongo数组。这些数组包含字符串,我相信它们是序列化的字符串。例如:Article.first.tags = ["Guest Writer", "News Article", "Press Release"]。不幸的是,这不是作为一个单独的标签表正确设置的。

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

+0

如何被存储在数据库中的这个阵列串 - 作为一个单独的表(表模式,请)或Array场?你在用什么DBMS? – PinnyM

+1

我看起来约翰通过序列化数组来存储'tags'列表(@John,请保持诚实) –

+0

这是作为一个数组存储在MongoDB中的一个列中,而不是一个单独的表(这将使回顾过程中的生活变得更容易)。 – John

回答

0

假设整个tags列表存储在一个单一的数据库字段,要保持这种方式,我没有看到很大的改进范围,因为你需要的所有数据进入红宝石进行处理。

然而,有一个问题,您的数据库查询

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' 
+0

ActiveRecord内置支持将'Array'和'Hash'序列化为一个列。如果为'tags'定义了一个'text'列,并且分配了'tags = @ your_array',它将被序列化并保存到数据库中,当您查询记录时,它将被反序列化为一个'Array'对象。例如,看到这个SO线程,http://stackoverflow.com/questions/6694432/using-rails-serialize-to-save-hash-to-database –

0

我回答了有关像ActiveRecord的here查询一般路口的问题。

下面提取:


以下是我使用用于构造之类的查询相交在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。

每个子查询结果集都将AND'在一起,从而将匹配集限制为所有子查询的交集。