Available in versions: Dev (3.19) | Latest (3.18) | 3.17 | 3.16

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 traversal abort condition to short circuit traversal e.g.
     * when the searched object has been found.
     */
    Predicate<QueryPart> recurse();

    /**
     * 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());

References to this page

Feedback

Do you have any feedback about this page? We'd love to hear it!

The jOOQ Logo