How to load multiple one-to-many relations in Typeorm / PostgreSQL?

Viewed 384

I have 3 tables A, B, C.

  • A is the main table
  • B & C have a many-to-one relation with A

Let's say the data is as follow:

Table A                  Table B                            Table C
|  id   | name  |        |  id   | a_id (FK) | value |      |  id   | a_id (FK) | value |
| :---: | :---: |        | :---: | :-------: | :---: |      | :---: | :-------: | :---: |
|  a0   |  A0   |        |  b0   |    a0     |  B0   |      |  c0   |    a0     |  C0   |
|  a1   |  A1   |        |  b1   |    a0     |  B1   |      |  c1   |    a0     |  C1   |
                         |  b2   |    a1     |  B2   |

I want to retrieve the records in A with their relations aggregated into an array or a json, like so:

id name b c
a0 A0 [[b0, B0], [b1, B1]] [[c0, C0], [c1, C1]]
a1 A1 [[b2, B2]] []

If I use the classic TypeORM .find():

const items = await aRepository.find({ relations: ["B", "C"] });

I will quickly run out of memory because TypeORM is simply making a query with LEFT JOINs which will return duplicated lines before aggregating them. There's an issue about it here.

This leaves me writing a custom SQL statement with nested SELECTs:

SELECT
  A.id, A.name,
  (
    SELECT json_agg(bs)
    FROM (
      SELECT id, "value"
      FROM B WHERE B.a_id = A.id
    ) bs
  ) AS b,
  (
    SELECT json_agg(cs)
    FROM (
      SELECT id, "value"
      FROM C WHERE C.c_id = A.id
    ) cs
  ) AS c
FROM A

Without an index on B.a_id and C.a_id, it takes ~230ms with a 200 records in A and +/-10 records in B and C for each A record.
By creating an index on B.a_id and C.a_id, I can bring this down to 30ms.

Is there still a better way to do this?

0 Answers
Related