Gremilin select Id from elementMap to get the vertex by id - gremlin

I try to get a vertex by an id which is in a element map. Something like
g.V(g.V().limit(1).elementMap().as(‘map’).select(‘map’).by(‘id’))
This is just a simplified example I must use the elementMap since I get an elementMap from an union result where I must use elementMap to order the vertices by specific properties which might be not available in a vertex.
And what I don’t understand at all is why selecting id or label returns null, while selecting an other property from the map of the vertex works.
g.V().limit(1).elementMap().as(‘map’).select(‘map’).by(‘id’)
==>null
g.V().limit(1).elementMap().as(‘map’).select(‘map’).by(‘key’)
==>value
So how can I achieve selecting the id in the elementMap to get the vertex for that id?

gremlin> g.V(
g.V().limit(1).elementMap().unfold()
.where(select(keys).is(eq(id))).select(values).next() )
==>v[1]
Things got confusing because your original query had three issues:
You have to next() the inner traversal to get a value rather than the value wrapped in an iterating GraphTraversal. Note that the gremlin console does next() automatically for the outer traversal.
The by() modulator on the elementMap only works on the values of the Map
Although the ElementMap seems to show an 'id' string as key it actually is the id token defined by TinkerPop
Finally note that you may not need the elementMap step at all and do the query like this:
gremlin> g.V(g.V().limit(1).id().next())
==>v[1]
[This answer was significantly extended after the comments below]

Related

UPSERT an edge with properties with Gremlin

I'm trying to upsert an edge (insert if it does not exist but update if it does) with properties in Gremlin via a single query.
I have this which adds a manages edge with a duration property between two vertices with id of Amy and John.
g.E().where(outV().has('id','Amy')).where(inV().has('id','John')).fold().coalesce(unfold(),g.V('Amy').as('a').V('John').addE('manages').from('a').property('duration','1year')).
The query does do an upsert, but if I change the value of the duration property, remove the duration property, or add other properties, the property changes are not reflected on the edge.
I want to be able to change the value of the duration property without adding a new edge or having to remove the old one.
I'm fairly new to Gremlin so please share any advice which may help.
I'm not sure it matters but the underlying DB is an Azure Cosmos DB.
To always have the property change apply whether the edge exists or is created, you just need to move the property step to after the coalesce:
g.E().
where(outV().has('id', 'Amy')).
where(inV().has('id', 'John')).
fold().
coalesce(
unfold(),
g.V('Amy').as('a').
V('John').
addE('manages').from('a')).
property('duration', '1year')
However, that said, there are a few observations that can be made about the query. Starting with g.E() is likely to be inefficient and using g.V() mid traversal should be avoided, and where necessary just V() used.
If 'John" and 'Amy' are unique ID's you should take advantage of that along these lines:
g.V('Amy').
outE().where(inV().hasId('John')).
fold().
coalesce(
unfold(),
addE('manages').from(V('Amy')).to(V('John'))).
property('duration', '1year')
Two additions to Kevin's answer:
I think you need to specify the edge label in outE, otherwise the unfold may return other relationships present between the two vertices, and these will be updated with the property, rather than triggering the addE of the new edge. Specifying the edge label should ensure the length of the folded array is 0 in the case of INSERT and 1 in the case of UPDATE.
For Cosmos DB Gremlin API, use the anonymous class __ when not using g
g.V('Amy').
outE('manages').where(inV().hasId('John')).
fold().
coalesce(
unfold(),
__.addE('manages').from(__.V('Amy')).to(__.V('John'))).
property('duration', '1year')
More detailed example here.

How to update an edge properties if it already exists?

