我的数据库中有两个条目
Obj1 被标记为“你好,世界,星球” Obj2 被标记为“你好”
如果我这样做 modelName.tagged_with(["hello", "world", "planet", "earth"], :any=>true)
我想按照匹配的标签数量从高到低的顺序对返回的对象进行排序。所以在这种情况下,我希望订单是 Obj1,Obj2
我怎样才能做到这一点?有没有办法获得与每个返回结果匹配的标签数量?
我的数据库中有两个条目
Obj1 被标记为“你好,世界,星球” Obj2 被标记为“你好”
如果我这样做 modelName.tagged_with(["hello", "world", "planet", "earth"], :any=>true)
我想按照匹配的标签数量从高到低的顺序对返回的对象进行排序。所以在这种情况下,我希望订单是 Obj1,Obj2
我怎样才能做到这一点?有没有办法获得与每个返回结果匹配的标签数量?
您可以调用tag_list
对象并使用它来确定有多少标签:
tags = %w{hello world planet earth}
objs = ModelName.taggedWith(tags, :any => true)
objs.sort_by! { |o| -(tags & o.tag_list).length }
产生您正在寻找的标签和找到的标签的tags & o.tag_list
交集,然后我们否定交叉点的大小以告诉sort_by
(按升序排序)将较大的交叉点放在前面,否定结果是一种简单的方法颠倒通常的排序顺序。
如果其他人正在寻找一种按标签查询模型并按匹配数排序的方法,请在此处发布。此解决方案还允许使用任何“相等”运算符,例如%
from pg_trgm。
query = <<-SQL
SELECT users.*, COUNT(DISTINCT taggings.id) AS ct
FROM users
INNER JOIN taggings ON taggings.taggable_type = 'User'
AND taggings.context = 'skills'
AND taggings.taggable_id = users.id
AND taggings.tag_id IN
(SELECT tags.id FROM tags
WHERE (LOWER(tags.name) % 'ruby'
OR LOWER(tags.name) % 'java'
OR LOWER(tags.name) % 'sa-c'
OR LOWER(tags.name) % 'c--'
OR LOWER(tags.name) % 'gnu e'
OR LOWER(tags.name) % 'lite-c'
))
GROUP BY users.id
ORDER BY ct DESC;
SQL
User.find_by_sql(query)
请注意,上面的代码仅在您启用 pg_trgm 时才有效。您也可以简单地替换%
为ILIKE
.
编辑:使用 ActiveRecord 和急切加载:
这可以在范围或类方法中,并且可以与其他 ActiveRecord 方法链接。
ActiveRecord::Base.connection
.execute('SET pg_trgm.similarity_threshold = 0.5')
matches = skills.map do
'LOWER(tags.name) % ?'
end.join(' OR ')
select('users.*, COUNT(DISTINCT taggings.id) AS ct')
.joins(sanitize_sql_array(["INNER JOIN taggings
ON taggings.taggable_type = 'User'
AND taggings.context = 'skills'
AND taggings.taggable_id = users.id
AND taggings.tag_id IN
(SELECT tags.id FROM tags WHERE (#{matches}))", *skills]))
.group('users.id')
.order('ct DESC')
.includes(:skills)
skill_list
从模型中的 act-as-taggable-on覆盖:
def skill_list
skills.collect(&:name)
end
并正常进行。