2

我的数据库中有两个条目

Obj1 被标记为“你好,世界,星球” Obj2 被标记为“你好”

如果我这样做 modelName.tagged_with(["hello", "world", "planet", "earth"], :any=>true)

我想按照匹配的标签数量从高到低的顺序对返回的对象进行排序。所以在这种情况下,我希望订单是 Obj1,Obj2

我怎样才能做到这一点?有没有办法获得与每个返回结果匹配的标签数量?

4

2 回答 2

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(按升序排序)将较大的交叉点放在前面,否定结果是一种简单的方法颠倒通常的排序顺序。

于 2011-01-12T08:02:52.677 回答
1

如果其他人正在寻找一种按标签查询模型并按匹配数排序的方法,请在此处发布。此解决方案还允许使用任何“相等”运算符,例如%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

并正常进行。

于 2021-01-08T15:25:47.217 回答