Optional Match Queries

70 views
Skip to first unread message

Udit Sharma

unread,
May 2, 2018, 3:21:44 AM5/2/18
to Gremlin-users
How to write a Match query where some of the graph patterns are Optional?

Example:
g.V().match(__.as('temp').has('id',"dept0"),
__.as('temp').in('wf').as('X'),
__.as('X').has('type','prof'),
__.as('X').out('to').as('Z'),
__.as('Z').in('tc').as('Y'),
__.as('Y').out('a').as('X')).select('X','Y','Z')

We want last three match patterns to be optional.

Daniel Kuppitz

unread,
May 2, 2018, 6:26:23 AM5/2/18
to gremli...@googlegroups.com
If all 3 are optional, then as('Y').out('a').as('X') is not really relevant and can be removed. That leaves us with:

g.V().match(__.as('temp').has('id',"dept0"),
            __.as('temp').in('wf').as('X'),
            __.as('X').has('type','prof'),
            __.as('X').out('to').as('Z'),
            __.as('Z').in('tc').as('Y')).select('X','Y','Z')

Now making as('X').out('to').as('Z') optional is simple:

g.V().match(__.as('temp').has('id',"dept0"),
            __.as('temp').in('wf').as('X'),
            __.as('X').has('type','prof'),
            __.as('X').coalesce(out('to'), constant("N/A")).as('Z'),
            __.as('Z').in('tc').as('Y')).select('X','Y','Z')

However, that's an invalid traversal as we can't do as('Z').in('tc') if Z is a String value (N/A), hence the last pattern has to be optional and also take the possible String value into account:

g.V().match(__.as('temp').has('id',"dept0"),
            __.as('temp').in('wf').as('X'),
            __.as('X').has('type','prof'),
            __.as('X').coalesce(out('to'), constant("N/A")).as('Z'),
            __.as('Z').choose(__.is("N/A"),
                                constant("N/A"),
                                coalesce(__.in("tc"), constant("N/A"))).as('Y')).select('X','Y','Z')

But in general, I would say that you shouldn't use match() for patterns that are optional. As you can see, it gets cumbersome pretty quickly.

Cheers,
Daniel


--
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-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/gremlin-users/6d052733-8488-4d5c-aac0-261e34d28e09%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Udit Sharma

unread,
May 2, 2018, 8:42:48 AM5/2/18
to Gremlin-users
Thanks Daniel. I had two more questions.
1. How to modify this query if identifying non-relevant match pattern is difficult(in case of large number of patterns)?
2. How to write this query without using match()?
To unsubscribe from this group and stop receiving emails from it, send an email to gremlin-user...@googlegroups.com.

Daniel Kuppitz

unread,
May 2, 2018, 9:54:17 AM5/2/18
to gremli...@googlegroups.com
Can you please provide a small sample graph for your query? This will make it a lot easier to compare the 2 approaches.

Cheers,
Daniel


Udit Sharma

unread,
May 9, 2018, 7:10:04 AM5/9/18
to Gremlin-users
Hi Daniel,
 
Sample Graph
g=TinkerGraph.open().traversal()
g.addV().property('type','student').property('id','student1').as('s1').
addV().property('type','student').property('id','student2').as('s2').
addV().property('type','student').property('id','student3').as('s3').
addV().property('type','student').property('id','student4').as('s4').
addV().property('type','course').property('id','course1').as('c1').
addV().property('type','course').property('id','course2').as('c2').
addV().property('type','course').property('id','course3').as('c3').
addV().property('type','teacher').property('id','teacher1').as('t1').
addE('takesCourse').from('s1').to('c1').
addE('takesCourse').from('s2').to('c2').
addE('takesCourse').from('s3').to('c3').
addE('teacherOf').from('t1').to('c1').
addE('teacherOf').from('t1').to('c2')

Query1
g.V().match(__.as('X').has('type','student'),
__.as('X').out('takesCourse').as('Y'),
__.as('Y').in('teacherOf').as('Z')).select('X','Y','Z')

Output
==>[X:v[0],Y:v[12],Z:v[21]]
==>[X:v[3],Y:v[15],Z:v[21]]

Expected Output: 
==>[X:v[0],Y:v[12],Z:v[21]]
==>[X:v[3],Y:v[15],Z:v[21]]
==>[X:v[6],Y:N/A,Z:N/A]
==>[X:v[9],Y:N/A,Z:N/A]

