Neo4j Single Query to Refactor Relationships To and From - neo4j

I have 2 nodes where I want to move all the TO and FROM relationships to the second node.
//This works FROM
MATCH (c)<-[r:HAS]-(f {name: 'ball(a)'}), (t {name: 'ball(b)'})
WITH c, r, f, t
CALL apoc.refactor.from(r, t)
YIELD input, output
Return c
//AND this works TO
MATCH (c)-[r:HAS]->(f {name: 'ball(a)'}), (t {name: 'ball(b)'})
WITH c, r, f, t
CALL apoc.refactor.to(r, t)
YIELD input, output
Return c
//THIS DOES NOT WORK
MATCH (c)-[r:HAS]->(f {name: 'ball(a)'})-[r2:HAS]->(d), (t {name: 'ball(b)'})
WITH c, r, f, t, r2, d
CALL apoc.refactor.to(r, t)
CALL apoc.refactor.from(r2, t)
YIELD input, output
Return c
I get the ERROR: "Procedure call inside a query does not support naming results implicitly"
How can I refactor TO and FROM relations in one query?

Please try this:
MATCH (c)-[r:HAS]->(f {name: 'ball(a)'})-[r2:HAS]->(d), (t {name: 'ball(b)'})
WITH c, r, t, r2, d
CALL apoc.refactor.to(r, t)
YIELD input, output
WITH c, r2, t
CALL apoc.refactor.from(r2, t)
YIELD input, output
Return c
Line number 3 is a function that takes an input r, t and as a result returns input, output. Inside this return you do not have r2 and t, that's why you're receiving an error.

Related

generate subgraphs from special match rules

Say I have the following graph:
(a1:A) -> (b1:B) -> (c1:C) -> (d1:D)
\ /
- -> (x1:X) - --> (y1:Y)
(a2:A) -> (b2:B) -> (c2:C) -> (d2:D)
(a3:A) -> (x3:X) -> (y3:Y) -> (d3:D)
The actual graph also contains other relationships between node label A and D. But I am only interested in these relationships between A and D. So I have to force some rules on the path. see query below
match p1=((:A)-->(:B)-->(:C)->(:D))
return p1
match p2=((:A)-->(:X)-->(:Y)->(:D))
return p2
This will return me four rows
a1-b1-c1-d1
a1-x1-y1-d1
a2-b2-c2-d2
a3-x3-y3-d3
But I would like to return an array of subgraphs and merge paths based on the common node attribute D.name. I.e., d1, d2, d3 are differentiated by their node attribute called "name". So the output I would like to have is
// query logic
// return subgraphs, each subgraph is a row. each subgraph contains the unique node and relationships from A to D. If there are multiple paths to the same D, then merge these paths to the same subgraph
More concretely, the return of the above example becomes
row1: nodes(a1, b1, c1, d1, x1, y1), relationships(a1, b1, c1, d1, x1, y1)
row2: nodes(a2, b2, c2, d2), relationships(a2, b2, c2, d2)
row3: nodes(a3, x3, y3, d3), relationships(a3, x3, y3, d3)
What would be the query?
UPDATE:
Test graph
merge (a1:A{name: 'a1'})
merge (b1:B{name: 'b1'})
merge (c1:C{name: 'c1'})
merge (d1:D{name: 'd1'})
merge (x1:X{name: 'x1'})
merge (y1:Y{name: 'y1'})
merge (a2:A{name: 'a2'})
merge (b2:B{name: 'b2'})
merge (c2:C{name: 'c2'})
merge (d2:D{name: 'd2'})
merge (a3:A{name: 'a3'})
merge (x3:X{name: 'x3'})
merge (y3:Y{name: 'y3'})
merge (d3:D{name: 'd3'})
merge(a1)-[:TESTS]->(b1)
merge(b1)-[:TESTS]->(c1)
merge(c1)-[:TESTS]->(d1)
merge(a1)-[:TESTS]->(x1)
merge(x1)-[:TESTS]->(y1)
merge(y1)-[:TESTS]->(d1)
merge(a2)-[:TESTS]->(b2)
merge(b2)-[:TESTS]->(c2)
merge(c2)-[:TESTS]->(d2)
merge(a3)-[:TESTS]->(x3)
merge(x3)-[:TESTS]->(y3)
merge(y3)-[:TESTS]->(d3)
It seems that not just the :D nodes are different, but also the :A nodes. But, assuming that you want group by d.name, I guess this does it.
MATCH p=((:A)-[*]->(d:D))
RETURN d.name AS dName,
apoc.coll.toSet(
apoc.coll.flatten(
COLLECT(nodes(p))
)
) AS nodes,
apoc.coll.toSet(
apoc.coll.flatten(
COLLECT(relationships(p))
)
) AS relationships
In case you want to filter for specific paths, you can do something like:
MATCH (a:A), (d:D)
OPTIONAL MATCH p1=((a)-->(:B)-->(:C)-->(d))
OPTIONAL MATCH p2=((a)-->(:X)-->(:Y)-->(d))
WITH d.name AS dName,
apoc.coll.toSet(
apoc.coll.flatten(
COLLECT(COALESCE(nodes(p1),[]) + COALESCE(nodes(p2),[]))
)
) AS nodes,
apoc.coll.toSet(
apoc.coll.flatten(
COLLECT(COALESCE(relationships(p1),[]) + COALESCE(relationships(p2),[]))
)
) AS relationships
WHERE nodes <> []
RETURN nodes, relationships

