Tinkerpop3 Gremlin Traversal Error - gremlin

I have a graph with the following structure:
|-ProductFit
|-|-Part
|-|-App
|-|-|-ProductID
|-|-|-ProductModelID
|-|-|-ProductYearID
|-ProductID
|-|-ProductName
|-|-ProductModelID
|-|-ProductYearID
|-ProductModelID
|-|-ProductModelName
|-ProductYearID
|-|-ProductYear
where ProductFit is my first independent vertex and ProductID, ProductModelID and ProductYearID as my connected vertices.
Now, there are some fields of ProductID's in ProductFit having wrong values for which I need to get values from other vertex of ProductID.
Here is my query:
g.V().has('ProductFit','Part','PA01').properties('App')
.valueMap('ProductID','ProductModelID','ProductYearID')
.choose(values('ProductModelID'))
.option(PM01, g.V().has('ProductFit','Part','PA01').properties('App').values('ProductModelID'))
.option(PM02, g.V().has('ProductID','ProductModelID','PM01'))
.values('ProductModelID')
But this is giving me this error:
java.util.HashMap cannot be cast to org.apache.tinkerpop.gremlin.structure.Element
Is it that I can't go from one vertex to other during traversal or is there some problem in query? TIA.

Your choose() is using values() which is not meant to pick values out of a Map. It is meant to be used with an Element. I get the same error on The Crew toy graph:
gremlin> graph = TinkerFactory.createTheCrew()
==>tinkergraph[vertices:6 edges:14]
gremlin> g = graph.traversal()
==>graphtraversalsource[tinkergraph[vertices:6 edges:14], standard]
gremlin> g.V().properties('location').valueMap().choose(values('startTime')).option(2004,constant(1)).option(none,constant(2))
java.util.HashMap cannot be cast to org.apache.tinkerpop.gremlin.structure.Element
Type ':help' or ':h' for help.
Display stack trace? [yN]n
You should instead use select:
gremlin> g.V().properties('location').valueMap().choose(select('startTime')).option(2004,constant(1)).option(none,constant(2))
==>2
==>2
==>1
...
==>2

Related

How to inject map and filter based on map content in gremlin

I am running JanusGraph server and connected to it from gremlin console.
As can be seen, I have two vertices, with id property set to 1 and 2
gremlin> g.addV('user').property('id', 1)
==>v[4224]
gremlin> g.addV('user').property('id', 2)
==>v[4192]
gremlin> g.V().valueMap()
==>{id=[2]}
==>{id=[1]}
Next, I injected a map arraylist with different properties. I am trying to loop over this map and filter the vertices, but I couldn't figure out why the query isn't working. Can someone please help me figure out right query?
I tried using terminal step next() after select("id"), but that failed too.
gremlin> g.inject([["id": 1], ["id": 2, "something":"anything"]]).unfold().as("m").V().has("user", "id", select("m").select("id"))
Value [[SelectOneStep(last,m), SelectOneStep(last,id)]] is not an instance of the expected data type for property key [id] and cannot be converted. Expected: class java.lang.Integer, found: class org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal
Type ':help' or ':h' for help.
Display stack trace? [yN]
I can't use g.V().has(label, key, within(...)), as I need to use same map in multiple places and repeat steps for each row.
I would probably take a different approach to my traversal and avoid inject() in this case. With inject() you will have to unfold your list of Map objects and then do a vertex lookup for each which is probably expensive on all graph systems if your list is on the long side. I'd also wonder if you could write the traversal in such a way that graph systems will be able to optimize your vertex lookups with an index.
Given all that and your end goal of the traversal to update the properties of the found vertices I think I'd take this approach:
gremlin> g = TinkerGraph.open().traversal()
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin> g.addV('user').property('id', 1)
==>v[0]
gremlin> g.addV('user').property('id', 2)
==>v[2]
gremlin> m = [["id": 1], ["id": 2, "something":"anything"]]
==>[id:1]
==>[id:2,something:anything]
gremlin> ids = m.collect{it.id}
==>1
==>2
gremlin> g.withSideEffect('m',m).
......1> V().has('user','id',within(ids)).as('v').
......2> sideEffect(select('m').unfold().
......3> filter(select('id').as('ii').
......4> where('ii',eq('v')).
......5> by().
......6> by('id')).
......7> unfold().as('kv').
......8> select('v').
......9> property(select('kv').by(keys),select('kv').by(values)))
==>v[0]
==>v[2]
gremlin> g.V().elementMap()
==>[id:0,label:user,id:1]
==>[id:2,label:user,id:2,something:anything]
Here, we find all the vertices with within() as a single lookup and then filter() the list of data by the current vertex "id" property to then update the properties.
Try the following query, it should work:
g.inject([["id": 1], ["id": 2, "something":"anything"]]).
unfold().as("m").
V().
hasLabel("user").
has("id", select("m").select("id")).
dedup()
Your query failed because has(label, propertyKey, traversal) is not valid syntax. You can either use has(label, propertyKey, propertyValue) or has(label, propertyKey, predicate)

