Source code and results are available, for JS driver and for native driver. First I populated the database with two tables, posts and comments, where each comment has a related post. I generated 10000 posts, each with 100 comments. I tested these queries.
|JOIN + array_agg||2828.6||2967.4||3919.1||2956.3||4015.4||5091.2|
|JOIN + to_json(array_agg)||2847.6||2822.4||4305.4||4278.5|
|JOIN + to_jsonb(array_agg)||3640||4246.4||5015.8||5824.2|
|SUBQUERY + array_agg||2696.4||2726.3||3530.5||3030.1||4125.5||4513.3|
|SUBQUERY + to_json(array_agg)||2690.4||2729.9||3751.2||3765.6|
|SUBQUERY + to_jsonb(array_agg)||3525.8||4776.6||4828.2||6047.6|
Green color marks combinations I would recommend. Red color shows JSONB should be avoided.
My insights from the benchmark are:
Conclusion. If you are using PostgreSQL fields which can be reasonably converted to JSON and you are using node.js, it seems you should simply always convert results to JSON before sending them over to the client. If you want to fetch related documents, do not do JOIN but do a subquery and aggregate results into an array. If you are always converting results to JSON, then this is it. If not, then at least convert that aggregated array to JSON.