且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2023-02-05 15:21:16

以下将要求论坛具有 @tags 数组中的所有论坛标签.我假设 forum 不会多次使用相同的 forum_tag.

The following will require the forums to have all the forum tags in the @tags array. I am making the assumption that a forum will not have the same forum_tag more than once.

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

这将生成如下的 SQL 查询:

This will produce an SQL query like the following:

@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;

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

This will group all the rows in the join table by forums that match the given tags. If the COUNT function has the value of the total number of tags that you're looking for (and there are no duplicate forum/forum_tag pairs) then the forum must contain all the tags.

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

To get the leftover tags (question asked in the comments):

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

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

Each Forum object in @forums_with_leftovers will have an extra attribute leftover_tags that contains a comma separated list of tags in each forum object that is not in the original @tags variable.