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.
Related
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).
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).
I am new Gremlin and having trouble with filtering by property.
A -> B
Assume A and B are vertices and has edge between them with properties Created_on and deleted_on.
deleted_on property will be added only at the time of deletion.
How list by the edge property?
g.V(id).outE('Label').has('deleted_on', lt(timestamp.now())).outV().elementMap()
The above query returns empty, because the deleted_on property is not added to the edge yet.
How to handle this?
I'm not completely sure but I think you are looking to find all connections where the deleted_on property is less than now or it does not exist. If that is the case then you can use the or() and hasNot() steps in Gremlin to accomplish this similar to the query below.
g.V(id).
outE('Label').
has('deleted_on', lt(timestamp.now())).
or().
hasNot('deleted_on').
outV().
elementMap()
i create the property for a vertex, as
g.addV('sth').property('p1', '1').property('p2', '2').property('p3', '3')
however when I query the vertex, like
g.V().hasLabel('sth').valueMap(true)
or
g.V().hasLabel('sth').properties()
the order of the properties is lost, I get
p3, p1, p2, how can I make sure I can order the property like the order I created.
Gremlin does not guarantee order for any result stream so if you need a specific order then you need to sort yourself:
gremlin> g.V().hasLabel('sth').valueMap().order(local).by(keys,desc)
==>[p3:[3],p2:[2],p1:[1]]
Of course, that isn't insertion order and I'm not sure how you would be able to achieve that as Gremlin does not have that information available to it - only the underlying graph database does. You may be beholden to what the underlying graph allows with respect to this.
I need to get vertices filtered by a specific predicate on the properties, and all the edges (with a particular label, and perhaps some predicate on the properties of an edge) existing between them.
This is for a Cosmos Azure Db graph, and the solution should be a single Gremlin query.
So far I am thinking of something along the lines of:
g.V().has('property1', value1).has('property2', value2).select('vertices')
.outE().as('edges').inV().has('property1', value1).has('property2', value2)
.select('vertices','edges')
Is there a better way to achieve this?
Given the description and your comment, this traversal should work for you:
g.V().has('property1', value1).has('property2', value2).
aggregate('v').
outE(). /* add edge filters here */
inV().where(within('v')).
path().
union(union(limit(local, 1),
tail (local, 1)).dedup().aggregate('vertices'),
range(local, 1, 2).aggregate('edges')).
cap('vertices','edges').next()