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
if you just want to count each, the most Gremlinesque way is to do:
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/1080592362297901088stephen 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:
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:
It appears that even when disabling bulking, inject()
still only spawns 2 traversers in this case.
I believe this may be a bug in the inject step. Thoughts @spmallette ?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:
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()
.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.