This report is only intended as a general guide for identifying possible upgrade issues between 3.7.4 and 3.8.0. There may be other problems to resolve. Be familiar with the Upgrade Documentation and review your code carefully.
docs/upgrade/java/src/main/java/org/apache/tinkerpop/example/cmdb/graph/TraversalBuilders.javanone() discarded traversers. In 3.8.0, that functionality is renamed to discard(), and none() is repurposed to none(P) as a quantifier complementing any(P) and all(P).public static Function<GraphTraversalSource, GraphTraversal<?, Vertex>> noneAndDiscard() { return g -> g.V().none(); }
g.V().none() no longer discards traversers and will be interpreted differently. This is a breaking change that will change behavior or fail.none() with discard() for the discard behavior:return g -> g.V().discard();
split("") now splits a string into its characters (e.g., "Hello" -> ["H","e","l","l","o"]). In earlier versions it returned the entire string as a single element.firstAndLast):return g -> g.V().hasLabel("team").values("name"). split("").map(__.union(__.limit(Scope.local,1), tail(Scope.local,1)).fold());
map(...). This changes the cardinality and semantics of subsequent steps. If the intent was to extract first and last full team names, split("") in 3.8.0 will break that logic.split("") entirely:return g -> g.V().hasLabel("team").values("name") .map(__.union(__.limit(Scope.local, 1), tail(Scope.local, 1)).fold());
limit(local)/tail(local) return types).range(local), limit(local), and tail(local) now consistently return collections. Previously, if the result had a single element, the step returned the element itself (unwrapped).firstAndLast):... map(__.union(__.limit(Scope.local,1), tail(Scope.local,1)).fold());
limit(Scope.local,1) and tail(Scope.local,1) often yielded single elements which then produced a flat two-element list after union(...).fold() like [first, last].[[first], [last]] before the final fold(), thus changing the output structure and any downstream consumers.unfold() each collection before folding:.map(__.union( __.limit(Scope.local, 1).unfold(), tail(Scope.local, 1).unfold() ).fold())
head(local) once available or use range(local, 0, 1).unfold() as a uniform pattern.repeat() now consistently behaves with global-child semantics. Previously it behaved like a hybrid between local and global, which could affect ordering and interactions with barriers/side-effects inside repeat().incidentBlastRadius):.emit() .repeat( __.union( __.out("dependsOn"), __.out("deployedAs").in("deploymentOf") ) ) .times(d) .path().by(__.valueMap(true));
serviceDependencies):.emit() .repeat(__.out("dependsOn")).times(d) .valueMap(true);
repeat() bodies don’t include explicit barriers like order() or side-effect steps, the traversal may produce a different ordering of paths or intermediate traversers in 3.8.0 compared to 3.7.x due to the global semantics change, especially when combined with emit().times(d).path() elements or on determinism in the emitted iterations, results could differ.local() to emulate the old behavior:.local(repeat(...).times(d))
order() where ordering is required.java.time.OffsetDateTime instead of java.util.Date. While Date is still accepted as input, server-side manipulations may yield OffsetDateTime.deploymentTimeline):.order().by("openedAt", Order.desc) .valueMap(true);
Date properties for openedAt and your application assumes Date objects are returned, the switch to OffsetDateTime in some manipulation contexts can surface type differences when data is consumed from valueMap(true). You are not calling the date conversion steps (asDate(), dateAdd(), dateDiff()), so impact is typically minimal, but cross-version data or server-side transformations could still alter types.openedAt in integration tests against 3.8.0 and adjust DTOs/serialization if needed to accept OffsetDateTime.