我在尝试按照我想要的方式运行查询时遇到了一些麻烦,我没有得到我希望的所有结果。
我有3个模型Post
,Comment
和Tag
。帖子和评论都可以包含标签,两者都与标签有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
答案 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 JOIN
和comments
制作了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
请注意DISTINCT
和uniq
的必要性,因为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) }