@@ -262,6 +262,104 @@ gremlin> g.inject([Float.MAX_VALUE, Float.MAX_VALUE], [Double.MAX_VALUE, Double.
262262
263263See link:https://issues.apache.org/jira/browse/TINKERPOP-3115[TINKERPOP-3115]
264264
265+ ==== repeat() Step Global Children Semantics Change
266+
267+ The `repeat()` step has been updated to treat the repeat traversal as a global child in all cases. Previously, the
268+ repeat traversal behaved as a hybrid between local and global semantics, which could lead to unexpected results in
269+ certain scenarios. The repeat traversal started off as a local child but as traversers were added back per iteration,
270+ it behaved more like a global child.
271+
272+ With this change, the repeat traversal now consistently operates with global semantics, meaning that all traversers
273+ are processed together rather than being processed per traverser. This provides more predictable behavior and aligns
274+ with the semantics of other steps.
275+
276+ [source,text]
277+ ----
278+ // In 3.7.x and earlier, the order would be local to the first traverser.
279+ // Notice how the results are grouped by marko, then vadas, then lop
280+ gremlin> g.withoutStrategies(RepeatUnrollStrategy).V(1, 2, 3).
281+ ......1> repeat(both().simplePath().order().by("name")).times(2).path().by("name")
282+ ==>[marko,lop,josh]
283+ ==>[marko,josh,lop]
284+ ==>[marko,lop,peter]
285+ ==>[marko,josh,ripple]
286+ ==>[vadas,marko,josh]
287+ ==>[vadas,marko,lop]
288+ ==>[lop,marko,josh]
289+ ==>[lop,josh,marko]
290+ ==>[lop,josh,ripple]
291+ ==>[lop,marko,vadas]
292+
293+ // In 3.8.0, the repeat now consistently uses global semantics
294+ // The traversers from the final iteration are ordered first then by the traversers from previous iterations
295+ gremlin> g.withoutStrategies(RepeatUnrollStrategy).V(1, 2, 3).
296+ ......1> repeat(both().simplePath().order().by("name")).times(2).path().by("name")
297+ ==>[marko,lop,josh]
298+ ==>[vadas,marko,josh]
299+ ==>[lop,marko,josh]
300+ ==>[marko,josh,lop]
301+ ==>[vadas,marko,lop]
302+ ==>[lop,josh,marko]
303+ ==>[marko,lop,peter]
304+ ==>[marko,josh,ripple]
305+ ==>[lop,josh,ripple]
306+ ==>[lop,marko,vadas]
307+ ----
308+
309+ This change may affect traversals that relied on the previous hybrid behavior, particularly those using side effects
310+ or barrier steps within `repeat()`. Review any traversals using `repeat()` with steps like `aggregate()`, `store()`,
311+ or other barrier steps to ensure they produce the expected results.
312+
313+ If you would like `repeat()` to behave similarly to how it did in 3.7.x, then you should wrap the repeat inside a
314+ `local()`. The following example demonstrates this:
315+
316+ [source,text]
317+ ----
318+ // In 3.7.x
319+ gremlin> g.V().repeat(both().simplePath().order().by("name")).times(2).path().by("name")
320+ ==>[marko,lop,josh]
321+ ==>[marko,josh,lop]
322+ ==>[marko,lop,peter]
323+ ==>[marko,josh,ripple]
324+ ==>[vadas,marko,josh]
325+ ==>[vadas,marko,lop]
326+ ==>[lop,marko,josh]
327+ ==>[lop,josh,marko]
328+ ==>[lop,josh,ripple]
329+ ==>[lop,marko,vadas]
330+ ==>[josh,marko,lop]
331+ ==>[josh,lop,marko]
332+ ==>[josh,lop,peter]
333+ ==>[josh,marko,vadas]
334+ ==>[ripple,josh,lop]
335+ ==>[ripple,josh,marko]
336+ ==>[peter,lop,josh]
337+ ==>[peter,lop,marko]
338+
339+ // In 3.8.0, placing the repeat inside a local will again cause the repeat traversal to apply per traverser (locally)
340+ gremlin> g.V().local(repeat(both().simplePath().order().by("name")).times(2)).path().by("name")
341+ ==>[marko,lop,josh]
342+ ==>[marko,josh,lop]
343+ ==>[marko,lop,peter]
344+ ==>[marko,josh,ripple]
345+ ==>[vadas,marko,josh]
346+ ==>[vadas,marko,lop]
347+ ==>[lop,marko,josh]
348+ ==>[lop,josh,marko]
349+ ==>[lop,josh,ripple]
350+ ==>[lop,marko,vadas]
351+ ==>[josh,marko,lop]
352+ ==>[josh,lop,marko]
353+ ==>[josh,lop,peter]
354+ ==>[josh,marko,vadas]
355+ ==>[ripple,josh,lop]
356+ ==>[ripple,josh,marko]
357+ ==>[peter,lop,josh]
358+ ==>[peter,lop,marko]
359+ ----
360+
361+ See: link:https://issues.apache.org/jira/browse/TINKERPOP-3200[TINKERPOP-3200]
362+
265363==== Prefer OffsetDateTime
266364
267365The default implementation for date type in Gremlin is now changed from the `java.util.Date` to the more encompassing
@@ -985,35 +1083,6 @@ gremlin> g.withoutStrategies(RepeatUnrollStrategy).V().values('name').repeat(inj
9851083==>peter
9861084----
9871085
988- Another example is the usage of `aggregate()` inside `repeat()`. The following results returned from the `modern` graph
989- demonstrate the change of semantics if the `aggregate()` in `repeat()` were to be unrolled:
990-
991- [source,text]
992- ----
993- gremlin> g.V().both().aggregate('x').both().aggregate('x').limit(10)
994- ==>v[1]
995- ==>v[1]
996- ==>v[1]
997- ==>v[1]
998- ==>v[1]
999- ==>v[1]
1000- ==>v[1]
1001- ==>v[4]
1002- ==>v[4]
1003- ==>v[4]
1004- gremlin> g.withoutStrategies(RepeatUnrollStrategy).V().repeat(both().aggregate('x')).times(2).limit(10)
1005- ==>v[1]
1006- ==>v[1]
1007- ==>v[1]
1008- ==>v[4]
1009- ==>v[6]
1010- ==>v[5]
1011- ==>v[3]
1012- ==>v[3]
1013- ==>v[2]
1014- ==>v[4]
1015- ----
1016-
10171086Other examples of affected traversals include (but are not limited to):
10181087
10191088[source,groovy]
@@ -1196,6 +1265,62 @@ The `ChooseStep` now provides a `ChooseSemantics` enum which helps indicate if t
11961265
11971266See: link:https://issues.apache.org/jira/browse/TINKERPOP-3178[TINKERPOP-3178]
11981267
1268+ ===== repeat() Step Global Children Semantics Change
1269+
1270+ The `RepeatStep` has been updated to consistently treat the repeat traversal as a global child rather than using
1271+ hybrid local/global semantics. This change affects how the repeat traversal processes traversers and interacts with
1272+ the parent traversal.
1273+
1274+ Previously, `RepeatStep` would start with local semantics for the first iteration and then switch to global semantics
1275+ for the subsequent iterations, which created inconsistencies in how side effects and barriers behaved within the repeat
1276+ traversal. The biggest change will be to `Barrier` steps in the repeat traversal as they will now have access to all
1277+ the starting traversers.
1278+
1279+ [source,text]
1280+ ----
1281+ // In 3.7.x and earlier, the order would be local to the first traverser.
1282+ // Notice how the results are grouped by marko, then vadas, then lop
1283+ gremlin> g.withoutStrategies(RepeatUnrollStrategy).V(1, 2, 3).
1284+ ......1> repeat(both().simplePath().order().by("name")).times(2).path().by("name")
1285+ ==>[marko,lop,josh]
1286+ ==>[marko,josh,lop]
1287+ ==>[marko,lop,peter]
1288+ ==>[marko,josh,ripple]
1289+ ==>[vadas,marko,josh]
1290+ ==>[vadas,marko,lop]
1291+ ==>[lop,marko,josh]
1292+ ==>[lop,josh,marko]
1293+ ==>[lop,josh,ripple]
1294+ ==>[lop,marko,vadas]
1295+
1296+ // In 3.8.0, the aggregate now consistently uses global semantics
1297+ // The traversers are now ordered so the traversers from the final iteration are ordered first then by
1298+ // the traversers from previous iterations
1299+ gremlin> g.withoutStrategies(RepeatUnrollStrategy).V(1, 2, 3).
1300+ ......1> repeat(both().simplePath().order().by("name")).times(2).path().by("name")
1301+ ==>[marko,lop,josh]
1302+ ==>[vadas,marko,josh]
1303+ ==>[lop,marko,josh]
1304+ ==>[marko,josh,lop]
1305+ ==>[vadas,marko,lop]
1306+ ==>[lop,josh,marko]
1307+ ==>[marko,lop,peter]
1308+ ==>[marko,josh,ripple]
1309+ ==>[lop,josh,ripple]
1310+ ==>[lop,marko,vadas]
1311+ ----
1312+
1313+ Providers implementing custom optimizations or strategies around `RepeatStep` should verify that their
1314+ implementations account for the repeat traversal being a global child. This particularly affects:
1315+
1316+ - Strategies that analyze or transform repeat traversals
1317+ - Optimizations that depend on the scope semantics of child traversals
1318+
1319+ The last point about optimizations may be particularly important for providers that have memory constraints as this
1320+ change may bring about higher memory usage due to more traversers needing to be held in memory.
1321+
1322+ See: link:https://issues.apache.org/jira/browse/TINKERPOP-3200[TINKERPOP-3200]
1323+
11991324===== Prefer OffsetDateTime
12001325
12011326The default implementation for date type in Gremlin is now changed from the deprecated `java.util.Date` to the more
0 commit comments