It should print 
1. All X that matches pattern 1.
2. Y and Z if both of them matches pattern 2 and 3, otherwise print N/A for both of them.

Query 2:(Your approach)
g.V().match(__.as('X').has('type','student'),
__.as('X').coalesce(out('takesCourse'), constant("N/A")).as('Y'),
__.as('Y').choose(__.is("N/A"),constant("N/A"),coalesce(__.in('teacherOf'), constant("N/A"))).as('Z')).select('X','Y','Z')

Output
==>[X:v[0],Y:v[12],Z:v[21]]
==>[X:v[3],Y:v[15],Z:v[21]]
==>[X:v[6],Y:v[18],Z:N/A]
==>[X:v[9],Y:N/A,Z:N/A]

How to modify the query to obtain the output as expected?
 

Daniel Kuppitz

unread,
May 9, 2018, 10:17:00 AM5/9/18
to gremli...@googlegroups.com
It should print 
1. All X that matches pattern 1.
2. Y and Z if both of them matches pattern 2 and 3, otherwise print N/A for both of them.

Then coalesce() is your friend again:

gremlin> g.V().has('type','student').
           coalesce(
             __.as('X').
               out('takesCourse').as('Y').
               in('teacherOf').as('Z').
               select('X','Y','Z'),
             project('X','Y','Z').
               by().
               by(constant('N/A')).
               by(constant('N/A')))
==>[X:v[0],Y:v[12],Z:v[21]]
==>[X:v[3],Y:v[15],Z:v[21]]
==>[X:v[6],Y:N/A,Z:N/A]
==>[X:v[9],Y:N/A,Z:N/A]

Cheers,
Daniel


To unsubscribe from this group and stop receiving emails from it, send an email to gremlin-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/gremlin-users/3d97b250-6e8f-4e89-b6dd-b417cbdd4e14%40googlegroups.com.

Udit Sharma

unread,
May 10, 2018, 9:35:25 AM5/10/18
to Gremlin-users
Thanks Daniel. That was great. But, I am not able to extend your approach to other queries.
Sample Graph
g=TinkerGraph.open().traversal()
g.addV().property('type','student').property('id','student1').as('s1').
addV().property('type','course').property('id','course1').as('c1').
addV().property('type','teacher').property('id','teacher1').as('t1').
addV().property('type','teacher').property('id','teacher2').as('t2').
addV().property('type','department').property('id','department1').as('d1').
addE('worksFor').from('t1').to('d1').
addE('worksFor').from('t2').to('d1').
addE('headOf').from('t1').to('d1').
addE('teacherOf').from('t1').to('c1').
addE('takesCourse').from('s1').to('c1').
addE('advisor').from('s1').to('t1')

Original Query
g.V().match(__.as('temp').has('id',"department1"),
__.as('temp').in('worksFor','headOf').as('X'),
__.as('X').out('teacherOf').as('Z'),
__.as('Z').in('takesCourse').as('Y'),
__.as('Y').out('advisor').as('X'))

Expected Output: Last three patterns are optional
==>[temp:v[12],X:v[6],Y:v[0],Z:v[3]]
==>[temp:v[12],X:v[6],Y:v[0],Z:v[3]]
==>[temp:v[12],X:v[9] Y:N/A,Z:N/A]


Daniel Kuppitz

unread,
May 10, 2018, 10:35:17 AM5/10/18
to gremli...@googlegroups.com
Whenever you have something that's optional, but you still want to get a value, use coalesce():

gremlin> g.V().has('id','department1').as('temp').
           in('worksFor','headOf').as('X').
           coalesce(
             out('teacherOf').as('Z').
               in('takesCourse').as('Y').
               out('advisor').where(eq('X')).
               select('temp','X','Y','Z'),
             constant('N/A').as('Y','Z').
               select('temp','X','Y','Z'))
==>[temp:v[12],X:v[9],Y:N/A,Z:N/A]
==>[temp:v[12],X:v[6],Y:v[0],Z:v[3]]
==>[temp:v[12],X:v[6],Y:v[0],Z:v[3]]

Cheers,
Daniel


To unsubscribe from this group and stop receiving emails from it, send an email to gremlin-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/gremlin-users/089f3b6b-961e-4f94-aab7-602990c8aefe%40googlegroups.com.

Udit Sharma

unread,
May 17, 2018, 3:04:25 AM5/17/18
to Gremlin-users
Thanks Daniel,
I got your point. But, I have a query regarding coalesce step:
Reply all
Reply to author
Forward
0 new messages