Explanation of sack + lambda expression behaviour

30 views
Skip to first unread message

Carlos Bobed

unread,
Nov 22, 2021, 10:04:50 AM11/22/21
to Gremlin-users
Hi all, 

I've been trying to optimize a query using sack to keep track of local information I gathered around, and I've been struggling a little bit against how to use the values stored in a sack when it is a map. Searching in the list I've found a pointer to a section of Kelvin's book I forgot in the past and I've already been able to write a query but I fail to see the rationale behind the following: 

if I write: 
g.withSac{tmp:'XXX'}.V().limit(100).
   where(valuesMap('iri').next().is(eq(sack().map{x->x.get()['tmp']})))

it does not retrieve anything

(assumming that XXX coincides with an 'iri' value in the first 100 vertices of the graph, of course) 

However, adapting it to use filter + project directly: 

g.withSack{[tmp:'XXX']}.V().limit(100).
filter(project('a','b').by('iri').by(sack().map{x->x.get()['tmp']}).where('a',neq('b')))

it performs the comparison correctly.

I was supposing that sack().map{x->x.get['tmp'].next()} was returning a String and I could use it directly within the predicate as in:

g.withSack{[tmp:'XXX']}.V().limit(100).
where(values('iri').is(eq('XXX')))

which works as expected, but apparently, even though the class map{x->x.get()['tmp'].next()} is String, I cannot use it in the predicate. This is only a sample, the query is quite more complex (this could have been done with withSideEffect).
The point is that the more straightforward approach using match (disclaimer: more straightforward having a SPARQL background :) ) gives always a timeout in my graphs and I wanted to speed up some joins.
Now, I want to see why it doesn't take that particular String directly :)

Thank you in advance,
Best


Carlos Bobed

unread,
Nov 22, 2021, 10:55:39 AM11/22/21
to gremli...@googlegroups.com
Hi again, 

I've realized that there are some typos in the copy/pasted queries, missing next() calls, etc. The queries in the same order: 

doesn't do the comparison: 
g.withSack{tmp:'XXX'}.V().limit(100).
   where(values('iri').next().is(eq(sack().map{x->x.get()['tmp'].next()})))

- in fact, it does not let pass anyone (if I change to neq, everyone passes). Besides, it makes no actual difference whether I add the .next to x.get() in the lambda or not. 

does the comparison: 
g.withSack{[tmp:'XXX']}.V().limit(100).
filter(project('a','b').by('iri').by(sack().map{x->x.get()['tmp']}).where('a',eq('b')))

this one lets only pass the one with the iri set in the sack initially.

sorry for the typos, several consoles open and a lot of versions tried ... :)

Best,

Carlos




--
You received this message because you are subscribed to a topic in the Google Groups "Gremlin-users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/gremlin-users/MYN0ocJ94F8/unsubscribe.
To unsubscribe from this group and all its topics, send an email to gremlin-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/gremlin-users/7dcf8a06-6bf6-418c-a6a2-6d16613455d7n%40googlegroups.com.

Stephen Mallette

unread,
Nov 23, 2021, 6:45:22 AM11/23/21
to gremli...@googlegroups.com
next() is a terminal step:


so you can't use it in the middle of a traversal and then add more steps to follow it as you do in your where()

g.withSack{tmp:'XXX'}.V().limit(100).
   where(values('iri').next().is(eq(sack().map{x->x.get()['tmp'].next()})))

I'm not sure what you are trying to do exactly and sack() doesn't seem quite right for the outcome I think you want. In any case, I think you'd want something more like:

gremlin> g.withSack{['n':'josh']}.V().as('v').where('v',eq('v')).by('name').by(sack().select('n'))
==>v[4]

where you get rid of the closures, remove terminal steps from the middle of the traversal and use where() more in variable matching mode rather than just a straight filter().





You received this message because you are subscribed to the Google Groups "Gremlin-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to gremlin-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/gremlin-users/CAEM0JZ6chw2e4y_%2BT1%3DZzK8Em4KH23%3DZQxdrFn3sK5zatj2tHQ%40mail.gmail.com.

Carlos Bobed

unread,
Nov 23, 2021, 9:09:01 AM11/23/21
to gremli...@googlegroups.com
Hi, 

first of all, thank you for your answer. As I've said, I've managed to do it with sack (I'm translating some SPARQL queries with projections that requires storing quite few fields, as well as aggregations, and all the solutions based on pure traversals were timing out). 

I was aware that next is a terminal step, but if you call .class on the x.get['tmp'].next() => it states that it is a String. 
The thing I find confusing is that using a constant String works in the query, but using a terminal step that effectively returns a String doesn't. 

Regarding sack + select, I wasn't seen that I could use select step in that context, ... I came up with the lambda solution, but it's indeed the way to go, thank you! :) 

Best, 
Carlos 



Stephen Mallette

unread,
Nov 23, 2021, 11:02:54 AM11/23/21
to gremli...@googlegroups.com
> x.get['tmp'].next() => it states that it is a String. 

it's not doing what you think. x.get['tmp'] returns a String - you accessed your "tmp" key in the Map you added to the sack. you then called next() on that String which is something Groovy allows so you are really incrementing your string in the "tmp" key:

gremlin> "xxx".next()
==>xxy
gremlin> g.withSack{['n':'josh']}.V().sack().map{x -> x.get()['n'].next()}
==>josi
==>josi
==>josi
==>josi
==>josi
==>josi



Carlos Bobed

unread,
Nov 23, 2021, 11:35:05 AM11/23/21
to gremli...@googlegroups.com
Hi, 

yes, that's true ... I had already corrected it in the final query (I was using filter as Kelvin suggested in the book to use the contents of a sack + the lambda function without the next() step - which I've now substituted by a select as you suggested). 

I've just checked that the lambda sack().map{x->x.get()['tmp']} returns a DefaultGraphTraversal, thus, I should be able to use .next(), but in the following query: 

it raises an error about not having such element, is sack() at that point empty? if I'm not wrong, sack() + map should access the underlying structure correctly and return only the tmp field. 
I wanted to use the single value that it retrieves at that point (I mean, doing it in the other way works and in fact, the complete query I wrote works as a charm, but this is only to gain further insight about gremlin as following the steps and the returned types might be tricky sometimes :) ) 

Thank you very much for your comments! 

Best, 

Carlos 

Stephen Mallette

unread,
Nov 23, 2021, 12:10:49 PM11/23/21
to gremli...@googlegroups.com
I do see that you are still using a next() within the where() or more specifically within the eq():

where(values('iri').is(eq(sack().map{x->x.get()['tmp']}.next())))

Anonymous traversals do not have terminal steps. You call next(), toList(), etc on the root of the traversal and not the things inside. In a more simple comparison of various forms of terminal/anonymous usage using addE()/to():

g.V(1).addE('link').to(V(2).next()) // failure because of next()
g.V(1).addE('link').to(V(2))   // correct
g.V(1).addE('link').to(__.V(2))   // correct - longer form without static import
g.V(1).addE('link').to(g.V(2))   // failure for trying to spawn from g rather than being anonymous
g.V(1).addE('link').to(g.V(2).next())   // correct - but only works because to() can take a Vertex 

Technically that last one is two separate traversals and not one.


Carlos Bobed

unread,
Nov 23, 2021, 4:30:27 PM11/23/21
to gremli...@googlegroups.com
Thanks for the insights! :) 


Reply all
Reply to author
Forward
0 new messages