relationship types in py2neo - neo4j

How can I can get the type of the relationship with a cypher query in py2neo?
I have this piece of code that works.
def print_row(row):
a,b = row
print (a["name"] + " " + b["name"])
cypher.execute(graph_db, "START a=node(1) MATCH (a) - [] - (b) RETURN a,b", row_handler=print_row)
This way I can print out the nodes that are connected to my input node (1).
ROCK PAPER
ROCK SCISSORS
However what I would like is to print the type of relationship that these nodes have.
For instance:
ROCK BEATS SCISSORS
ROCK BEATEN_BY PAPER
What I tried (and failed) is as follows:
def print_row(row):
a,b,r = row
print (a["name"] + r["type"] + b["name"])
cypher.execute(graph_db,"START a=node(1) MATCH (a) -[r]->(b) RETURN a,b,r", row_handler=print_row)
How can I do this with py2neo?

You need to use the Cypher TYPE function (http://docs.neo4j.org/chunked/milestone/query-functions-scalar.html#functions-type). Your code will then look something like this:
def print_row(row):
a, r_type, b = row
print(a["name"] + " " + r_type + " " + b["name"])
cypher.execute(graph_db, "START a=node(1) MATCH (a)-[r]->(b) RETURN a, TYPE(r), b", row_handler=print_row)

Related

Construct queries with a condition

I am using Spring Data Neo4j and I have a repository like this:
public interface MyNeo4jRepository extends Neo4jRepository<Object, Long> {
#Query("with ['X', 'Y','Z'] as list_labels, "
+ "$appsFilter as appsList\n "
+ "MATCH (apps:) where apps.n IN appsList "
+ "MATCH (a)<-[:event]-(nodes) "
+ "WHERE any(x IN labels(nodes) WHERE x IN list_labels) "
+ "CALL apoc.path.expandConfig(nodes, { "
+ "relationshipFilter: 'R1|R2>',"
+ "labelFilter: '-l1|>l2',"
+ "maxLevel: 6,"
+ "endNodes: [apps],"
+ "uniqueness: 'NODE_PATH'}) YIELD path "
+ "unwind nodes(path) as n "
...
}
I want to create this query using conditions like this:
#Query("with ['X', 'Y','Z'] as list_labels, "
+ "$appsFilter as appsList\n "
+ "MATCH (apps:) where apps.n IN appsList "
+ "MATCH (a)<-[:event]-(nodes) "
+ "WHERE any(x IN labels(nodes) WHERE x IN list_labels) "
if (condition) + "WHERE ...." else + ""
+ "CALL apoc.path.expandConfig(nodes, { "
...
Is there a way to do it in the Neo4j query or do I have to do it with Spring composable repositories?
I think what you are looking for is the CASE construct
'WHERE ....'
+
CASE
WHEN condition1 THEN 'cypherFragement1'
WHEN condition2 THEN 'cypherFragement2'
ELSE 'cypherFragementElse'
END
+
.....
Maybe you can rewrite your entire cypher via apoc.do.when or apoc.do.case functions, which provide conditional judgment.

Neo4j - Check if sequential path exists

I want to see if a path exists for a graph, given a list of sequential properties to search for. The list can be of variable length.
This is my most recent attempt:
WITH ['a', 'b', 'c', 'd'] AS search_list // can be any list of strings
// FOREACH (i IN range(search_list) |
// MATCH (a:Node {prop:i})-->(b:Node {prop:i+1}))
// RETURN true if all relationships exist, false if not
This solution doesn't work because you can't use MATCH in a FOREACH. What should I do instead?
You can try to build a query manually for the match entire path and execute it using the function apoc.cypher.run:
WITH ['a', 'b', 'c', 'd'] AS search_list
WITH search_list,
'MATCH path = ' +
REDUCE(c = '', i in range(0, size(search_list) - 2) |
c + '(:Node {prop: $props[' + i + ']})-->'
) +
'(:Node {prop: $props[' + (size(search_list) - 1) +']}) ' +
'RETURN count(path) as pathCount' AS cypherQuery
CALL apoc.cypher.run(cypherQuery, {props: search_list}) YIELD value
RETURN CASE WHEN value.pathCount > 0
THEN true
ELSE false
END AS pathExists
Assuming you pass the list of property values in a $props parameter and the length of that list is 4, this query will first search for all paths of length 4 that have the desired start and end nodes (to narrow down the candidate paths), and then filter the interior nodes of the paths:
MATCH p=(a:Node {prop: $props[0]})-[*4]->(b:Node {prop: $props[-1]})
WITH p, NODES(p)[1..-2] AS midNodes
WHERE ALL(i IN RANGE(1, SIZE(midNodes)) WHERE midNodes[i-1] = $props[i])
RETURN p;
To increase efficiency, you should create an index on :Node(prop) as well.
If this query returns nothing, then there are no matching paths.

Cypher: analog of `sort -u` to merge 2 collections?

Suppose I have a node with a collection in a property, say
START x = node(17) SET x.c = [ 4, 6, 2, 3, 7, 9, 11 ];
and somewhere (i.e. from .csv file) I get another collection of values, say
c1 = [ 11, 4, 5, 8, 1, 9 ]
I'm treating my collections as just sets, order of elements does not matter. What I need is to merge x.c with c1 with come magic operation so that resulting x.c will contain only distinct elements from both. The following idea comes to mind (yet untested):
LOAD CSV FROM "file:///tmp/additives.csv" as row
START x=node(TOINT(row[0]))
MATCH c1 = [ elem IN SPLIT(row[1], ':') | TOINT(elem) ]
SET
x.c = [ newxc IN x.c + c1 WHERE (newx IN x.c AND newx IN c1) ];
This won't work, it will give an intersection but not a collection of distinct items.
More RTFM gives another idea: use REDUCE() ? but how?
How to extend Cypher with a new builtin function UNIQUE() which accept collection and return collection, cleaned form duplicates?
UPD. Seems that FILTER() function is something close but intersection again :(
x.c = FILTER( newxc IN x.c + c1 WHERE (newx IN x.c AND newx IN c1) )
WBR,
Andrii
How about something like this...
with [1,2,3] as a1
, [3,4,5] as a2
with a1 + a2 as all
unwind all as a
return collect(distinct a) as unique
Add two collections and return the collection of distinct elements.
dec 15, 2014 - here is an update to my answer...
I started with a node in the neo4j database...
//create a node in the DB with a collection of values on it
create (n:Node {name:"Node 01",values:[4,6,2,3,7,9,11]})
return n
I created a csv sample file with two columns...
Name,Coll
"Node 01","11,4,5,8,1,9"
I created a LOAD CSV statement...
LOAD CSV
WITH HEADERS FROM "file:///c:/Users/db/projects/coll-merge/load_csv_file.csv" as row
// find the matching node
MATCH (x:Node)
WHERE x.name = row.Name
// merge the collections
WITH x.values + split(row.Coll,',') AS combo, x
// process the individual values
UNWIND combo AS value
// use toInt as the values from the csv come in as string
// may be a better way around this but i am a little short on time
WITH toInt(value) AS value, x
// might as well sort 'em so they are all purdy
ORDER BY value
WITH collect(distinct value) AS values, x
SET x.values = values
You could use reduce like this:
with [1,2,3] as a, [3,4,5] as b
return reduce(r = [], x in a + b | case when x in r then r else r + [x] end)
Since Neo4j 3.0, with APOC Procedures you can easily solve this with apoc.coll.union(). In 3.1+ it's a function, and can be used like this:
...
WITH apoc.coll.union(list1, list2) as unionedList
...

Cypher: find a path which takes the maximum valued step each time

I am trying to write a cypher query that finds a path between nodes a and b such that each step has the maximum timestamp value out of all available alternatives that is less than 15.
Here is my query so far, it does everything except for select the maximum possible timestamp at each step. How do I express this condition?
MATCH path=(a:NODE)-[rs:PARENT*]->(b:NODE)
WHERE a.name = 'SOME_VALUE' and b.name = 'SOME_OTHER_VALUE' AND ALL (r IN rs
WHERE r.timestamp < 15)
RETURN path
This is just awful sudo code but I think it expresses what I am looking for
MATCH path=(a:NODE)-[rs:PARENT*]->(b:NODE)
WHERE a.name = 'SOME_VALUE' and b.name = 'SOME_OTHER_VALUE' AND ALL (r IN rs
WHERE r.timestamp < 15 AND r.timestamp = max(allPossibleRsForThisStep))
RETURN path
Can this kind of query be written in cypher?
It won't be fast in cypher, it's possible to compute all maximum values first and then do what you want to do by compare the max value in a list with the current value.
Something like this (not sure if it works)
WITH range(1,10) as max_vals // a list with 10 values (actual values are not that important)
MATCH (a:NODE)-[rs:PARENT*..10]->(b:NODE)
WHERE a.name = 'SOME_VALUE' and b.name = 'SOME_OTHER_VALUE'
WITH a,b,
map(idx in range(0,size(rs)) |
max_vals[idx] = case when max_vals[idx]<rs[idx].timestamp then rs[idx].timestamp else max_vals[idx] end ), max_vals
MATCH path=(a)-[rs:PARENT*..10]->(b)
AND ALL (idx in range(0,size(rs) WHERE rs[idx].timestamp < 15 AND rs[idx].timestamp = max_vals[idx])
RETURN path

Iterate through Neo4j relationships and return the minimum value of relationships properties

I want to iterate through the relationships between the "begining node" and the "end node".
Indeed, there is my cypher request :
MATCH (ar1:Article)-[:PART_OF]->()-[:SERIES]->(s1),
(ar2:Article)-[:PART_OF]->()-[:SERIES]->(s2),
(ar1)-[:CREATOR]->(au1:Author),
(ar2)-[:CREATOR]->(au1:Author),
p1 = (au1)-[CONTRIBUTOR*]->(au2:Author)
WITH REDUCE (edge IN relationships(p1)|weight + 1/edge.fdegree) AS
strength_au1_au2_p1,ar1 AS ar1,s1 AS s1,ar2 AS ar2,s2 AS s2,au1 AS au1,au2 AS au2
WHERE s1.name='WWW' AND s2.name='Pods' AND ar2.year >2010.0 AND ar1.year >2010.0
AND strength_au1_au2_p1<5.0
RETURN ar1,s1,ar2,s2,au1,au2,ar1.year AS calc_fuzzy_ar1_year_recent,ar2.year AS
calc_fuzzy_ar2_year_recent,strength_au1_au2_p1 AS calc_fuzzy_length_p1_short**
Now I want to iterate through CONTRIBUTOR* relationships (in p1) and get each of its 'fdegree' and return the minimum value(fdegree) of relationships in p1.
Thank you all
Try this:
MATCH (au1:Author)<-[:CREATOR]-(ar1:Article)-[:PART_OF]->()-[:SERIES]->(s1),
(au2:Author)<-[:CREATOR]-(ar2:Article)-[:PART_OF]->()-[:SERIES]->(s2)
WHERE s1.name='WWW' AND s2.name='Pods' AND ar2.year >2010.0 AND ar1.year >2010.0
WITH au1,au2,ar1,ar2,s1,s2
MATCH (au1)-[rels:CONTRIBUTOR*]->(au2:Author)
WHERE REDUCE (weight = 0, edge IN rels | weight + 1/edge.fdegree) < 5.0
RETURN au1,au2,ar1,ar2,s1,s2,
REDUCE (weight = 1000000, edge IN rels |
case when weight < edge.fdegree then weight else edge.fdegree end) as min_degree

Resources