I'm writing a gremlin python query with the intention that
Create an edge if it doesn't exist
Update the edge properties if it does
I read some answers here already and got the first part figured out using coalesce but I'm not sure how to update the edge if it exists. I would imagine that would happen in the first part of the coalesce but I tried select edge and that didn't work
Here is what I have so far
g.V().
hasLabel('person').as_('p').
V().
hasLabel('house').as_('h').
coalesce(
__.inE('owns').where(__.outV().as_('p')),
__.addE('owns').from_('p').to('h').
property(Cardinality.set_, 'duration', 2)).
iterate()
Before writing the query, you need to correct your assumption on Edge property cardinality. From your query, it looks like you want to update the property of the edge with Set cardinality.
Tinkerpop does not support Set cardinality on Edge properties.
TLDR answer:
g.V().
hasLabel('house').as('p').
inE('owns').
where(outV().hasLabel('person')).
fold().
coalesce(
unfold().property('duration', 2),
addE('owns').from(V().hasLabel('person')).to(V().hasLabel('house')))
Above query is based on assumption that you only have 2 vertices in the database, first with label 'house' and second with label 'person'
If above information is incorrect then you would need to update your vertices filter to point them to single Vertex (unless you are planning to add mass edges).

How to get properties hasid/key with vertexes info in one gremlin query or Gremlin.Net

I try to get properties which has key or id in following query by Gremlin.Net, but vertex info(id and label) in VertexProperty is null in result.
g.V().Properties<VertexProperty>().HasKey(somekey).Promise(p => p.ToList())
So i try another way, but it's return class is Path, and i had to write an ugly code for type conversion.
g.V().Properties<VertexProperty>().HasKey(somekey).Path().By(__.ValueMap<object, object>(true))
Is there a better way to achieve this requirement
I think basically the only thing missing to get what you want is the Project() step.
In order to find all vertices that have a certain property key and then get their id, label, and then all information about that property, you can use this traversal:
g.V().
Has(someKey).
Project<object>("vertexId", "vertexLabel", "property").
By(T.Id).
By(T.Label).
By(__.Properties<object>(someKey).ElementMap<object>()).
Promise(t => t.ToList());
This returns a Dictionary where the keys are the arguments given to the Project step.
If you instead want to filter by a certain property id instead of a property key, then you can do it in a very similar way:
g.V().
Where(__.Properties<object>().HasId(propertyId)).
Project<object>("vertexId", "vertexLabel", "property").
By(T.Id).
By(T.Label).
By(__.Properties<object>(someKey).ElementMap<object>()).
Promise(t => t.ToList());
This filters in both cases first the vertices to only have vertices that have the properties we are looking for. That way, we can use the Project() step afterwards to get the desired data back.
ElementMap should give all information back about the properties that you want.
Note however that these traversals will most likely require a full graph scan in JanusGraph, meaning that it has to iterate over all vertices in your graph. The reason is that these traversals cannot use an index which would make them much more efficient. So, for larger graphs, the traversals will probably not be feasible.
If you had the vertex ids available instead of the property ids in the second traversal, then you could make the traversal a lot more efficient by replacing g.V().Where([...]) simply with g.V(id).

The provided traversal does not map to a value

