From 74193b582c4a068183ffa49a1aaf11376e7a7d5d Mon Sep 17 00:00:00 2001 From: Stephen Mallette Date: Wed, 5 Aug 2026 17:51:39 +0000 Subject: [PATCH] Show optimization strategies firing in the Unnecessary Steps recipe The Unnecessary Steps anti-patterns recipe stated that TinkerPop rewrites these traversals automatically but offered no way to confirm it, never named the strategies involved, and gave no pointer to further reading. Add a runnable explain() example on the original queries so the rewrites are visible, name the IncidentToAdjacentStrategy and AdjacentToIncidentStrategy strategies that perform them, and cross-reference the explain() and profile() steps and the traversal strategies section of the reference documentation. Assisted-by: Kiro:claude-opus-4.8 --- docs/src/recipes/anti-patterns.asciidoc | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/src/recipes/anti-patterns.asciidoc b/docs/src/recipes/anti-patterns.asciidoc index 2734999d656..b4683a0ccea 100644 --- a/docs/src/recipes/anti-patterns.asciidoc +++ b/docs/src/recipes/anti-patterns.asciidoc @@ -154,6 +154,32 @@ g.V().hasLabel("person").out("created").dedup() g.V().hasLabel("software").inE("created").count() ---- +These rewrites can be observed rather than taken on faith. The `explain()`-step reports how a traversal is compiled once +all registered traversal strategies have been applied, which makes the optimizations visible. Running `explain()` on the +two original (unoptimized) queries shows the relevant strategies firing: + +[gremlin-groovy,modern] +---- +g.V().hasLabel("person").outE("created").inV().dedup().explain() +g.V().hasLabel("software").inE("created").outV().count().explain() +---- + +Each row of the explanation is the state of the traversal after the strategy named in the first column has been applied. +The second column is the strategy category: [D]ecoration, [O]ptimization, [P]rovider optimization, +[F]inalization, or [V]erification. In the first explanation, the `IncidentToAdjacentStrategy` (an optimization) +is the row that folds `outE("created").inV()` into a single `out("created")` step, matching the manual rewrite shown +above. In the second explanation two strategies cooperate. The `IncidentToAdjacentStrategy` first collapses +`inE("created").outV()` to `in("created")`, and then the `AdjacentToIncidentStrategy` rewrites that counted adjacency +back onto the incident edges so that the vertex step becomes an edge step again, which is the `inE("created").count()` +form. The `Final Traversal` line at the bottom of each explanation is the execution plan that actually runs. + +Further detail on these execution plans, including how to measure their runtime effect rather than only inspect them, is +available in the link:https://tinkerpop.apache.org/docs/x.y.z/reference/#explain-step[`explain()`] and +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#profile-step[`profile()`] steps, as well as the +link:https://tinkerpop.apache.org/docs/x.y.z/reference/#traversalstrategy[traversal strategies] section of the Reference +Documentation, which catalogs the full set of optimizations (`IncidentToAdjacentStrategy` and +`AdjacentToIncidentStrategy` among them). + Another anti-pattern that is commonly seen is the chaining of `where()`-steps using predicates. Consider the following traversal: [gremlin-groovy,modern]