Gianluca Ghettini
Gianluca Ghettini

Reputation: 11678

Database schema design for posts, comments and replies

In my previous project I had posts and comments as two tables:

post

comment

Now I've got to design replies to comments. The replies is just one level, so users can only reply to comments, not to replies. The tree structure is only 1 level deep. My first idea was to use the same comment table for both comments and replies. I added a new column though:

comment

Replies have parentcommentid set to the parent comment they belong. Parent comments don't have it (null)

Retrieving comments for a given post is simple:

but this time I need another query to find out the comment replies. This has to be done for each comment:

This doesn't seem to be a good solution, is there a way to have a single query which returns the complete list of comments/replies in the correct order? (dictated by the timestamp and the nesting)

Upvotes: 10

Views: 23900

Answers (3)

lugreen
lugreen

Reputation: 102

I know this reply is years too late, but hopefully it will help others facing this problem now.

I came up with single table and a single query that returns all the results in the correct order that I use on my own site, it's slightly different but the logic could be used to fit this question.

table name : comments

  • id / varchar(32)
  • userid / int(10)
  • comment / text
  • ordering / int(10)
  • ordering_secondary / int(10)
  • source / tinyint(4)
  • state / tinyint(4)
  • created / timestamp
  • edited / timestamp

There is a primary key on (id, ordering, ordering_secondary, source), so no duplicate of these four columns combined will insert.

Inserting a comment you first check "ordering" and increment the new comment by 1.

SELECT ordering FROM comments WHERE id="page id" AND source=0 ORDER BY ordering DESC LIMIT 1

"source" column will be "0" for parent, "1" for a reply for example. So just insert the comment with the ordering value incremented by 1 for each comment on a specific id.

When inserting a reply comment, use the same "ordering" value as the parent but increment the "ordering_secondary" column.

SELECT ordering FROM comments WHERE id="page id" AND source=1 AND ordering="parent comment ordering" ORDER BY ordering DESC LIMIT 1

So the data would look like :

enter image description here

In the table there are two parent comments and two replies to the second parent comment. No replies to first parent comment.

This approach is obviously slightly more overhead on inserts as you have to look up the ordering value of the last comment on an "id" but querying the data is simple.

SELECT * FROM comments WHERE id=? ORDER BY ordering DESC, ordering_secondary ASC LIMIT 30

Upvotes: 2

getup8
getup8

Reputation: 8248

If you're using a database that supports JSON or object aggregation, you can get a nicer result from the query where each top-level comment is a row (and is not duplicated), and the replies are nested in an array/JSON within each row.

This gives you flexibility with what you do with it and also makes it easier to ensure the ordering and nesting is correct.

An example using Postgres:

SELECT
  p.id AS post_id,
  c.id AS comment_id,
  c.message,
  JSON_AGG(
    JSON_BUILD_OBJECT('comment', r.comment, 'timestamp', r.timestamp)
    ORDER BY r.timestamp
  ) AS child_comments
FROM
  post AS p
  INNER JOIN comment AS c
    ON c.post_id = p.id
  LEFT JOIN comment AS r
    ON r.parent_id = c.id
WHERE
  post.id = <some id>
  AND c.parent_id IS NULL
GROUP BY
  post.id,
  c.id,
  c.message
ORDER BY
  c.timestamp DESC
;

Note that, as above, this example will only retrieve the top-level and their first-level replies. It won't get replies to replies. You can use recursive commands or additional subqueries to do that.

Upvotes: 1

Rupal Javiya
Rupal Javiya

Reputation: 579

You may use join and achieve result in single query like I provided below:

  SELECT *, cc.message as replied_message 
    FROM `post` 
    JOIN comment as c 
      ON c.postid = post.id 
    JOIN comment as cc 
      ON cc.id = c.parentcommentid 
ORDER BY c.timestamp DESC, cc.timestamp DESC;

Please note that, it works correctly only if 1 comment have 1 reply only.multiple replies on single comment will not support by this query

Upvotes: 4

Related Questions