I am trying to execute a math query.
gts.V()
.hasLabel("account")
.has("id",42)
.both("account_label1").as("label1")
.and(__.identity()
.project("a","b")
.by(__.identity()
.both("label1_label2")
.both("label2_label3")
.values("createTime"))
.by(__.identity()
.both("label1_label4")
.both("label4_label5")
.values("launchTime"))
.math("floor((a-b)/(86400))").is(100))
.select("label1")
.toList()
Above query fails with error
The provided traverser does not map to a value: v[137]->[IdentityStep, VertexStep(BOTH,[label1_label2],vertex), VertexStep(BOTH,[label2_label3],vertex), NoOpBarrierStep(2500), PropertiesStep([createTime],value)]
Why is gremlin injection NoOpBarrierStep?
What is the meaning of the NoOpBarrierStep(2500)?
What will be the correct gremlin query for the same?
When you use project() it expects a value for each by() modulator and that value should not produce an empty Iterator. Here's a simple example:
gremlin> g = TinkerFactory.createModern().traversal()
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
gremlin> g.V().project('x').by(out())
==>[x:v[3]]
The provided traverser does not map to a value: v[2]->[VertexStep(OUT,vertex)]
Type ':help' or ':h' for help.
Display stack trace? [yN]
The first vertex is able to traverse out() but the next one processed by project() has no outgoing edges and therefore produces this error. In your case, that simply means that not all of your traversers can traverse both().both() or if they can, you would want to be sure that they all had "createTime" property values. Either of those scenarios could cause the problem.
You could fix this in a variety of ways. Obviously, if it's a data problem you could simply fix your data and always assume that the traversal path is right. If that's not the case, you need to write your Gremlin to be a bit more forgiving if the traversal path is not available. In my case I could do:
gremlin> g.V().project('x').by(out().fold())
==>[x:[v[3],v[2],v[4]]]
==>[x:[]]
==>[x:[]]
==>[x:[v[5],v[3]]]
==>[x:[]]
==>[x:[v[3]]]
Perhaps in your case you might do:
by(coalesce(both("label1_label2").both("label2_label3").values("createTime"),
constant('n/a')))
Note that you do not need to specify identity() for the start of your anonymous traversals.
Finally, in answer to your questions about NoOpBarrierStep, that step is injected into traversals where Gremlin thinks it can take advantage of a bulking optimization. You can add them yourself with barrier() step as well. Here's a quick description of "bulking" as taken from the TinkerPop Reference Documentation:
The theory behind a "bulking optimization" is simple. If there are one million traversers at vertex 1, then there is no need to calculate one million both()-computations. Instead, represent those one million traversers as a single traverser with a Traverser.bulk() equal to one million and execute both() once.

Gremlin: how to overcome property null problem and write a query which updates all properties of a certain vertex?

I need to write a single Gremlin query that can set the new property values of a vertex. All the property names are known in advance (in this example: Type, Country, Status). Some of the property values can be null - and I don't know which ones in advance. The query should work for all cases. For example, let's say I currently have this query:
g.V(123).
property('Type',Type).
property('Country',Country).
property('Status',Status)
This query works fine if all the parameter (Type, Country, Status) values are non-null. If, say, Country is null, I get an error:
The AddPropertyStep does not have a provided value: AddPropertyStep({key=[Country]})
In such case I would need to use a different query to drop the property (by the way, is there a better way for dropping a property?):
g.V(123).
property('Type',Type).
property('Status',Status).
properties('Country').drop()
Is it possible to write a universal query that can handle both null and non-null values? I cannot use console or programming, just a single Gremlin query to be executed.
Thanks!
TinkerPop doesn't allow null values in properties (though you might find some graph databases allowing different semantics there, I suppose), so you should validate your data up front to ensure that it has some meaningful "empty value" as opposed to a null. If you can't do that for some reason, I guess you could use choose() step to "check for null":
gremlin> g = TinkerFactory.createModern().traversal()
==>graphtraversalsource[tinkergraph[vertices:6 edges:6], standard]
gremlin> g.V().has('person','name','marko').valueMap()
==>[name:[marko],age:[29]]
gremlin> age = null
gremlin> g.V().has('person','name','marko').choose(constant(age).count().is(0), properties('age').drop(),property('age',age))
gremlin> g.V().has('person','name','marko').valueMap()
==>[name:[marko]]
gremlin> age = 30
==>30
gremlin> g.V().has('person','name','marko').choose(constant(age).count().is(0), properties('age').drop(),property('age',age))
==>v[1]
gremlin> g.V().has('person','name','marko').valueMap()
==>[name:[marko],age:[30]]
The check for "is null" is basically just: constant(age).count().is(0), which leans on the Gremlin's semantics for null values in a stream being empty and giving a count() of zero. It works, but it makes your Gremlin a little less readable. That might be a nice DSL step to add if you have to write that a lot.

Resources