我的数据库中有两个条目
Obj1被标记为“你好,世界,星球” Obj2标有“你好”
如果我做modelName.tagged_with([“hello”,“world”,“planet”,“earth”],:any => true)
我想按照匹配的最高到最低数量的顺序对返回的对象进行排序。 所以在这种情况下,我希望订单是Obj1,Obj2
我该怎么做?有没有办法获得每个返回结果匹配的标签数量?
答案 0 :(得分:3)
你可以在对象上调用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
(按升序排序)放大在前面的交叉点,否定结果是一种简单的方法来反转通常的排序顺序。
答案 1 :(得分:1)
如果其他人正在寻找一种通过标签查询模型并按匹配数量排序的方法,请在此处发布。此解决方案还允许使用任何“相等”运算符,例如 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)
从模型中的acts-as-taggable-on 覆盖skill_list
:
def skill_list
skills.collect(&:name)
end
然后正常进行。