Hi Everyone,
I'd like to discuss a breaking change to the behavior of limit() and range()
steps inside repeat() that I am planning to create a pull request for soon
against the 3.8-dev branch.
Currently, limit() and range() steps inside repeat() maintain global
counters that persist across repeat iterations, leading to strange behavior. To
demonstrate, consider the following limit() traversals for the Grateful Dead
toy graph which are querying for pairs of song sequences starting with the song
'JAM'.
This repeat() traversal does not produce results because the limit(2) counter
reaches the global limit after the first iteration (note that
RepeatUnrollStrategy is disabled so that the repeat() step is not stripped by
strategy optimization):
gremlin>
g.withoutStrategies(RepeatUnrollStrategy).V().has('name','JAM').repeat(out('followedBy').limit(2)).times(2).values('name’)
gremlin>
However, the following unrolled traversal without repeat() produces 2 results:
gremlin>
g.V().has('name','JAM').out('followedBy').limit(2).out('followedBy').limit(2).propertyMap('name’)
==>[name:[vp[name->HURTS ME TOO]]]
==>[name:[vp[name->BLACK THROATED WIND]]]
These examples demonstrate how having globally tracked limit() and range()
counters inside repeat() are counter intuitive and instead should be tracked
per-iteration.
My change will introduce per-iteration counter tracking, making the repeat()
behaviour more consistent with unrolled traversals. For example, the following
Grateful Dead repeat () traversal will produce 2 results after the change:
gremlin>
g.withoutStrategies(RepeatUnrollStrategy).V().has('name','JAM').repeat(out('followedBy').limit(2)).times(2).values('name’)
==>[name:[vp[name->HURTS ME TOO]]]
==>[name:[vp[name->BLACK THROATED WIND]]]
Please let me know if you have any concerns or feedback regarding this change.
Cheers,
Andrea