Cypher variable lenght pattern aggregated property filtering

I'm working on neo4j and I'm trying to put a condition on a series of relationships, where I need to sum a property of said relationship.
When I do the basic textbook part of filtering for the relationship without the sum, it all works.
MATCH c= (a)-[:VENDE*2..3]->(b)
WHERE ALL (r IN relationships(c)
WHERE r.ammontare = 25000)
RETURN c
When I try to put the condition on the sum of said property, I can't find a way.
I tried REDUCE but it's stuck because the this error I didn't manage to work around: "Type mismatch: accumulator is Integer but expression has type Boolean"
MATCH c= (a)-[:VENDE*2..3]->(b)
WHERE ALL (r IN relationships(c)
WHERE REDUCE( t = 0, n in relationships(c)|t= t+ n.ammontare) = 25000)
RETURN c
I tried with apoc but this doesn't work either. What am I getting wrong?
MATCH c= (a)-[:VENDE*2..3]->(b)
WHERE all(r in relationships(r)
WHERE APOC.COLL.SUM( [r IN relationships(c)| r.ammontare]) = 25000)
return c
The last method runs but it's too heavy and goes into timeout.
MATCH c= (a)-[:VENDE*2]->(b)
WITH c, relationships(c) as rels
UNWIND (rels) as rel
With c, sum(rel.ammontare) as re
where re > 25000
return c
I'm not sure but this will work.
MATCH c= (a)-[:VENDE*2..3]->(b)
WHERE ALL (r IN relationships(c)
WHERE REDUCE( t = 0, n in relationships(c)| t+ n.ammontare) = 25000)
RETURN c

combine several apoc.path.expand calls into one

I have a several cypher calls that returns the count of the nodes from a specific type (A, B or C) that are connect to my node
MATCH (n {{ID:"{id}"}})
call apoc.path.expand(n, "<", ">A", 1, 10) yield path as p
return count(distinct nodes(p)[-1])
MATCH (n {{ID:"{id}"}})
call apoc.path.expand(n, "<", ">B", 1, 10) yield path as p
return count(distinct nodes(p)[-1])
MATCH (n {{ID:"{id}"}})
call apoc.path.expand(n, "<", ">C", 1, 10) yield path as p
return count(distinct nodes(p)[-1])
Making these calls 3 time is wasteful, and I wonder if I can combine them into one, but still get the distinct count for all three
Does something like this get it done for you?
MATCH (n {{ID:"{id}"}})
CALL apoc.path.expand(n, "<", ">A,>B,>C", 1, 10) yield path as p
RETURN labels(last(nodes(p)))[0] AS label, count(distinct nodes(p)[-1])

