Rails 3多对多查询条件

发布于 2024-12-24 16:23:11 字数 778 浏览 1 评论 0原文

我正在尝试在 Rails 3 中做一个简单的帖子/标签关系。 一切工作正常,除了当我想查询与多个标签相关的帖子时。 基本上我想做这样的事情:

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

但是我不想只有一个变量,而是想使用一个数组。 我尝试过:

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

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

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

但这似乎是对我来说有点丑。

为了更好地理解问题。 我有 3 个帖子: 邮政A 邮政B PostC

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

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

那么有人有更好的方法来处理这个问题吗?

谢谢。

I'm trying to do a simple Post/Tags relation in rails 3.
Everything working fine except when I want to query the Posts which are related to several tags.
Basically I'd like to do this kind of thing :

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

But instead of having just one var, I'd like to use an array.
I tried :

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

But unfortunately it does a simple LIKE (not ILIKE) and works like a OR condition which sounds perfectly logical.

I also found another solution by using find_by_sql in this post
http://snippets.dzone.com/posts/show/32

But it seems a bit ugly to me.

To better understand the problem.
I've got 3 posts :
PostA
PostB
PostC

PostA is related to TagA TagB and TagC tags.
PostB is related to TagA and TagB tags.
PostC is only related to TagA.

If I look for TagA and TagC Posts I'd like to finds PostA because it is related to both Tags.
Using a hash condition returns PostA PostB and PostC.
What I want is the Posts which are related to "at least" all the specified Tags.

So anyone has a better way to handle this ?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

庆幸我还是我 2024-12-31 16:23:11

我不知道它是否能解决您的问题,但对于像这样的复杂查询,我几乎总是使用 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

如果我记得 squeel 非常擅长使用 ILIKE 而不是 LIKE,具体取决于所使用的数据库。 (至少比 AR 更好)

你也可以使用 AR 来做到这一点,而不需要 squeel 但我真的很喜欢其中的一些想法以及 squeel 附带的帮助程序,如 _all


至于解释......

假设我搜索了标签 A 和 B。

它的作用是查找带有这些标签的所有帖子。

因此,您将得到如下内容:

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

然后它将使用 post_id 按连接标签对所有这些不同的帖子结果进行分组。

  • PostA TagA TagB
  • PostB TagA TagB
  • PostC TagA

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

I don't know if it will solve you problem but for complex queries like that I almost always just use Squeel.

Then do something like this:

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

The SQL hopefully looks something like this

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

If I remember squeel is pretty good at using ILIKE instead of LIKE depending on the database used. (atleast better than AR)

Also you could do this using AR without squeel but I REALLY like some of the ideas and helpers that come with squeel like _all


As for an explination...

Assume I searched for TagsA and B.

What that does is finds all the Posts with those tags.

So you'll have something like:

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

Then it will group all those different Post results by the joined tags using post_id.

  • PostA TagA TagB
  • PostB TagA TagB
  • PostC TagA

Then it will check the number of Tags the SQL line has by checking how many forgien_ids are present.
Since A and B have 2 tags you know it matched all you input.

汹涌人海 2024-12-31 16:23:11

好吧,我发现没有办法避免 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) 。特别是它连接选择的部分。因此,如果有人能解释这个查询是如何真正工作的,那就太好了。

此外,如果有人知道如何以更“轨道”的方式(而不是硬编码查询)执行此操作,我会很高兴。

希望这对一些人有帮助。

Ok so I found no way to avoid a find_by_sql.
Here is what I've done.

@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])

I personally don't completely understand this query (found on http://www.sergiy.ca/how-to-write-many-to-many-search-queries-in-mysql-and-hibernate/ - #3). Especially the part where it joins a select. So if anyone could explain how really work this query it would be great.

Further more, if anyone knows how to do this in a more "rails" way (than a hard coded query), I'd love it.

Hope this helps some people.

天赋异禀 2024-12-31 16:23:11

我陷入了同样的问题squeel docuemtnation 表明这是可能的。在“复合条件”部分中,他列出了执行类似操作的三种方法。

鉴于

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

那么你可以做

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

or

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

or

Person.where{name.like_any names}

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

然而,我似乎无法让它适用于习惯关系。它不断为我提供 ActiveRecord 实例的未定义的方法“call”

I'm stuck on the same problem. The squeel docuemtnation suggests this is possible. In the Compound Conditions section he lists three ways to do something similar.

Given

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

Then you can do

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

or

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

or

Person.where{name.like_any names}

The documentation implies we can use AND instead of OR or like_all as opposed to like_any.

However I can't seem to get it to work for a habtm relationship. It keeps giving me undefined method 'call' for an instance of ActiveRecord.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文