0

I have an app with users; the users can create bookmarks, and each bookmark can have multiple tags.

So, if I'm not mistaken in the notation: user =* bookmark *=* tag

Now I try to retrieve all users with a JSON array with the tags they used. I need a JSON array because I may need to retrieve the name or other data from the tags or the bookmarks:

SELECT
  `user`.id,
  JSON_ARRAYAGG(
    JSON_OBJECT(
      "id", tag.id
    )
  ) AS tags,
  JSON_ARRAYAGG(
    JSON_OBJECT(
      "id", bookmark.id
    )
  ) AS bookmarks
FROM tag
INNER JOIN bookmark_tag ON bookmark_tag.tag_id = tag.id
INNER JOIN bookmark ON bookmark.id = bookmark_tag.bookmark_id
INNER JOIN `user` ON user.id = bookmark.user_id
GROUP BY `user`.id;

But this returns duplicated ids both for bookmarks and tags:

Here is a DB fiddle to play with: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=56ac0313fe2a3bf7af32a3ed9a3ea78a

How can I do that?

Emille C.
  • 562
  • 1
  • 7
  • 23

2 Answers2

0

This answers the original question.

You need neither the tags nor the users table for your query, so it can be much simplified.

DISTINCT is not allowed with JSON_AGG(). But you can use a subquery:

 SELECT b.user_id, JSON_ARRAYAGG( b.tag_id) AS tags
 FROM (SELECT DISTINCT b.user_id, bt.tag_id
       FROM bookmark_tag bt JOIN
            bookmark b 
            ON b.id = bt.bookmark_id
      ) b
 GROUP BY b.user_id;

Here is a db<>fiddle.

Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786
0

Ended up doing this. This way I can add more JSON objects to the main select,

SELECT
  `user`.`id`,
  (
    SELECT
      JSON_ARRAYAGG(
        JSON_OBJECT(
          'id', subQuery.id,
          'name', subQuery.name
        )
      )
      FROM (
        SELECT DISTINCT
        bookmark.user_id,
          tag.id,
          tag.name
        FROM tag
        INNER JOIN bookmark_tag ON bookmark_tag.tag_id = tag.id
        INNER JOIN bookmark ON bookmark.id = bookmark_tag.bookmark_id
        INNER JOIN `user` ON user.id = bookmark.user_id
      ) subQuery
      WHERE user.id = subQuery.user_id
  ) AS tags,
  (
    SELECT
      JSON_ARRAYAGG(
        JSON_OBJECT(
          'id', bookmark.id
        )
      )
    FROM bookmark
    WHERE user.id = bookmark.user_id
  ) AS bookmarks
  FROM `user`
  GROUP BY `user`.`id`
;

Please let me know if this is really wrong.

Here is a fiddle: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=abedb69ae4db65dd344f81a7dadb565a

Emille C.
  • 562
  • 1
  • 7
  • 23