joist-orm / joist-orm/joist-orm

Separate preloading joins from filtering joins

Open
#1,470 2 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.