Gremlin: is it possible to filter on optional properties? (graph database is Neptune)

I have a graph with a single vertex:
gremlin> g.V().valueMap(true)
==>{id=a, x=[foo], label=vertex}
The vertex can be found in the following query:
gremlin> g.V().has('x', 'foo')
==>v[a]
However, I would like to modify the above query with the additional match constraint: match the vertex if it does not have property "y", or if the vertex does have property "y" and the value for property "y" equals "bar".
I have constructed the following query.
g.V().has('x', 'foo').or(__.hasNot('y'), __.has('y', 'bar'))
The query returns no matching vertices. So, I think I am looking for something equivalent to "IFNULL()" in mysql.
Any advice is much appreciated!
Joel
There's nothing wrong with your or() filter, it should just work.
gremlin> g = TinkerGraph.open().traversal()
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin> g.addV().property(id, 'a').property('x', 'foo').iterate()
gremlin> g.V().has('x', 'foo').or(__.hasNot('y'), __.has('y', 'bar'))
==>v[a]
Alternatively you can check that there's no y value that is not bar.
gremlin> g.V().has('x', 'foo').not(__.values('y').is(neq('bar')))
==>v[a]
However, double negations tend to be confusing, so I would just go with or().

How to access the vertices of a path in Gremlin through Java?

I'm using Java to access Gremlin. I have the following lines of code for creating a path between two vertices in an already created graph:
//begin simple path finding
Vertex fromNode = g.V().has("name", "i2").next();
Vertex toNode = g.V().has("name", "state1").next();
ArrayList list = new ArrayList();
g.V(fromNode).repeat(both().simplePath()).until(is(toNode)).limit(1).path().fill(list);
However, at this point I"m stuck. I can print out the list using list.toString() and I get the vertices that (I believe) are on the path. However, I now want to get those vertices themselves, access and potentially mutate their data. For instance, it isn't enough to know that v[32] is on the path if I can't access the ID 32 itself.
I should mention that I'm a bit new to this so please let me know if I'm using outdated/incorrect practices.
You ended up with a list filled with Path objects. I think what you're looking for is the unfold() step (see the docs) so you can unroll/flatten out the elements in path to put into the list. Here's a Gremlin Console session showing it:
gremlin> graph = TinkerGraph.open()
==>tinkergraph[vertices:0 edges:0]
gremlin> g = graph.traversal()
==>graphtraversalsource[tinkergraph[vertices:0 edges:0], standard]
gremlin> g.addV("name", "i2").as("a").addV("name", "state1").addE("to").from("a").toList()
==>e[4][0-to->2]
gremlin> fromNode = g.V().has("name", "i2").next()
==>v[0]
gremlin> toNode = g.V().has("name", "state1").next()
==>v[2]
gremlin> l = new ArrayList()
gremlin> g.V(fromNode).repeat(both().simplePath()).until(is(toNode)).limit(1).path().fill(l)
==>[v[0],v[2]]
gremlin> l = []; g.V(fromNode).repeat(both().simplePath()).until(is(toNode)).limit(1).path().fill(l)
==>[v[0],v[2]]
gremlin> l = []; g.V(fromNode).repeat(both().simplePath()).until(is(toNode)).limit(1).path().unfold().fill(l)
==>v[0]
==>v[2]
gremlin> l[0].getClass()
==>class org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertex
gremlin> l[0].property("name", "j3") // mutate the vertex's name
==>vp[name->i3]
gremlin> g.V(fromNode).valueMap(true).toList()
==>[id:0,name:[j3],label:vertex]
You should have access to these through list you're using on the fill step. The toString method won't print out the properties. If you just want to see those, you can call valueMap() instead of fill at the end of your traversal. If you'd like to iterate over all of the vertices and get the properties, you can use the properties method. If you want to get a specific property, you can use the property(key) method. The rest of what you'd need is in the API docs

