Sample Header Ad - 728x90

Return recursive self joins in PostgreSQL with aggregated data

3 votes
1 answer
378 views
I have this simple table schema: SQL Fiddle **PostgreSQL 9.6 Schema Setup**: CREATE TABLE IF NOT EXISTS users( id INT NOT NULL PRIMARY KEY, email TEXT NOT NULL UNIQUE, password TEXT NOT NULL, first_name TEXT NOT NULL, last_name TEXT NOT NULL, is_active BOOLEAN DEFAULT FALSE, is_staff BOOLEAN DEFAULT FALSE, is_superuser BOOLEAN DEFAULT FALSE ); INSERT INTO users ( id, email, password, first_name, last_name, is_active, is_staff, is_superuser ) VALUES ( 1, 'sir@a.com', '23456ses', 'John', 'Idogun', true, true, true ), ( 2, 'ma@a.com', '23456ses', 'Nelson', 'Idogun', true, true, true ); -- articles table CREATE TABLE IF NOT EXISTS articles( id INT NOT NULL PRIMARY KEY, fore_image TEXT NULL, title TEXT NOT NULL, slug TEXT NOT NULL UNIQUE, content TEXT NOT NULL, is_published BOOLEAN DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), user_id INT NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); INSERT INTO articles ( id, title, slug, content, is_published, user_id ) VALUES ( 1, 'Upgrading from v0.4 to v0.5 of rust rocket', 'Upgrading from v0.4 to v0.5 of rust rocket', 'Upgrading from v0.4 to v0.5 of rust rocket', true, 1 ); CREATE TABLE IF NOT EXISTS comment( id INT NOT NULL PRIMARY KEY, article_id INT NOT NULL REFERENCES articles(id) ON DELETE CASCADE, user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE, responded_to_comment_id INT NULL REFERENCES comment(id) ON DELETE CASCADE, content TEXT NOT NULL ); INSERT INTO comment (id, article_id, user_id, responded_to_comment_id, content) VALUES (1, 1, 1, NULL, 'First comment'), ( 2, 1, 2, 1, 'First reply' ), (3, 1, 1, 2, 'Response to the first reply.'); **Query 1**: SELECT c.id AS c_id, c.content AS c_content, json_agg(r) as replies, u.id AS u_id FROM comment AS c JOIN users AS u ON c.user_id = u.id LEFT JOIN comment r ON r.id = c.responded_to_comment_id WHERE c.article_id = 1 GROUP BY c.id, u.id **Results **: | c_id | c_content | replies | u_id | |------|--------------------------|------------------------------------------------------------------------------------------------|------| | 1 | First comment | [null] | 1 | | 2 | First reply | [{"id":1,"article_id":1,"user_id":1,"responded_to_comment_id":null,"content":"First comment"}] | 2 | | 3 | Response to first reply. | [{"id":2,"article_id":1,"user_id":2,"responded_to_comment_id":1,"content":"First reply"}] | 1 | I am trying to write a query that selects comments under an article and recursively nests all comments having responded_to_comment_id inside their parent comment so I will have only one row returned: | c_id | c_content | replies | u_id | |------|---------------|-----------|------| | 1 | First comment | [...] | 1 | and that the results for the replies column will be something like this:
[
  {
    "id": 2,
    "user_id": 1,
    "responded_to_comment_id": 1,
    "content": "First reply",
    "replies": [
      {
        "id": 3,
        "article_id": 1,
        "user_id": 2,
        "responded_to_comment_id": 2,
        "content": "Response to the first reply."
      }
    ]
  }
]
Only top-level comments (comments whose responded_to_comment_id is NULL) should be at the top.
Asked by Sirjon (165 rep)
Mar 15, 2023, 10:18 PM
Last activity: Mar 19, 2023, 02:14 PM