diff --git a/app/models/post.rb b/app/models/post.rb index 8e51d89de43..91324a77900 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -656,28 +656,26 @@ class Post < ActiveRecord::Base def reply_ids(guardian = nil) replies = Post.exec_sql(" - WITH RECURSIVE breadcrumb(id, post_number, level) AS ( - SELECT id, post_number, 0 - FROM posts - WHERE id = :post_id + WITH RECURSIVE breadcrumb(id, level) AS ( + SELECT :post_id, 0 UNION - SELECT p.id, p.post_number, b.level + 1 - FROM posts p, breadcrumb b - WHERE b.post_number = p.reply_to_post_number - AND p.topic_id = :topic_id - ), breadcrumb_with_replies AS ( - SELECT b.id, b.level, COUNT(*) - FROM breadcrumb b, post_replies pr - WHERE pr.reply_id = b.id - GROUP BY b.id, b.level - ) SELECT id, level FROM breadcrumb_with_replies WHERE count = 1 ORDER BY id - ", post_id: id, topic_id: topic_id).to_a + SELECT reply_id, level + 1 + FROM post_replies, breadcrumb + WHERE post_id = id + ), breadcrumb_with_count AS ( + SELECT id, level, COUNT(*) + FROM post_replies, breadcrumb + WHERE reply_id = id + GROUP BY id, level + ) + SELECT id, level FROM breadcrumb_with_count WHERE level > 0 AND count = 1 ORDER BY id + ", post_id: id).to_a replies.map! { |r| { id: r["id"].to_i, level: r["level"].to_i } } secured_ids = Post.secured(guardian).where(id: replies.map { |r| r[:id] }).pluck(:id).to_set - replies.reject { |r| r[:id] == id || !secured_ids.include?(r[:id]) } + replies.reject { |r| !secured_ids.include?(r[:id]) } end def revert_to(number) diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index 2d5718ae59c..11fc04988a1 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -785,20 +785,23 @@ describe Post do let!(:p3) { Fabricate(:post, topic: topic, post_number: 3) } let!(:p4) { Fabricate(:post, topic: topic, post_number: 4, reply_to_post_number: 2) } let!(:p5) { Fabricate(:post, topic: topic, post_number: 5, reply_to_post_number: 4) } + let!(:p6) { Fabricate(:post, topic: topic, post_number: 6) } before { PostReply.create!(post: p1, reply: p2) PostReply.create!(post: p2, reply: p4) - PostReply.create!(post: p3, reply: p5) + PostReply.create!(post: p2, reply: p6) # simulates p6 quoting p2 + PostReply.create!(post: p3, reply: p5) # simulates p5 quoting p3 PostReply.create!(post: p4, reply: p5) } it "returns the reply ids and their level" do - expect(p1.reply_ids).to eq([{ id: p2.id, level: 1 }, { id: p4.id, level: 2 }]) - expect(p2.reply_ids).to eq([{ id: p4.id, level: 1 }]) - expect(p3.reply_ids).to be_empty # no replies - expect(p4.reply_ids).to be_empty # not a direct reply - expect(p5.reply_ids).to be_empty # last post + expect(p1.reply_ids).to eq([{ id: p2.id, level: 1 }, { id: p4.id, level: 2 }, { id: p6.id, level: 2 }]) + expect(p2.reply_ids).to eq([{ id: p4.id, level: 1 }, { id: p6.id, level: 1 }]) + expect(p3.reply_ids).to be_empty # has no replies + expect(p4.reply_ids).to be_empty # p5 replies to 2 posts (p4 and p3) + expect(p5.reply_ids).to be_empty # has no replies + expect(p6.reply_ids).to be_empty # last post end end