Question about `local` step

Sometimes I don't understand how local step works. I want to count how many elements in each array, this query looks correct but gives unexpected result. How to fix it? g.inject([1,2],[1],[1,2]).local(unfold().count())
4 Replies
spmallette
spmallette2y ago
if you just want to count each, the most Gremlinesque way is to do:
gremlin> g.inject([1,2],[1],[1,2]).count(local)
==>2
==>2
==>1
gremlin> g.inject([1,2],[1],[1,2]).count(local)
==>2
==>2
==>1
local() step has some nuances to it explored here: http://stephen.genoprime.com/snippet/2020/04/25/snippet-10.html and more recently here: https://discord.com/channels/838910279550238720/888507863847284766/1080592362297901088
stephen mallette
Use of local()
One of the more misunderstood, misused or simply unknown parts of the Gremlin language is local()-step. Its purpose is to execute a child traversal on a single element within the stream. In the following example limit(2) is applied to the stream in a global fashion and thus only two results are returned:
ColeGreer
ColeGreer2y ago
There appears to be a subtle difference between .count(local) and .local(unfold().count()). Based on my observations, .count(local) performs a separate count on each incoming array. .local(unfold().count()) takes each incoming traverser, and runs is through an unfold().count(). The subtle difference is regarding bulked traversers. A bulked traverser can contain 2 copies of the same object, but it still gets fed into a single execution of the unfold().count() child traversal. g.inject([1,2],[1],[1,2]) appears to spawn 2 traversers: {t = [1], bulk=1}, {t = [1,2], bulk=2}. There are 3 arrays but only 2 traversers. This creates the following results:
gremlin> g.inject([1,2],[1],[1,2]).count(local)
==>2
==>2
==>1
gremlin> g.inject([1,2],[1],[1,2]).local(unfold().count())
==>4
==>1
gremlin> g.inject([1,2],[1],[1,2]).count(local)
==>2
==>2
==>1
gremlin> g.inject([1,2],[1],[1,2]).local(unfold().count())
==>4
==>1
It appears that even when disabling bulking, inject() still only spawns 2 traversers in this case.
gremlin> g.inject([1,2],[1],[1,2]).local(unfold().count())
==>4
==>1
gremlin> g.withBulk(false).inject([1,2],[1],[1,2]).local(unfold().count())
==>2
==>1
gremlin> g.inject([1,2],[1],[1,2]).local(unfold().count())
==>4
==>1
gremlin> g.withBulk(false).inject([1,2],[1],[1,2]).local(unfold().count())
==>2
==>1
I believe this may be a bug in the inject step. Thoughts @spmallette ?
spmallette
spmallette2y ago
I'm definitely turning that thing i wrote in the discord link above into a blog post today. if i didn't re-read that i'd have probably gone down into a hole of debugging with this example. sorry i didn't understand if this was the real question here. anyway, no, not a bug. that's just how local() works. a global count() (not count(local)) occurs over the same objects in the stream. maybe this is easier to see:
gremlin> g.inject([1],[1],[1]).local(unfold().count())
==>3
gremlin> g.inject([1],[1],[1]).local(unfold().count())
==>3
The objects are all List with a 1 in there so they all count under the same traverser object. kinda weird but there are some use cases where you want to do object-local traversal computations. this example with inject() is fairly contrived so it looks especially strange. whenever i see local() in a traversal, i always stop to ask if it really needs to be there. often, it should be replaced with a Scope.local argument (like count(local) in this case) or a direct replacement with map() or flatMap().
spmallette
spmallette2y ago
hope i didn't jump to "mark solution" too quickly....wanted that link in Answer Overflow so that i could complete the blog post: https://stephen.genoprime.com/snippet/2023/04/13/snippet-15.html
stephen mallette
Use of local() Revisited
In recent weeks there have been many questions about the local()-step both in Discord and in TinkerPop’s JIRA. I’d written on this topic once before in Use of local() but it perhaps begs a further revisit. The example that arose from Discord helped inspire this post as it was sufficiently primitive to hopefully clarify usage.
Want results from more Discord servers?
Add your server