joist-orm / joist-orm/joist-orm
Separate preloading joins from filtering joins
- Dominant language
- TypeScript
- Stars
- 385
- Forks
- 26
- Avg merge
- 8h 55m
- Merged PRs (30d)
- 42
Description
When doing something like:
```
em.find(Author, { books: { title: "b1" } }, { populate: "comments" });
```
The generated query has two joins:
- a JOIN into `books` for evaling the title=b1, and
- a JOIN into `comments` for doing the preload
However, this means that (unless the postgres query planner is smart enough to avoid it) we're calculating the preload/rollup of "every author's list of comments" for all `authors` rows, even the ones that won't end up matching the `WHERE title=b1` predicate.
Instead, we should only do the preloading for matched rows, i.e. something like:
```sql
select
a.*, ...preload columns...
from (
select a.* from ...filtering joins...
)
...preload joins...
```
This current query structure isn't supported as-in in the `ParsedFindQuery` AST, b/c we'll need an "inner table" type, unless maybe we use a CTE? 🤔
---
I've benchmarked a "current query" vs. "proposed query" in our production database and found similar execution times & `EXPLAIN`s, so maybe this is not nece
```sql
-- current query, both joins in one query
select * from item_template_items iti
cross join lateral (
select json_agg(json_build_object("id", pi.id)) from project_items pi
where pi.item_template_item_id = iti.id
)
where iti.id > 10000 limit 2000;
-- proposed query, move predicate to an inner query
select * from (
select * from item_template_items iti where iti.id > 10000 limit 2000
) iti
cross join lateral (
select json_agg(json_build_object("id", pi.id)) from project_items pi
where pi.item_template_item_id = iti.id
);
```
Contributor guide
Assessment
This issue has not been assessed yet.