Rails通过2个不同的关联模型进行查询

时间:2014-12-11 19:28:49

标签: ruby-on-rails postgresql activerecord

我在尝试按照我想要的方式运行查询时遇到了一些麻烦,我没有得到我希望的所有结果。

我有3个模型PostCommentTag。帖子和评论都可以包含标签,两者都与标签有has_and_belongs_to_many的关系。我希望能够获得所有具有指定标记或具有该标记的评论的帖子,我已经在以下范围内对这样的帖子进行了这样的操作:

scope :tag, -> (tag_id) { joins(:tags, :comment_tags).where("tags_posts.tag_id = :tag_id OR comments_tags.tag_id = :tag_id", tag_id: tag_id) }

但是,这并没有返回所有帖子,只是它们的一部分,似乎只是关于评论的帖子,这是它生成的查询:

SELECT COUNT(*) FROM "posts" 
INNER JOIN "tags_posts" ON "tags_posts"."post_id" = "posts"."id" 
INNER JOIN "tags" ON "tags"."id" = "tags_posts"."tag_id" 
INNER JOIN "comments" ON "comments"."post_id" = "posts"."id" 
INNER JOIN "comments_tags" ON "comments_tags"."comment_id" = "comments"."id" 
INNER JOIN "tags" "comment_tags_posts" ON "comment_tags_posts"."id" = "comments_tags"."tag_id" 
WHERE (tags_posts.tag_id = 1 OR comments_tags.tag_id = 1)

这些是模型:

class Post < ActiveRecord::Base
  has_and_belongs_to_many :tags
  has_many :comment_tags, through: :comments, source: :tags
end

class Tag < ActiveRecord::Base
  has_and_belongs_to_many :posts
  has_and_belongs_to_many :comments
end

class Comment < ActiveRecord::Base
  belongs_to :post
  has_and_belongs_to_many :tags
end

2 个答案:

答案 0 :(得分:1)

我不确定你是否已经明白了这一点,但万一你没有,这是一个可能的解决方案:

在纯SQL中,主要用于说明目的:

SELECT 
    DISTINCT posts.*
FROM
    posts
        INNER JOIN
    tags_posts ON tags_posts.post_id = posts.id
        LEFT JOIN
    comments ON comments.post_id = posts.id
        LEFT JOIN
    comments_tags ON comments_tags.comment_id = comments.id
        INNER JOIN
    tags ON (tags.id = tags_posts.tag_id OR tags.id = comments_tags.tag_id)
WHERE tags.id = 1

原始版本中的主要问题是您使用INNER JOINcomments制作了comments_tags。结果你可能会删除每个没有任何评论的Post。所以解决方案是LEFT JOIN与评论相关的所有内容。然后,由于我们处于加入阶段,我们可以在标记帖子或评论帖子上INNER JOIN tags

转换为Active Record不是很漂亮,但必要:

Post.joins("INNER JOIN posts_tags ON posts_tags.post_id = posts.id")
    .joins("LEFT JOIN comments ON comments.post_id = posts.id")
    .joins("LEFT JOIN comments_tags ON comments_tags.comment_id = comments.id")
    .joins("INNER JOIN tags ON (posts_tags.tag_id = tags.id OR comments_tags.tag_id = tags.id)")
    .where(tags: {id: 1})
    .uniq

请注意DISTINCTuniq的必要性,因为LEFT JOIN会导致重复。

修改

如果对数据集或结构存在一些误解,这是我在测试中用于创建上述查询的数据示例。

<强>帖子

+----+--------------------------+
| id | text                     |
+----+--------------------------+
|  1 | Post about programming 1 |
|  2 | Post about programming 2 |
|  3 | Post about programming 3 |
|  4 | Post about cooking 1     |
|  5 | Post about cooking 2     |
+----+--------------------------+

<强>代码

+----+-------------+
| id | name        |
+----+-------------+
|  1 | programming |
|  2 | cooking     |
|  3 | woodworking |
+----+-------------+

<强> tags_posts

+--------+---------+
| tag_id | post_id |
+--------+---------+
|      1 |       1 |
|      1 |       2 |
|      1 |       3 |
|      2 |       4 |
|      2 |       5 |
+--------+---------+

<强>评论

+----+----------------------------------------------+---------+
| id | comment_text                                 | post_id |
+----+----------------------------------------------+---------+
|  1 | comment - programming on programming post 1a |       1 |
|  2 | comment - programming on programming post 1b |       1 |
|  3 | comment - programming on programming post 2a |       2 |
|  4 | comment - cooking on programming post 3a     |       3 |
|  5 | comment - programming on cooking post 4a     |       4 |
|  6 | comment - cooking on cooking post 4b         |       4 |
|  7 | comment - cooking on cooking post 5a         |       5 |
+----+----------------------------------------------+---------+

<强> comments_tags

+--------+------------+
| tag_id | comment_id |
+--------+------------+
|      1 |          1 |
|      1 |          2 |
|      1 |          3 |
|      1 |          5 |
|      2 |          4 |
|      2 |          6 |
|      2 |          7 |
+--------+------------+

如果我想搜索&#34;编程&#34;,上述查询将产生此结果集:

+----+--------------------------+
| id | text                     |
+----+--------------------------+
|  1 | Post about programming 1 |
|  2 | Post about programming 2 |
|  4 | Post about cooking 1     |
|  3 | Post about programming 3 |
+----+--------------------------+

因为我们有3个帖子专门用&#34;编程&#34;标记,并且有一个标记标记为&#34;编程&#34;在标记不同的帖子上。

答案 1 :(得分:0)

我不确定什么是百胜,这是一个帖子吗?

从您的SQL查询中,它似乎只会计算具有此特定标记的特定标记和注释的yum。你想要的是计算具有特定标签的yum或具有此特定标签的评论。

我会做2个查询,一个用特定标签计算yum,用一个来计算带有特定注释标签的yum,并添加它们以获得总数或使用UNION条件进行一次查询。

scope :yums_tagged, -> (tag_id) { joins(:tags).where("tags_yums.tag_id = :tag_id", tag_id: tag_id) }
scope :comments_taged, -> (tag_id) { joins(:comment_tags).where("comments_tags.tag_id = :tag_id", tag_id: tag_id) }