Java Query DSL
Drivine4j provides a fluent, type-safe query API for Java that mirrors the Kotlin DSL capabilities.
Basic Usage
Section titled “Basic Usage”import org.drivine.query.dsl.JavaQueryBuilderKt;
List<RaisedAndAssignedIssue> results = JavaQueryBuilderKt .query(graphObjectManager, RaisedAndAssignedIssue.class) .filterWith(RaisedAndAssignedIssueQueryDsl.class) .where(dsl -> dsl.getIssue().getState().isEqualTo("open")) .loadAll();The pattern is:
query(graphObjectManager, GraphViewClass)- Start a queryfilterWith(QueryDslClass)- Specify the generated DSL for type-safe filtering- Chain
where(),whereAny(),orderBy()as needed - Terminate with
loadAll(),loadFirst(), ordeleteAll()
Available Operators
Section titled “Available Operators”Comparison:
.where(dsl -> dsl.getIssue().getId().isEqualTo(100L)) // equals.where(dsl -> dsl.getIssue().getId().isNotEqualTo(100L)) // not equals.where(dsl -> dsl.getIssue().getId().isGreaterThan(100L)) // greater than.where(dsl -> dsl.getIssue().getId().isAtLeast(100L)) // greater than or equal.where(dsl -> dsl.getIssue().getId().isLessThan(100L)) // less than.where(dsl -> dsl.getIssue().getId().isAtMost(100L)) // less than or equalString Operations:
.where(dsl -> dsl.getIssue().getTitle().hasSubstring("Bug")).where(dsl -> dsl.getIssue().getTitle().hasPrefix("Feature")).where(dsl -> dsl.getIssue().getTitle().hasSuffix("needed"))Null Checks:
.where(dsl -> dsl.getIssue().getBody().isAbsent()).where(dsl -> dsl.getIssue().getBody().isPresent())Collections:
.where(dsl -> dsl.getIssue().getState().isIn(Arrays.asList("open", "reopened")))Multiple Conditions (AND)
Section titled “Multiple Conditions (AND)”Chain multiple where() calls for AND logic:
List<RaisedAndAssignedIssue> results = JavaQueryBuilderKt .query(graphObjectManager, RaisedAndAssignedIssue.class) .filterWith(RaisedAndAssignedIssueQueryDsl.class) .where(dsl -> dsl.getIssue().getState().isEqualTo("open")) .where(dsl -> dsl.getIssue().getLocked().isEqualTo(false)) .where(dsl -> dsl.getIssue().getId().isAtLeast(100L)) .loadAll();// WHERE issue.state = 'open' AND issue.locked = false AND issue.id >= 100OR Conditions
Section titled “OR Conditions”Use whereAny() for OR logic:
List<RaisedAndAssignedIssue> results = JavaQueryBuilderKt .query(graphObjectManager, RaisedAndAssignedIssue.class) .filterWith(RaisedAndAssignedIssueQueryDsl.class) .whereAny(dsl -> Arrays.asList( dsl.getIssue().getState().isEqualTo("open"), dsl.getIssue().getState().isEqualTo("reopened") )) .loadAll();// WHERE (issue.state = 'open' OR issue.state = 'reopened')Combine AND and OR:
// locked=false AND (state='open' OR state='reopened')List<RaisedAndAssignedIssue> results = JavaQueryBuilderKt .query(graphObjectManager, RaisedAndAssignedIssue.class) .filterWith(RaisedAndAssignedIssueQueryDsl.class) .where(dsl -> dsl.getIssue().getLocked().isEqualTo(false)) .whereAny(dsl -> Arrays.asList( dsl.getIssue().getState().isEqualTo("open"), dsl.getIssue().getState().isEqualTo("reopened") )) .loadAll();Ordering
Section titled “Ordering”List<RaisedAndAssignedIssue> results = JavaQueryBuilderKt .query(graphObjectManager, RaisedAndAssignedIssue.class) .filterWith(RaisedAndAssignedIssueQueryDsl.class) .where(dsl -> dsl.getIssue().getState().isEqualTo("open")) .orderBy(dsl -> dsl.getIssue().getId().descending()) .loadAll();.nullsLast() or .nullsFirst() after ascending() / descending() says where the rows with no
value go, as in Kotlin: .orderBy(dsl -> dsl.getIssue().getClosedAt().descending().nullsLast()).
Load First
Section titled “Load First”Get only the first matching result:
RaisedAndAssignedIssue result = JavaQueryBuilderKt .query(graphObjectManager, RaisedAndAssignedIssue.class) .filterWith(RaisedAndAssignedIssueQueryDsl.class) .where(dsl -> dsl.getIssue().getState().isEqualTo("open")) .orderBy(dsl -> dsl.getIssue().getId().descending()) .loadFirst(); // Returns null if no matchesPolymorphic Filtering with instanceOf
Section titled “Polymorphic Filtering with instanceOf”Filter by @NodeFragment subtype using instanceOf():
import org.drivine.sample.fragment.AnonymousWebUser;import org.drivine.sample.fragment.RegisteredWebUser;
// Filter to only anonymous web usersList<GuideUserWithPolymorphicWebUser> results = JavaQueryBuilderKt .query(graphObjectManager, GuideUserWithPolymorphicWebUser.class) .filterWith(GuideUserWithPolymorphicWebUserQueryDsl.class) .where(dsl -> dsl.getWebUser().instanceOf(AnonymousWebUser.class)) .loadAll();
// Combine with other conditionsList<GuideUserWithPolymorphicWebUser> activeAnonymous = JavaQueryBuilderKt .query(graphObjectManager, GuideUserWithPolymorphicWebUser.class) .filterWith(GuideUserWithPolymorphicWebUserQueryDsl.class) .where(dsl -> dsl.getCore().getGuideProgress().isAtLeast(10)) .where(dsl -> dsl.getWebUser().instanceOf(AnonymousWebUser.class)) .loadAll();
// Use in OR conditionsList<GuideUserWithPolymorphicWebUser> allUsers = JavaQueryBuilderKt .query(graphObjectManager, GuideUserWithPolymorphicWebUser.class) .filterWith(GuideUserWithPolymorphicWebUserQueryDsl.class) .whereAny(dsl -> Arrays.asList( dsl.getWebUser().instanceOf(AnonymousWebUser.class), dsl.getWebUser().instanceOf(RegisteredWebUser.class) )) .loadAll();Delete with DSL
Section titled “Delete with DSL”int deleted = JavaQueryBuilderKt .query(graphObjectManager, RaisedAndAssignedIssue.class) .filterWith(RaisedAndAssignedIssueQueryDsl.class) .where(dsl -> dsl.getIssue().getState().isEqualTo("closed")) .deleteAll();Grouped Reads
Section titled “Grouped Reads”A grouped read is not on the query builder: it is getGroups() on either manager, and its handles come from the spec. That section has an example; this is what each Kotlin line is in Java.
| Kotlin | Java |
|---|---|
where { … } |
spec.onlyIf(condition, …) |
key(query.x), keyOrNull(query.x) |
spec.key(dsl.getX()), spec.keyOrNull(dsl.getX()) |
key(path<N>(Hop(…), …)) |
spec.key(spec.path(N.class, spec.hop("TYPE", Direction.INCOMING, "Label"), …)) |
count(), count { … } |
spec.count(), spec.countWhere(condition, …) |
sum(query.x) |
spec.sumOfInts, sumOfLongs, sumOfFloats, sumOfDoubles |
avg, min, max |
spec.avg(dsl.getX()), spec.min(…), spec.max(…) |
sum(query.x) { … } |
spec.filtered(spec.sumOfLongs(dsl.getX()), condition, …) |
having { votes gte 3 } |
spec.having(votes, ComparisonOperator.GREATER_THAN_OR_EQUAL, 3) |
orderBy { votes.desc() } |
spec.orderBy(votes.descending()), then .nullsFirst() / .nullsLast() |
rows { … } |
return spec.rowsOf(row -> …) |
groups.countBy(T::x) |
manager.getGroups().countBy(T.class, "x") |
A Java field does not say whether it may be null, so key of one is not refused: a node with no value is reported when its row is read, and keyOrNull reads it as null.
Java DSL Summary
Section titled “Java DSL Summary”The Java methods are named differently from their Kotlin counterparts (isEqualTo for eq, ascending for
asc, …). Kotlin’s are context-parameter members that register themselves inside a where/orderBy block;
from Kotlin 2.3 two members sharing a name are ambiguous there, so each language gets its own. Renamed in
0.0.87 — see 0.0.87-java-dsl-names.md for the old → new table.
| Operation | Example |
|---|---|
| Equality | .isEqualTo("value"), .isNotEqualTo("value") |
| Comparison | .isGreaterThan(n), .isAtLeast(n), .isLessThan(n), .isAtMost(n) |
| Strings | .hasSubstring("x"), .hasPrefix("x"), .hasSuffix("x") |
| Null | .isAbsent(), .isPresent() |
| Collections | .isIn(Arrays.asList(...)) |
| Type filter | .instanceOf(SubtypeClass.class) |
| AND | Chain multiple .where() |
| OR | .whereAny(dsl -> Arrays.asList(...)) |
| Order | .orderBy(dsl -> dsl.getProp().ascending()), .descending(); then .nullsLast() / .nullsFirst() |
| Keyset cursor | .seek(dsl -> List.of(dsl.getProp().cursorAfter(v))) |
| Grouped read | manager.getGroups().load(T.class, dsl, spec -> …): see Grouped Reads |

