在數據庫中按相關性排序既可能,也比在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
子句將爲您提供分頁支持。