From 171635ceac91f79373271d563246c1fad9c50ebc Mon Sep 17 00:00:00 2001
From: Jem Gillam <6413628+jemgillam@users.noreply.github.com>
Date: Thu, 10 Oct 2024 16:27:17 +0100
Subject: [PATCH 1/7] Rename evaluating
---
postgraphile/website/postgraphile/evaluating.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/postgraphile/website/postgraphile/evaluating.md b/postgraphile/website/postgraphile/evaluating.md
index 7caedcfb6c..02c2ee6e51 100644
--- a/postgraphile/website/postgraphile/evaluating.md
+++ b/postgraphile/website/postgraphile/evaluating.md
@@ -1,8 +1,8 @@
---
layout: page
path: /postgraphile/evaluating/
-title: Evaluating
-fullTitle: Evaluating PostGraphile For Your Project
+title: Adoption Analysis
+fullTitle: PostGraphile Adoption Analysis
---
Hopefully you’ve been convinced that PostGraphile serves an awesome GraphQL API,
From 3bf5a93a051006bf9483e109f5e62f7558b9a052 Mon Sep 17 00:00:00 2001
From: Jem Gillam <6413628+jemgillam@users.noreply.github.com>
Date: Thu, 10 Oct 2024 16:35:12 +0100
Subject: [PATCH 2/7] New eval page
---
postgraphile/website/postgraphile/limit-eval.md | 6 ++++++
postgraphile/website/sidebars.js | 1 +
2 files changed, 7 insertions(+)
create mode 100644 postgraphile/website/postgraphile/limit-eval.md
diff --git a/postgraphile/website/postgraphile/limit-eval.md b/postgraphile/website/postgraphile/limit-eval.md
new file mode 100644
index 0000000000..e798f58dd1
--- /dev/null
+++ b/postgraphile/website/postgraphile/limit-eval.md
@@ -0,0 +1,6 @@
+---
+layout: page
+path: /postgraphile/limit-eval/
+title: Limit eval()
+fullTitle: Limitations of using eval()
+---
diff --git a/postgraphile/website/sidebars.js b/postgraphile/website/sidebars.js
index c2ba673b04..5bf01a3ec0 100644
--- a/postgraphile/website/sidebars.js
+++ b/postgraphile/website/sidebars.js
@@ -146,6 +146,7 @@ const sidebars = {
"jwk-verification",
"default-role",
"testing-jest",
+ "limit-eval",
"bundling-webpack",
"multiple-schemas",
"running-postgraphile-in-docker",
From 8d4ab8e6e72328ac96e025b1a41a32f683c2ad08 Mon Sep 17 00:00:00 2001
From: Jem Gillam <6413628+jemgillam@users.noreply.github.com>
Date: Thu, 10 Oct 2024 16:56:48 +0100
Subject: [PATCH 3/7] Fix sidebar headings margins
---
postgraphile/website/src/css/custom.css | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/postgraphile/website/src/css/custom.css b/postgraphile/website/src/css/custom.css
index b5253e9f89..e97e46b7eb 100644
--- a/postgraphile/website/src/css/custom.css
+++ b/postgraphile/website/src/css/custom.css
@@ -103,6 +103,11 @@ figure figcaption {
background-color: var(--docusaurus-highlighted-code-line-bg);
}
*/
+.theme-doc-sidebar-item-link h4 {
+ margin-bottom: 5px;
+ margin-top: 5px;
+ padding-top: 5px;
+}
.ul-check li {
background-image: url("../../static/img/check.svg");
background-repeat: no-repeat;
From 8242e1d1105a547dd7e64e70b0e03f404c791fd4 Mon Sep 17 00:00:00 2001
From: Jem Gillam <6413628+jemgillam@users.noreply.github.com>
Date: Fri, 11 Oct 2024 12:28:34 +0100
Subject: [PATCH 4/7] Move eval page to operation
---
postgraphile/website/postgraphile/{limit-eval.md => eval.md} | 4 ++--
postgraphile/website/sidebars.js | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
rename postgraphile/website/postgraphile/{limit-eval.md => eval.md} (53%)
diff --git a/postgraphile/website/postgraphile/limit-eval.md b/postgraphile/website/postgraphile/eval.md
similarity index 53%
rename from postgraphile/website/postgraphile/limit-eval.md
rename to postgraphile/website/postgraphile/eval.md
index e798f58dd1..842587ac5d 100644
--- a/postgraphile/website/postgraphile/limit-eval.md
+++ b/postgraphile/website/postgraphile/eval.md
@@ -1,6 +1,6 @@
---
layout: page
-path: /postgraphile/limit-eval/
-title: Limit eval()
+path: /postgraphile/eval/
+title: Eval()
fullTitle: Limitations of using eval()
---
diff --git a/postgraphile/website/sidebars.js b/postgraphile/website/sidebars.js
index 5bf01a3ec0..494ef202cf 100644
--- a/postgraphile/website/sidebars.js
+++ b/postgraphile/website/sidebars.js
@@ -92,6 +92,7 @@ const sidebars = {
},
items: ["subscriptions", "live-queries"],
},
+ "eval",
"background-tasks",
"exporting-schema",
"reserved-keywords",
@@ -146,7 +147,6 @@ const sidebars = {
"jwk-verification",
"default-role",
"testing-jest",
- "limit-eval",
"bundling-webpack",
"multiple-schemas",
"running-postgraphile-in-docker",
From fc366b6652342bce3337538d441942a7f4a35e30 Mon Sep 17 00:00:00 2001
From: Jem Gillam <6413628+jemgillam@users.noreply.github.com>
Date: Fri, 11 Oct 2024 14:42:35 +0100
Subject: [PATCH 5/7] Draft article
---
postgraphile/website/postgraphile/eval.md | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/postgraphile/website/postgraphile/eval.md b/postgraphile/website/postgraphile/eval.md
index 842587ac5d..96df615c2a 100644
--- a/postgraphile/website/postgraphile/eval.md
+++ b/postgraphile/website/postgraphile/eval.md
@@ -1,6 +1,27 @@
---
layout: page
path: /postgraphile/eval/
-title: Eval()
+title: eval()
fullTitle: Limitations of using eval()
---
+
+Input steps can have an `eval()` method which evaluates the real value of the step during planning. However, using `$__inputStep.eval()` during query planning can result in 2x potential plans being generated — a behavior which is likely unwanted due to the impact on planning time and only a small fraction of the generated plans actually being used.
+
+# Alternatives to `eval()`
+
+All the eval methods have a cost, but being specific about how the plans should branch reduces the number of plans generated and reduces the resulting planning time and code complexity.
+
+`evalHas(key)`
+Results in two plans being generated, the plans branch on whether a particular `key` is set or not.
+
+`evalIs(val)`
+Results in two plans being generated: one where `$__inputStep`'s value `=== val`, and one where it doesn't.
+
+`evalLength()`
+Results in one plan being generated for each length of the list being used.
+
+:::info
+
+We are currently _evaulating_ whether to remove `eval()` completely from PostGraphile and Grafast in a future version (after Version 5). This is another reason why you should choose one of the alternatives above!
+
+:::
From e6b81848b2e18b976f7242d54dff1fd409b4e18b Mon Sep 17 00:00:00 2001
From: Jem Gillam <6413628+jemgillam@users.noreply.github.com>
Date: Fri, 11 Oct 2024 14:48:42 +0100
Subject: [PATCH 6/7] Add gh issue link
---
postgraphile/website/postgraphile/eval.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/postgraphile/website/postgraphile/eval.md b/postgraphile/website/postgraphile/eval.md
index 96df615c2a..acdee6e162 100644
--- a/postgraphile/website/postgraphile/eval.md
+++ b/postgraphile/website/postgraphile/eval.md
@@ -22,6 +22,6 @@ Results in one plan being generated for each length of the list being used.
:::info
-We are currently _evaulating_ whether to remove `eval()` completely from PostGraphile and Grafast in a future version (after Version 5). This is another reason why you should choose one of the alternatives above!
+We are currently _evaulating_ whether to remove `eval()` completely from PostGraphile and Grafast in a future version (after Version 5), see [issue #2060](https://github.com/graphile/crystal/issues/2060). This is another reason why you should choose one of the alternatives above!
:::
From 958dbdfb7a39231a21b9431991fa5646e12e08a3 Mon Sep 17 00:00:00 2001
From: Benjie Gillam
Date: Wed, 16 Oct 2024 12:00:56 +0100
Subject: [PATCH 7/7] Move to Grafast website and add more detail
---
grafast/website/grafast/access-control.mdx | 4 +
grafast/website/grafast/eval.mdx | 129 ++++++++++++++++++
grafast/website/grafast/plan-diagrams.mdx | 1 +
grafast/website/grafast/polymorphism.mdx | 4 +
.../grafast/production-considerations.md | 4 +
postgraphile/website/postgraphile/eval.md | 27 ----
.../website/postgraphile/evaluating.md | 6 +-
postgraphile/website/sidebars.js | 1 -
8 files changed, 146 insertions(+), 30 deletions(-)
create mode 100644 grafast/website/grafast/eval.mdx
delete mode 100644 postgraphile/website/postgraphile/eval.md
diff --git a/grafast/website/grafast/access-control.mdx b/grafast/website/grafast/access-control.mdx
index 37d47f690e..4299becdf7 100644
--- a/grafast/website/grafast/access-control.mdx
+++ b/grafast/website/grafast/access-control.mdx
@@ -1,3 +1,7 @@
+---
+sidebar_position: 12
+---
+
# Access control
Access control is typically the responsibility of your business logic layer, as
diff --git a/grafast/website/grafast/eval.mdx b/grafast/website/grafast/eval.mdx
new file mode 100644
index 0000000000..b3f03d4bf8
--- /dev/null
+++ b/grafast/website/grafast/eval.mdx
@@ -0,0 +1,129 @@
+---
+title: Plan branching via `.eval*()`
+sidebar_position: 13
+---
+
+import Mermaid from "@theme/Mermaid";
+
+Input steps (that is: steps that represent inputs to your GraphQL operations
+such as `context()` and steps representing field arguments accessed through
+`FieldArgs`) have a suite of `eval*` methods used to constrain query plans
+based on the concrete values seen at runtime. For example, if the user has used
+the `@skip` or `@include` directives to turn on/off sections of their query,
+it's more efficient to have separate plans for this so that we don't overfetch
+data:
+
+```graphql
+query GetUserDetails($includeFriends: Boolean! = false) {
+ currentUser {
+ name
+ avatarUrl
+ friends(first: 100) @include(if: $includeFriends) {
+ name
+ avatarUrl
+ }
+ }
+}
+```
+
+ᐸ2.currentUserIdᐳ"}}:::plan
+ Z__Value2["__Value[2∈0] ➊
ᐸcontextᐳ"]:::plan
+ Z__Value2 --> ZAccess6
+ ZLoad7[["Load[7∈0] ➊
ᐸuserByIdᐳ"]]:::plan
+ ZAccess6 --> ZLoad7
+ end
+ subgraph "includeFriends=true"
+ Access6{{"Access[6∈0] ➊
ᐸ2.currentUserIdᐳ"}}:::plan
+ __Value2["__Value[2∈0] ➊
ᐸcontextᐳ"]:::plan
+ __Value2 --> Access6
+ Load7[["Load[7∈0] ➊
ᐸuserByIdᐳ"]]:::plan
+ Access6 --> Load7
+ Load10[["Load[10∈0] ➊
ᐸfriendshipsByUserIdᐳ"]]:::plan
+ Access6 --> Load10
+ __Item14[/"__Item[14∈3]
ᐸ10ᐳ"\]:::itemplan
+ Load10 ==> __Item14
+ Access16{{"Access[16∈3]
ᐸ14.friend_idᐳ"}}:::plan
+ __Item14 --> Access16
+ Load17[["Load[17∈3]
ᐸuserByIdᐳ"]]:::plan
+ Access16 --> Load17
+ end
+ classDef bucket0 stroke:#696969
+ class Bucket0,__Value2,__Value4,Access6,Load7,Load10 bucket0
+ class ZAccess6,Z__Value2,ZLoad7 bucket0
+ classDef bucket1 stroke:#00bfff
+ class Bucket1 bucket1
+ classDef bucket3 stroke:#ffa500
+ class Bucket3,__Item14,Access16,Load17 bucket3
+ classDef bucket4 stroke:#0000ff
+ class Bucket4 bucket4
+`}
+/>
+
+Grafast does this branching automatically by evaluating whether the value of
+`$includeFriends` is `true` or not (`$includeFriends.evalIs(true)`) before
+deciding which plan resolvers to call. This requirement is then stored as a
+constraint on the plan, such that the next request may reuse the plan only if
+the result of `$includeFriends.evalIs(true)` retains the same value. Thus this
+allows the same operation to actually generate two plans, one for
+`includeFriends=false` and one for `includeFriends=true`.
+
+:::note Plans are established on demand
+
+Grafast doesn't plan all possible values of `includeFriends` up front; instead
+it just evaluates the version it's currently handling (e.g.
+`includeFriends=false`) and will only plan the alternative when a request comes
+through for it (e.g. with `includeFriends=true`).
+
+:::
+
+Note that this can result in up to 2x potential plans being
+generated for the same operation, where `x` is the number of unique
+`@skip`/`@include` variables. This reduces the "reusability" of the operation
+plans - each time a new request comes through that doesn't match the
+constraints of any pre-existing plans, we have to plan the entire operation
+again from scratch.
+
+## The `.eval*()` family
+
+It is possible, **but recommended against**, for user plan resolvers to use
+this functionality baked into Grafast. All the eval methods have a cost; being
+specific about the exact circumstances under which the plan should fork reduces
+the number of potential branches and reduces the resulting planning time and
+code complexity.
+
+Note that only input-related steps implement any of these methods, and even
+then these steps only implement the methods appropriate to them. Tread
+carefully.
+
+- `.evalIs(val)` - Branches the plan into two forks: one where `$__inputStep`'s
+ value `=== val`, and one where it isn't.
+- `.evalHas(key)` - Branches the plan into two forks based on whether the
+ specified `key` is set or not.
+- `.evalLength()` - Branches the plan into a fork for each length of the list
+ seen at runtime.
+- `.evalIsEmpty()` - Branches the plan into a fork based on whether the step
+ represents an "empty" object (an object with no attributes) or not.
+- `.eval()` - Branches the plan into a fork for every possible value - **you
+ should almost never use `.eval()` in your plan resolvers**, instead choose one
+ of the more specific options above (or, better, avoid `.eval*()` altogether!)
+
+:::info `.eval*()` might be going away!
+
+We are currently _evaulating_ whether to remove `.eval*()` completely from
+Grafast in a future version, see [issue
+#2060](https://github.com/graphile/crystal/issues/2060).
+
+**If possible, you should avoid branching the plan and instead incorporate the
+required logic into your step classes directly.**
+
+:::
diff --git a/grafast/website/grafast/plan-diagrams.mdx b/grafast/website/grafast/plan-diagrams.mdx
index ec6d73b01e..71b13419f2 100644
--- a/grafast/website/grafast/plan-diagrams.mdx
+++ b/grafast/website/grafast/plan-diagrams.mdx
@@ -1,5 +1,6 @@
---
title: "Plan diagrams"
+sidebar_position: 10
---
import Mermaid from "@theme/Mermaid";
diff --git a/grafast/website/grafast/polymorphism.mdx b/grafast/website/grafast/polymorphism.mdx
index 1dd79a2f93..f190fc3a4c 100644
--- a/grafast/website/grafast/polymorphism.mdx
+++ b/grafast/website/grafast/polymorphism.mdx
@@ -1,3 +1,7 @@
+---
+sidebar_position: 11
+---
+
# Polymorphism
GraphQL has two types of output polymorphism currently: interfaces and unions.
diff --git a/grafast/website/grafast/production-considerations.md b/grafast/website/grafast/production-considerations.md
index 14f8f66e09..e42b71e342 100644
--- a/grafast/website/grafast/production-considerations.md
+++ b/grafast/website/grafast/production-considerations.md
@@ -1,3 +1,7 @@
+---
+sidebar_position: 14
+---
+
# Production Considerations
## Overview
diff --git a/postgraphile/website/postgraphile/eval.md b/postgraphile/website/postgraphile/eval.md
deleted file mode 100644
index acdee6e162..0000000000
--- a/postgraphile/website/postgraphile/eval.md
+++ /dev/null
@@ -1,27 +0,0 @@
----
-layout: page
-path: /postgraphile/eval/
-title: eval()
-fullTitle: Limitations of using eval()
----
-
-Input steps can have an `eval()` method which evaluates the real value of the step during planning. However, using `$__inputStep.eval()` during query planning can result in 2x potential plans being generated — a behavior which is likely unwanted due to the impact on planning time and only a small fraction of the generated plans actually being used.
-
-# Alternatives to `eval()`
-
-All the eval methods have a cost, but being specific about how the plans should branch reduces the number of plans generated and reduces the resulting planning time and code complexity.
-
-`evalHas(key)`
-Results in two plans being generated, the plans branch on whether a particular `key` is set or not.
-
-`evalIs(val)`
-Results in two plans being generated: one where `$__inputStep`'s value `=== val`, and one where it doesn't.
-
-`evalLength()`
-Results in one plan being generated for each length of the list being used.
-
-:::info
-
-We are currently _evaulating_ whether to remove `eval()` completely from PostGraphile and Grafast in a future version (after Version 5), see [issue #2060](https://github.com/graphile/crystal/issues/2060). This is another reason why you should choose one of the alternatives above!
-
-:::
diff --git a/postgraphile/website/postgraphile/evaluating.md b/postgraphile/website/postgraphile/evaluating.md
index 02c2ee6e51..703182d483 100644
--- a/postgraphile/website/postgraphile/evaluating.md
+++ b/postgraphile/website/postgraphile/evaluating.md
@@ -1,10 +1,12 @@
---
layout: page
path: /postgraphile/evaluating/
-title: Adoption Analysis
-fullTitle: PostGraphile Adoption Analysis
+title: Evaluating PostGraphile
+fullTitle: Evaluating PostGraphile For Your Project
---
+# Does PostGraphile Fit Your Project?
+
Hopefully you’ve been convinced that PostGraphile serves an awesome GraphQL API,
but now let’s take a more critical look at whether or not you should adopt
PostGraphile for your project.
diff --git a/postgraphile/website/sidebars.js b/postgraphile/website/sidebars.js
index 494ef202cf..c2ba673b04 100644
--- a/postgraphile/website/sidebars.js
+++ b/postgraphile/website/sidebars.js
@@ -92,7 +92,6 @@ const sidebars = {
},
items: ["subscriptions", "live-queries"],
},
- "eval",
"background-tasks",
"exporting-schema",
"reserved-keywords",