How to query hops using PGQL? - oracle-graph

We are trying to write PGQL query to get multiple hop(s) for selected node.
To get nodes and edge for the selected node,
SELECT n0.id as n0id, e0.id as e0id, n1.id as n1id FROM MATCH (n0)->[e0]->(n1) WHERE n0.id=12345
To increase nodes and edges result, example 2 hops,
... FROM MATCH (n0)->[e0]->(n1)->[e1]->(n3) ...
However in this case, nodes with 1 hop will not be return.
I wonder if there is any way query required hop(s) for selected node?
Any solution would be appreciated.

It seems you are looking for the PGQL syntax for variable-length paths:
https://pgql-lang.org/spec/1.4/#variable-length-paths
Among these patterns, I suppose the reachability syntax is useful in your case.
SELECT n0.id as n0id, e0.id as e0id, n1.id as n1id
FROM MATCH (n0)->/:edge-label{1,3}/->(n1)
WHERE n0.id=12345
https://pgql-lang.org/spec/1.4/#between-n-and-m
Thanks!

Related

Gremlin - simultaneously limit traverse iterations and search for edge property

I have a (broken) piece of gremlin code to generate the shortest path from a given vertex to one which has the parameter test_parameter. If that parameter is not found on an edge, no paths should be returned.
s.V(377524408).repeat(bothE().has('date', between(1554076800, 1556668800)).otherV()) /* date filter on edges */
.until(or(__.bothE().has('test_property', gt(0)),
loops().is(4))) /* broken logic! */
.path()
.local(unfold().filter(__.has('entity_id')).fold()) /* remove edges from output paths*/
The line that's broken is .until(or(__.outE().has('test_property', gt(0)), loops().is(4))).
At present - and it makes sense as to why - it gives all paths that are 4 hops from the starting vertex.
I'm trying to adapt it so that if the traverse is at 4 iterations, and if the property test_property is not found, then it should not return any paths. If test_property is found, it should return only the path(s) to that vertex.
I've attempted to put a times(4) constraint in and removing the loops() condition, but don't know how to have both the times(4) this and the .has('test_property', gt(0)) constraint.
Daniel's answer has few issues (see comments).
This query returns the correct result:
g.V(377524408)
.repeat(bothE().has('date', between(1554076800, 1556668800)).otherV().simplePath().as("v"))
.until(and(bothE().has('tp', gt(0)), loops().is(lte(4))))
.select(all, "v")
.limit(1)
The simplePath() is required so we won't go back and forth and avoid circles.
The repeat loop is until the condition is met AND we have not reached max hop.
The limit(1) return only the first (shortest) path. Omit to get all paths.
Note that if the graph is directed it is better to use outE() and not bothE().
This should work:
s.V(377524408).
repeat(bothE().has('date', between(1554076800, 1556668800)).otherV().as('v')).
times(4).
filter(bothE().has('test_property', gt(0))).
select(all, 'v')
Also note, that I replaced your local(unfold().filter(__.has('entity_id')).fold()) with something much simpler (assuming that the sole purpose was the removal of edges from the path).

Gremlin: Add edges to multiple vertices

I have vertices [song1, song2, song3, user].
I want to add edges listened from user to the songs.
I have the following:
g.V().is(within(song1, song2, song3)).addE('listened').from(user)
However I'm getting the following error:
No signature of method: org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.DefaultGraphTraversal.from() is applicable for argument types: (org.janusgraph.graphdb.vertices.CacheVertex) values: [v[4344]]
Possible solutions: sort(), drop(int), sum(), find(), grep(), sort(groovy.lang.Closure)
Of course, I can iterate through them one at a time instead but a single query would be nice:
user.addEdge('listened', song1)
user.addEdge('listened', song2)
user.addEdge('listened', song3)
The from() modulator accepts two things:
a step label or
a traversal
A single vertex or a list of vertices can easily be turned into a traversal by wrapping it in V(). Also, note that g.V().is(within(...)) will most likely end up being a full scan over all vertices; it pretty much depends on the provider implementation, but you should prefer to use g.V(<list of vertices>) instead. Thus your traversal should look more like any of these:
g.V().is(within(song1, song2, song3)).
addE('listened').from(V(user)) // actually bad, as it's potentially a full scan
g.V(song1, song2, song3).
addE('listened').from(V(user))
g.V(user).as('u').
V(within(song1, song2, song3)).
addE('listened').from('u')

