2012-01-27 90 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)