Skip to content

Commit

Permalink
chore(query-optimization-performance): Add () to queries (#5806)
Browse files Browse the repository at this point in the history
  • Loading branch information
janpio authored Apr 4, 2024
1 parent c37bc05 commit 2333c20
Showing 1 changed file with 14 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Alternatively, if you are only interested in the time taken to run a query, you

The n+1 problem occurs when you loop through the results of a query and perform one additional query **per result**, resulting in `n` number of queries plus the original (n+1). This is a common problem with ORMs, particularly in combination with GraphQL, because it is not always immediately obvious that your code is generating inefficient queries.

### Solving n+1 in GraphQL with <inlinecode>findUnique</inlinecode> and Prisma Client's dataloader
### Solving n+1 in GraphQL with <inlinecode>findUnique()</inlinecode> and Prisma Client's dataloader

<div class="videoWrapper">

Expand All @@ -36,9 +36,9 @@ The n+1 problem occurs when you loop through the results of a query and perform

</div>

The Prisma Client dataloader automatically **batches** `findUnique` queries that ✔ occur in the same tick and ✔ have the same `where` and `include` parameters.
The Prisma Client dataloader automatically **batches** `findUnique()` queries that ✔ occur in the same tick and ✔ have the same `where` and `include` parameters.

Automatic batching of `findUnique` is particularly useful in a **GraphQL context**. GraphQL runs a separate resolver function for every field, which can make it difficult to optimize a nested query.
Automatic batching of `findUnique()` is particularly useful in a **GraphQL context**. GraphQL runs a separate resolver function for every field, which can make it difficult to optimize a nested query.

For example - the following GraphQL runs the `allUsers` resolver to get all users, and the `posts` resolver **once per user** to get each user's posts (n+1):

Expand Down Expand Up @@ -81,7 +81,7 @@ This results in a single SQL query:
}
```

However, the resolver function for `posts` is then invoked **once per user**. This results in a `findMany` query **✘ per user** rather than a single `findMany` to return all posts by all users (expand CLI output to see queries).
However, the resolver function for `posts` is then invoked **once per user**. This results in a `findMany()` query **✘ per user** rather than a single `findMany()` to return all posts by all users (expand CLI output to see queries).

<CodeWithResult>
<cmd>
Expand Down Expand Up @@ -150,7 +150,7 @@ const User = objectType({
</cmdResult>
</CodeWithResult>

Instead, use `findUnique` in combination with [the fluent API](/orm/prisma-client/queries/relation-queries#fluent-api) (`.posts()`) as shown to return a user's posts. Even though the resolver is called once per user, the Prisma dataloader in Prisma Client **✔ batches the `findUnique` queries**.
Instead, use `findUnique()` in combination with [the fluent API](/orm/prisma-client/queries/relation-queries#fluent-api) (`.posts()`) as shown to return a user's posts. Even though the resolver is called once per user, the Prisma dataloader in Prisma Client **✔ batches the `findUnique()` queries**.

<CodeWithResult>
<cmd>
Expand Down Expand Up @@ -209,21 +209,21 @@ const User = objectType({
</cmdResult>
</CodeWithResult>

If the `posts` resolver is invoked once per user, the dataloader in Prisma Client groups `findUnique` queries with the same parameters and selection set. Each group is optimized into a single `findMany`.
If the `posts` resolver is invoked once per user, the dataloader in Prisma Client groups `findUnique()` queries with the same parameters and selection set. Each group is optimized into a single `findMany()`.

#### Do I have to use the fluent API to enable batching of queries?

It may seem counterintitive to use a `prisma.user.findUnique(...).posts()` query to return posts instead of `prisma.posts.findMany()` - particularly as the former results in two queries rather than one.

The **only** reason you need to use the fluent API (`user.findUnique(...).posts()`) to return posts is that the dataloader in Prisma Client batches `findUnique` queries and does not currently [batch `findMany` queries](https://github.com/prisma/prisma/issues/1477).
The **only** reason you need to use the fluent API (`user.findUnique(...).posts()`) to return posts is that the dataloader in Prisma Client batches `findUnique()` queries and does not currently [batch `findMany()` queries](https://github.com/prisma/prisma/issues/1477).

When the dataloader batches `findMany` queries, you no longer need to use `findUnique` with the fluent API in this way.
When the dataloader batches `findMany()` queries, you no longer need to use `findUnique()` with the fluent API in this way.

### n+1 in other contexts

The n+1 problem is most commonly seen in a GraphQL context because you have to find a way to optimize a single query across multiple resolvers. However, you can just as easily introduce the n+1 problem by looping through results with `forEach` in your own code.

The following code results in n+1 queries - one `findMany` to get all users, and one `findMany` **per user** to get each user's posts:
The following code results in n+1 queries - one `findMany()` to get all users, and one `findMany()` **per user** to get each user's posts:

<CodeWithResult expanded="{true}">
<cmd>
Expand Down Expand Up @@ -326,10 +326,10 @@ SELECT "public"."Post"."id", "public"."Post"."createdAt", "public"."Post"."updat

It is generally more performant to read and write large amounts of data in bulk - for example, inserting 50,000 records in batches of 1000 rather than as 50,000 separate inserts. Prisma Client supports the following bulk queries:

- [`createMany`](/orm/reference/prisma-client-reference#createmany)
- [`deleteMany`](/orm/reference/prisma-client-reference#deletemany)
- [`updateMany`](/orm/reference/prisma-client-reference#updatemany)
- [`findMany`](/orm/reference/prisma-client-reference#findmany)
- [`createMany()`](/orm/reference/prisma-client-reference#createmany)
- [`deleteMany()`](/orm/reference/prisma-client-reference#deletemany)
- [`updateMany()`](/orm/reference/prisma-client-reference#updatemany)
- [`findMany()`](/orm/reference/prisma-client-reference#findmany)

## Using <inlinecode>select</inlinecode> to limit number of columns returned

Expand All @@ -338,6 +338,6 @@ Using `select` to limit the number of columns that are returned is **unlikely to
- Tables with a large number of columns
- Large columns that are stored in a separate location on disk rather than a row, which results in an additional disk read

Furthermore, if you have a mature product with well established query patterns and finely tuned indexes, selecting a specific subset of fields may be beneficial as it avoids reading data from disk. However, in most cases, this level of performance tuning is only necessary at a certain scale.
Furthermore, if you have a mature product with well-established query patterns and finely tuned indexes, selecting a specific subset of fields may be beneficial as it avoids reading data from disk. However, in most cases, this level of performance tuning is only necessary at a certain scale.

<!-- Pagination -->

0 comments on commit 2333c20

Please sign in to comment.