This is experimental functionality, and as such subject to change. Use at your own risk!
Traversal
Applies to ❌ Open Source Edition ✅ Express Edition ✅ Professional Edition ✅ Enterprise Edition
While the accessor methods from the model API allow for traversing the expression tree manually, a more generic way to traverse the expression tree is using the org.jooq.Traverser
API, which can traverse a org.jooq.QueryPart
in a similar fashion as a java.util.stream.Collector
can iterate a java.util.stream.Stream
, collecting and aggregating data about the expression tree. A Traverser consists of this API
:
public interface Traverser<A, R> { /** * A supplier for a temporary data structure to accumulate {@link QueryPart} * objects into during traversal. */ Supplier<A> supplier(); /** * An optional traversal abort condition to short circuit traversal e.g. * when the searched object has been found. */ Predicate<A> abort(); /** * An optional recursion condition to prevent entering a specific * {@link QueryPart}, e.g. when it is undesired to enter any subqueries. */ Predicate<QueryPart> recurse(); /** * An optional recursion condition to prevent entering a specific * {@link QueryPart}'s children, e.g. when it is desired to traverse only * into certain operators. */ Predicate<QueryPart> recurseChildren(); /** * A callback that is invoked before recursing into a subtree. */ BiFunction<A, QueryPart, A> before(); /** * A callback that is invoked after recursing into a subtree. */ BiFunction<A, QueryPart, A> after(); /** * An optional transformation function to turn the temporary data structure * supplied by {@link #supplier()} into the final data structure. */ Function<A, R> finisher(); }
Some elements are similar to that of a java.util.stream.Collector
, others are specific to tree traversal.
A simple illustration shows what can be done:
// Any ordinary QueryPart: Condition condition = BOOK.ID.eq(1); int count = condition.$traverse( // Supplier of the initial data structure: an int () -> 0, // Print all traversed QueryParts and increment the counter (r, q) -> { System.out.println("Part " + r + ": " + q); return r + 1; } ); System.out.println("Count : " + count);
The above will print:
Part 0: "BOOK"."ID" = 1 Part 1: "BOOK"."ID" Part 2: 1 Count : 3
Using the same traverser on a slightly more complex QueryPart
Condition condition = BOOK.ID.eq(1).or(BOOK.ID.eq(2)); // ...
Part 0: ("BOOK"."ID" = 1 or "BOOK"."ID" = 2) Part 1: "BOOK"."ID" = 1 Part 2: "BOOK"."ID" Part 3: 1 Part 4: "BOOK"."ID" = 2 Part 5: "BOOK"."ID" Part 6: 2 Count : 7
Re-using your JDK collectors
Any Collector
can be turned into a Traverser
using Traversers.collecting(Collector). For example, if you want to count all QueryPart
items in an expression, instead of the above hand-written traverser, just use the JDK Collectors.counting():
// Contains 3 query parts long count1 = BOOK.ID.eq(1) .$traverse(Traversers.collecting(Collectors.counting()); // Contains 7 query parts long count2 = BOOK.ID.eq(1).or(BOOK.ID.eq(2)) .$traverse(Traversers.collecting(Collectors.counting());
Feedback
Do you have any feedback about this page? We'd love to hear it!