How to remove an edge and add a new edge between two vertices?

I'm trying to drop an edge and add a new edge between two vertices. How do I do that in Tinkerpop3?
def user = g.V().has("userId", 'iamuser42').has("tenantId", 'testtenant').hasLabel('User');
user.outE("is_invited_to_join").where(otherV().has("groupId", 'test123')).drop();
def group = g.V().has("groupId", 'test123').has("tenantId", 'testtenant').hasLabel('Group').next();
user.addEdge("is_member_of", group);
This is the error I get on gremlin shell:
No signature of method: org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal.addEdge() is applicable for argument types: (java.lang.String, com.thinkaurelius.titan.graphdb.vertices.CacheVertex) values: [is_member_of, v[8224]]
Thank you.
The Gremlin Console tutorial discusses this issue a bit. You are not iterating your traversal. Consider the following:
gremlin> graph = TinkerFactory.createModern()
==>tinkergraph[vertices:6 edges:6]
gremlin> g = graph.traversal()
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
gremlin> person = g.V().has('name','marko')
==>v[1]
Great! The person Vertex is stored in the "person" variable...or is it?
gremlin> person.class
==>class org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal
Apparently it is not a Vertex. "person" is a Traversal, the console sees it as such and iterates it for you which is why you get "v1" in the output. Note that the traversal is now "done":
gremlin> person.hasNext()
==>false
You need to iterate the Traversal into your variable - in this case, using next():
gremlin> person = g.V().has('name','marko').next()
==>v[1]
gremlin> person.class
==>class org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertex
Note that you will have further problems down the line in your script because you are using what will now be a Vertex as a Traversal, so this line:
user.outE("is_invited_to_join").where(otherV().has("groupId", 'test123')).drop();
will now fail with a similar error because Vertex will not have outE(). You would need to do something like:
g.V(user).outE("is_invited_to_join").where(otherV().has("groupId", 'test123')).drop();
If you would like to add an edge and drop the old one in the same traversal, you could do something like this:
gremlin> graph = TinkerFactory.createModern()
==>tinkergraph[vertices:6 edges:6]
gremlin> g = graph.traversal()
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
gremlin> g.V(1).outE('knows')
==>e[7][1-knows->2]
==>e[8][1-knows->4]
gremlin> g.V(1).as('a').outE().as('edge').inV().hasId(2).as('b').addE('knew').from('a').to('b').select('edge').drop()
gremlin> g.V(1).outE('knew')
==>e[12][1-knew->2]
gremlin> g.V(1).outE('knows')
==>e[8][1-knows->4]
Arguably, that might not be as readable as breaking it into two or more separate traversals, but it can be done.

Return subset of properties from a node through Gremlin query

Is there a way to return just a subset of all properties of a graph node with a Gremlin query?
I know you cand use the transform pipe to concatenate property values, but I'm more interested in obtaining structured results (like propertyName1 = value1, propertyName2 = value2).
Assuming you're using Gremlin-Groovy, I find using Groovy's each construct to be a useful way to create my own objects from Gremlin query results. For example:
nodes = []
g.V.each{ node ->
nodes += [ propertyName1 : node.value1, propertyName2 : node.value2 ]
}
Now you have a List of Map objects representing nodes returned from your query. It's possible this isn't the most efficient way but it is very flexible.
As of Gremlin 2.4.0, you can do:
gremlin> g = TinkerGraphFactory.createTinkerGraph()
==>tinkergraph[vertices:6 edges:6]
gremlin> g.v(1).out.map('name')
==>{name=vadas}
==>{name=josh}
==>{name=lop}
gremlin> g.v(1).out.map('name','age')
==>{age=27, name=vadas}
==>{age=32, name=josh}
==>{age=null, name=lop}
It is possible doing this:
If data is:
gremlin> g.E.has('weight', T.gt, 0.5f).outV.age
==>32
==>29
Then the query to do is:
gremlin> g.E.has('weight', T.gt, 0.5f).outV.transform{[id:it.id,age:it.age]}
==>{id=4, age=32}
==>{id=1, age=29}
Have you looked at using Table??
Sample from : Gremlin Wiki
gremlin> t = new Table()
gremlin> g.v(1).out('knows').as('x').out('created').as('y').table(t){it.name}{it.name}
==>v[5]
==>v[3]
gremlin> t
==>[x:josh, y:ripple]
==>[x:josh, y:lop]

Resources