在数据库中按相关性排序既可能,也比在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
子句将为您提供分页支持。