查询Activerecord HABTM关系以包含数组的所有元素

Mar*_*arc 2 ruby ruby-on-rails ruby-on-rails-3

我有ForumForumTagHABTM的关系.我还有一个名为变量的数组@tags.此数组包含一些ForumTags的名称.我希望能够查询并查找具有该数组所有值的所有论坛.我目前有:

@forums = Forum.joines(:forum_tags).where(:forum_tags => {:name => @tags}).includes(:forum_tags).all

但是,这将返回阵列中至少具有一个值的所有论坛.

Don*_*ank 5

以下将要求论坛在@tags阵列中包含所有论坛标签.我假设a forum不会多次出现同样的情况forum_tag.

@forums = Forum.joins(:forum_tags).where(:forum_tags => {:name => @tags}).group("forums.id").having(['COUNT(*) = ?', @tags.length]).includes(:forum_tags).all
Run Code Online (Sandbox Code Playgroud)

这将生成如下的SQL查询:

@tags = ['foo', 'bar']

SELECT forums.id, forum_tags.id FROM forums
  LEFT OUTER JOIN forum_tags_forums on forum_tags_forums.forum_id = forums.id
  LEFT OUTER JOIN forum_tags ON forum_tags.id = forum_tags_forums.forum_tag_id
  WHERE forum_tags.name IN ('foo', 'bar')
  GROUP BY forums.id
  HAVING COUNT(*) = 2;
Run Code Online (Sandbox Code Playgroud)

这将通过与给定标记匹配的论坛对连接表中的所有行进行分组.如果COUNT函数具有您要查找的标记总数的值(并且没有重复forum/ forum_tag对),则论坛必须包含所有标记.

要获得剩余的标签(评论中提出的问题):

forum_tags = ForumTag.where(:name => @tags)

@forums_with_leftovers = Forum.select("forums.*, GROUP_CONCAT(forum_tags.name) AS leftover_tags").joins(:forum_tags).where(['forums.id IN (?) AND NOT forum_tags.id IN (?)', @forums, forum_tags]).group("forums.id").all
Run Code Online (Sandbox Code Playgroud)

每个Forum对象@forums_with_leftovers都有一个额外的属性leftover_tags,其中包含每个论坛对象中不在原始@tags变量中的逗号分隔的标记列表.