Rails 3多对多查询条件

时间:2012-01-04 12:03:32

标签: ruby-on-rails many-to-many

我正在尝试在rails 3中执行简单的Post / Tags关系。 一切正常,除非我想查询与几个标签相关的帖子。 基本上我想做这样的事情:

Post.joins(:tags).where('tags.name ILIKE ?', var)

但是我不想只使用一个var,而是想使用一个数组。 我试过了:

Post.joins(:tags).where('tags.name IN (?)', names_array)

但不幸的是,它做了一个简单的LIKE(而不是ILIKE),并且像OR条件一样,听起来非常合乎逻辑。

我还在这篇文章中使用了find_by_sql找到了另一个解决方案 http://snippets.dzone.com/posts/show/32

但对我来说似乎有点难看。

更好地了解问题。 我有3个帖子: 波斯塔 PostB POSTC

PostA与TagA TagB和TagC标签相关。 PostB与TagA和TagB标签相关。 PostC仅与TagA有关。

如果我查找TagA和TagC帖子,我想找到PostA,因为它与两个标签有关。 使用哈希条件返回PostA PostB和PostC。 我想要的是与“至少”所有指定标签相关的帖子。

所以任何人都有更好的方法来解决这个问题吗?

感谢。

3 个答案:

答案 0 :(得分:2)

我不知道它是否会解决您的问题,但对于复杂的查询,我几乎总是只使用Squeel

然后做这样的事情:

@posts = Post.joins(:tags)
  .where{tags.name.like_any names_array}
  .group("post_id")
  .having("count(post_id) = #{names_array.size}")

SQL希望看起来像这样

SELECT "posts".* FROM "posts"
  INNER JOIN "tags" ON "tags"."post_id" = "posts"."id"
  WHERE (("tags"."name" LIKE "TagA" OR "tags"."name" LIKE "TagB"))
  GROUP BY post_id
  HAVING count(post_id) = 2

如果我记得根据使用的数据库,使用ILIKE而不是LIKE非常擅长使用。 (至少比AR好)

你也可以使用没有squeel的AR来做到这一点,但我真的喜欢squeel附带的一些想法和帮助,例如_all


至于探索......

假设我搜索了TagsA和B.

这样做可以找到包含这些标签的所有帖子。

所以你会有类似的东西:

  • PostA TagA
  • PostA TagB
  • PostB TagA
  • PostB TagB
  • PostC TagA

然后它将使用post_id将所有那些不同的Post结果按联合标记分组。

  • PostA TagA TagB
  • PostB TagA TagB
  • PostC TagA

然后它将通过检查有多少forgien_ids来检查SQL行具有的标签数量。 由于A和B有2个标签,因此您知道它与您输入的所有标签相匹配。

答案 1 :(得分:0)

好的,所以我发现没办法避免使用find_by_sql。 这就是我所做的。

@posts = Post.find_by_sql([ "SELECT * FROM posts p
    JOIN (
        SELECT pt.post_id FROM posts_tags pt
        JOIN posts p ON p.id = pt.post_id
        JOIN tags t ON t.id = pt.tag_id
        WHERE t.label IN (?)
        GROUP BY pt.post_id
        HAVING count(pt.post_id) = ?
    ) ct ON c.id = ct.post_id", names_array, names_array.size])

我个人并不完全理解这个查询(在http://www.sergiy.ca/how-to-write-many-to-many-search-queries-in-mysql-and-hibernate/ - #3上找到)。特别是它加入选择的部分。因此,如果有人能够解释这个查询是如何真正有用的话那就太棒了。

此外,如果有人知道如何以更“铁路”的方式(比硬编码查询)这样做,我会喜欢它。

希望这可以帮助一些人。

答案 2 :(得分:0)

我被same problem困住了。 squeel docuemtnation表明这是可能的。在复合条件部分,他列出了三种类似的方法。

鉴于

names = ['Ernie%', 'Joe%', 'Mary%']

然后你可以做

Person.where('name LIKE ? OR name LIKE ? OR name LIKE ?', *names)

Person.where((['name LIKE ?'] * names.size).join(' OR '), *names)

Person.where{name.like_any names}

文档暗示我们可以使用AND而不是OR或like_all而不是like_any。

然而,我似乎无法让它为habtm关系工作。它不断给我undefined method 'call'一个ActiveRecord实例。