Traverse Graph With Directed Cycles using Relationship Properties as Filters

I have a Neo4j graph with directed cycles. I have had no issue finding all descendants of A assuming I don't care about loops using this Cypher query:
match (n:TEST{name:"A"})-[r:MOVEMENT*]->(m:TEST)
return n,m,last(r).movement_time
The relationships between my nodes have a timestamp property on them, movement_time. I've simulated that in my test data below using numbers that I've imported as floats. I would like to traverse the graph using the timestamp as a constraint. Only follow relationships that have a greater movement_time than the movement_time of the relationship that brought us to this node.
Here is the CSV sample data:
from,to,movement_time
A,B,0
B,C,1
B,D,1
B,E,1
B,X,2
E,A,3
Z,B,5
C,X,6
X,A,7
D,A,7
Here is what the graph looks like:
I would like to calculate the descendants of every node in the graph and include the timestamp from the last relationship using Cypher; so I'd like my output data to look something like this:
Node:[{Descendant,Movement Time},...]
A:[{B,0},{C,1},{D,1},{E,1},{X,2}]
B:[{C,1},{D,1},{E,1},{X,2},{A,7}]
C:[{X,6},{A,7}]
D:[{A,7}]
E:[{A,3}]
X:[{A,7}]
Z:[{B,5}]
This non-Neo4J implementation looks similar to what I'm trying to do: Cycle enumeration of a directed graph with multi edges
This one is not 100% what you want, but very close:
MATCH (n:TEST)-[r:MOVEMENT*]->(m:TEST)
WITH n, m, r, [x IN range(0,length(r)-2) |
(r[x+1]).movement_time - (r[x]).movement_time] AS deltas
WHERE ALL (x IN deltas WHERE x>0)
RETURN n, collect(m), collect(last(r).movement_time)
ORDER BY n.name
We basically find all the paths between any of your nodes (beware cartesian products get very expensive on non-trivial datasets). In the WITH we're building a collection delta's that holds the difference between two subsequent movement_time properties.
The WHERE applies an ALL predicate to filter out those having any non-positive value - aka we guarantee increasing values of movement_time along the path.
The RETURN then just assembles the results - but not as a map, instead one collection for the reachable nodes and the last value of movement_time.
The current issue is that we have duplicates since e.g. there are multiple paths from B to A.
As a general notice: this problem is much more elegantly and more performant solvable by using Java traversal API (http://neo4j.com/docs/stable/tutorial-traversal.html). Here you would have a PathExpander that skips paths with decreasing movement_time early instead of collection all and filter out (as Cypher does).

Find path for N levels with repeating pattern of directional relationships in Neo4J

I'm trying to use Neo4j to analyze relationships in a family tree. I've modeled it like so:
(p1:Person)-[:CHILD]->(f:Family)<-[:FATHER|MOTHER]-(p2)
I know I could have left out the family label and just had children connected to each parent, but that's not practical for my purposes. Here's an example of my graph and the black line is the path I want it to generate:
I can query for it with
MATCH p=(n {personID:3})-[:CHILD]->()<-[:FATHER|MOTHER]-()-[:CHILD]->()<-[:FATHER|MOTHER]-()-[:CHILD]->()<-[:FATHER|MOTHER]-() RETURN p
but there's a repeating pattern to the relationships. Could I do something like:
MATCH p=(n {personID:3})(-[:CHILD]->()<-[:FATHER|MOTHER]-())* RETURN p
where the * means repeat the :CHILD then :FATHER|MOTHER relationships, with the directions being different? Obviously if the relationships were all the same direction, I could use
-[:CHILD|FATHER|MOTHER*]->
I want to be able to query it from Person #3 all the way to the top of the graph like a pedigree chart, but also be specific about how many levels if needed (like 3 generations, as opposed to end-of-line).
Another issue I'm having with this, is if I don't put directions on the relationships like -[:CHILD|FATHER|MOTHER*]-, then it will start at Person #3, and go both in the direction I want (alternating arrows), but also descend back down the chain finding all the other "cousins, aunts, uncles, etc.".
Any seasoned Cypher experts that an help me?
I am just on the same problem. And I found out that the APOC Expand path procedures are just accomplishing what you/we want.
Applied to your example, you could use apoc.path.subgraphNodes to get all ancestors of Person #3:
MATCH (p1:Person {personId:3})
CALL apoc.path.subgraphNodes(p1, {
sequence: '>Person,CHILD>,Family,<MOTHER|<FATHER'
}) YIELD node
RETURN node
Or if you want only ancestors up to the 3 generations from start person, add maxLevel: 6 to config (as one generation is defined by 2 relationships, 3 generations are 6 levels):
MATCH (p1:Person {personId:3})
CALL apoc.path.subgraphNodes(p1, {
sequence: '>Person,CHILD>,Family,<MOTHER|<FATHER',
maxLevel: 6
}) YIELD node
RETURN node
And if you want only ancestors of 3rd generation, i.e. only great-grandparents, you can also specify minLevel (using apoc.path.expandConfig):
MATCH (p1:Person {personId:3})
CALL apoc.path.expandConfig(p1, {
sequence: '>Person,CHILD>,Family,<MOTHER|<FATHER',
minLevel: 6,
maxLevel: 6
}) YIELD path
WITH last(nodes(path)) AS person
RETURN person
You could reverse the directionality of the CHILD relationships in your model, as in:
(p1:Person)<-[:CHILD]-(f:Family)<-[:FATHER|MOTHER]-(p2)
This way, you can use a simple -[:CHILD|FATHER|MOTHER*]-> pattern in your queries.
Reversing the directionality is actually intuitive as well, since you can then more naturally visualize the graph as a family tree, with all the arrows flowing "downwards" from ancestors to descendants.
Yeah, that's an interesting case. I'm pretty sure (though I'm open to correction) that this is just not possible. Would it be possible for you to have and maintain both? You could have a simple cypher query create the extra relationships:
MATCH (parent)-[:MOTHER|FATHER]->()<-[:CHILD]-(child)
CREATE (child)-[:CHILD_OF]->parent
Ok, so here's a thought:
MATCH path=(child:Person {personID: 3})-[:CHILD|FATHER|MOTHER*]-(ancestor:Person),
WHERE ancestor-[:MOTHER|FATHER]->()
RETURN path
Normally I'd use a second clause in the MATCH like this:
MATCH
path=(child:Person {personID: 3})-[:CHILD|FATHER|MOTHER*]-(ancestor:Person),
ancestor-[:MOTHER|FATHER]->()
RETURN path
But Neo4j (at least by default, I think) doesn't traverse back through the path. Maybe comma-separating would be fine and this would be a problem:
MATCH path=(child:Person {personID: 3})-[:CHILD|FATHER|MOTHER]-(ancestor:Person)-[:MOTHER|FATHER]->()
I'm curious to know what you find!

neo4j logic gate simulation, how to?

I would like to create a bunch of "and" and "or" and "not" gates in a directed graph.
And then traverse from the inputs to see what they results are.
I assume there is a ready made traversal that would do that but I don't see it.
I don't know what the name of such a traversal would be.
Certainly breadth first will not do the job.
I need to get ALL the leaves, and go up toward the root.
In other words
A = (B & (C & Z))
I need to resolve C # Z first.
I need to put this type of thing in a graph and to traverse up.
You would probably create each of the operations as a node which has N incoming and one outgoing connection. You can of course also have more complex operations encapsuled as a node.
With Neo4j 2.0 I would use Labels for the 3 types of operations.
I assume your leaves would then be boolean values? Actually I think you have many roots and just a single leaf (the result expression)
(input1)-->(:AND {id:1})-->(:OR {id:2})-->(output)
(input2)-->(:AND {id:1})
(input3)------------------>(:OR {id:2})
Then you can use CASE when for decisions on the label type and use the collection predicates (ALL, ANY) for the computation
See: http://docs.neo4j.org/chunked/milestone/cypher-query-lang.html
Predicates: http://docs.neo4j.org/chunked/milestone/query-function.html
Labels: http://docs.neo4j.org/chunked/milestone/query-match.html#match-get-all-nodes-with-a-label

Resources