2012-01-27 88 views
1

当使用有很多关联来管理一个严重的标签时,通过选择的标签数量来排序/排序集合最有效的方法是什么?通过匹配进行排序的最快方式有很多通过关联?

例如:

  • 产品可以具有通过ProductTags
  • 许多标签。当用户选择标记,我想由所选择的标签的每个产品的数量,订购的产品。

在这种情况下可以使用cache_counter或类似的东西吗?我不确信使用sort是最好的选择。我认为在实际数据库上使用order的速度通常比sort快吗?

澄清/更新

对不起,如果上述是混淆。基本上我所追求的更接近于相关性的排序。例如,用户可能选择标签1,2和4.如果产品具有与其关联的所有树标签,我希望先列出该产品。第二种产品可能只有标签1 & 4.依此类推。我几乎可以肯定,这将不得不使用sortorder,但想知道是否有人找到了更有效的方法。

回答

0

在数据库中按相关性排序既可能,也比在Ruby中使用排序方法更有效率。假设下面的模型结构和适当的底层SQL表结构:

class Product < ActiveRecord::Base 
    has_many :product_taggings 
    has_many :product_tags, :through => :product_taggings 
end 

class ProductTags < ActiveRecord::Base 
    has_many :product_taggings 
    has_many :products, :through => :product_taggings 
end 

class ProductTaggings < ActiveRecord::Base 
    belongs_to :product 
    belongs_to :product_tags 
end 

在MySQL查询相关看起来是这样的:

SELECT 
    `product_id` 
    ,COUNT(*) AS relevance 
FROM 
    `product_taggings` AS ptj 
LEFT JOIN 
    `products` AS p 
    ON p.`id` = ptj.`product_id` 
LEFT JOIN 
    `product_tags` AS pt 
    ON pt.`id` = ptj.`product_tag_id` 
WHERE 
    pt.`name` IN ('Tag 1', 'Tag 2') 
GROUP BY 
    `product_id` 

如果我有以下产品及相关标签:

Product 1 -> Tag 3 
Product 2 -> Tag 1, Tag 2 
Product 3 -> Tag 1, Tag 3 

然后从上面的WHERE子句应该网我:

product_id | relevance 
---------------------- 
     2 |   2 
     3 |   1 

* Product 1 is not included since there were no matches. 
    Given that the user is performing a filtered search, 
    this behavior is probably fine. There's a way to get 
    Product 1 into the results with 0 relevance if 
    necessary. 

你所做的是创建一个很好的小结果集,它可以充当一种内联连接表。

SELECT * 
FROM 
    `products` AS p 
    ,(SELECT 
     `product_id` 
     ,COUNT(*) AS relevance 
    FROM 
     `product_taggings` AS ptj 
    LEFT JOIN 
     `products` AS p 
     ON p.`id` = ptj.`product_id` 
    LEFT JOIN 
     `product_tags` AS pt 
     ON pt.`id` = ptj.`product_tag_id` 
    WHERE 
     pt.`name` IN ('Tag 1', 'Tag 2') 
    GROUP BY `product_id` 
) AS r 
WHERE 
    p.`id` = r.`product_id` 
ORDER BY 
    r.`relevance` DESC 

什么你就必须是包含字段的结果集从products表:为了您的products表贴相关性得分到查询的每一行,如下使用该查询作为子查询和另一个相关性列末尾,然后将在ORDER BY子句中使用。

你需要写出一个方法,将填写此查询与您想要的pt.name IN列表。在将其插入查询之前,请确保将清单列表,否则您将打开自己的SQL注入。

取出查询组装方法的结果,并通过Product.find_by_sql(my_relevance_sql)运行它,让您的模型直接从数据库中按相关性进行预先排序。

明显不利的方面是,你介绍一个具体的DBMS的依赖到你的Rails代码(和风险SQL注入,如果你不小心)。如果您不使用MySQL,则可能需要修改语法。但是,它的执行速度要快得多,尤其是在一个巨大的结果集上,而不是结果上使用Ruby sort。此外,如果需要,添加LIMIT子句将为您提供分页支持。

0

建立在瑞安的出色答卷,我想可以用来acts-as-taggable-on和类似的插件(表称为tags/taggings)的方法,并结束了与此:

def Product.find_by_tag_list(tag_list) 
    tag_list_sql = "'" + tag_list.join("','") + "'" 
    Product.find_by_sql("SELECT * FROM products, (SELECT taggable_id, COUNT(*) AS relevance FROM taggings LEFT JOIN tags ON tags.id = taggings.tag_id WHERE tags.name IN (" + tag_list_sql + ") GROUP BY taggable_id) AS r WHERE products.id = r.taggable_id ORDER BY r.relevance DESC;") 
end 

要获取列表按相关性排序的相关产品,然后我可以这样做:

Product.find_by_tag_list(my_product.tag_list)