Sum up counts in Neo4j

I have directors who have directed films. These films have genres and some actors starring. I want to find the films by a directed sorted by the sum of (no of genres of the film, no of actors starring in the film).
MATCH(n) -- (f:Film)
WHERE n.name = "Steven Spielberg"
MATCH (f) - [r] -> (g:Genre)
OPTIONAL MATCH (f) - [r2] -> (s:Starring)
WITH n, f, count(r) as gc, count(r2) as sc
RETURN n, f, gc, sc
ORDER BY gc DESC
This works but now I want to sum gc and sc and order films by the result. How does one do that?
I think you can just add the sum you want in your RETURN statement and then order results by it:
MATCH(n) -- (f:Film)
WHERE n.name = "Steven Spielberg"
MATCH (f) - [r] -> (g:Genre)
OPTIONAL MATCH (f) - [r2] -> (s:Starring)
WITH n, f, count(r) as gc, count(r2) as sc
RETURN n, f, gc, sc, gc+sc AS S
ORDER BY S DESC

Scala parser combinators for (almost) trivial grammar

I've been trying to make a parser for a (very) simple language that looks like this:
block{you are a cow too blkA{ but maybe not} and so is he} hear me moo blockZ{moooooo}
I can break it apart using regexes:
.*?[^ ]*?\\{
.*?\\}
which would essentially keep eating characters until it found something that matches [^ ]*?\\{ or \\}: the start or end of a block. My question is, if I want to do it using Scala's Parser Combinators, how do I do that? I currently have:
def expr: Parser[Any] = (block | text)+
def text = ".+?".r
def block = "[^ ]*?\\{".r ~ expr ~ "}"
but this doesn't work:
parsed: List(b, l, o, c, k, {, y, o, u, a, r, e, a, c, o, w, t, o, o, b, l, k, A, {, b, u, t, m, a, y, b, e, n, o, t, }, a, n, d, s, o, i, s, h, e, }, h, e, a, r, m, e, m, o, o)
It seems that the block parser is not firing, and so the text parser is being fired repeatedly. but when i remove the text parser:
def expr: Parser[Any] = (block)+
I get:
failure: string matching regex `[^ ]*?\{' expected but `y' found
block{you are a cow too blkA{ but maybe not} and so is he} hear me moo
^
So obviously the block parser does work, except not when the text parser is present. What's happening? and is there a "proper" way of doing this, for so basic a grammar?
EDIT: Changed the title, since it's not so much about the reluctance anymore as just solving the problem
EDIT: I now have this:
def expr: Parser[Any] = (block | text)+
def text = "[^\\}]".r
def block = "[^ ]*?\\{".r ~ expr ~ "}"
The logic behind this is that for each character, it tests whether or not it is the start of a block. If it isn't, it moves on to the next character. This gives me:
parsed: List(((block{~List(y, o, u, a, r, e, a, c, o, w, t, o, o, ((blkA{~List(b, u, t, m, a, y, b, e, n, o, t))~}), a, n, d, s, o, i, s, h, e))~}), h, e, a, r, m, e, m, o, o)
which is kind of correct. It is parsing the non-block characters one-by-one though, which is probably a performance problem (i think?). Is there any way to parse all those non-block characters at once and leave them in one big string?
The problem is that text is consuming all closing curly braces (}). It goes like this:
expr -> block -> expr -> text.+ (until all input is consumed)
At this point, it exits expr and tries to parse }, which does not exists, fails, and falls back to text on the first expr.
You can use log to see what's going on when you parse.

Resources