Cypher WHERE NOT EXISTS with named variable - neo4j

In a Neo4J database, I need to find a (complex) pattern with 3 named node (let's say a, b, c) and some other non-named nodes, but only if there is no node (say "x") that connects to a, b, and c.
I'd like to write something like :
MATCH (a:A)-<something>-(b:B)-<something>-(c:C)
WHERE NOT EXISTS ((a)--(x:X)--(b), (x)--(c) )
RETURN a, b, c
But I get "Variable x not defined". It would be easy if x was only forbidden if connected to (a) and (b). And NOT EXISTS (a)--(:X)--(b) AND NOT EXISTS (a)--(:X)--(c) is too strong.
Any ideas?

You should try MATCHing x:X and using it the WHERE:
MATCH (x:X), (a:A)-<something>-(b:B)-<something>-(c:C)
WHERE NOT EXISTS ((a)--(x)--(b), (x)--(c) )
RETURN a, b, c

Related

Neo4j - Find missing node to complete circle

I am trying to get a query that starting from a node, it returns the missing node that, when making a new relation to it, would complete a circle. Also it should respond which is the node that, if the circle is close, will end up having a relationship with the input node. Example:
Let's say I have B -> C and C -> A. In this case, if I pass A as input, I would like to receive { newRelationToMake: B, relationToInputNode: C } as a result, since connecting A -> B will result in a closed circle ABC and the relation that the node A will be having will come from C.
Ideally, this query should work for a maximum of n depths. For example for a depth of 4, with relations B -> C, C -> D and D -> A, and I pass A as input, I would need to receive { newRelationToMake: C, relationToInputNode: D} (since if I connect A -> C I close the ACD circle) but also receive {newRelationToMake: B, relationToInputNode: D }(since if I connect A -> B I would close the ABCD circle).
Is there any query to get this information?
Thanks in advance!
You are basically asking for all distinct nodes on paths leading to A, but which are not directly connected to A.
Here is one approach (assuming the nodes all have a Foo label and the relationships all have the BAR type):
MATCH (f:Foo)-[:BAR*2..]->(a:Foo)
WHERE a.id = 'A' AND NOT EXISTS((f)-[:BAR]->(a))
RETURN DISTINCT f AS missingNodes
The variable-length relationship pattern [:BAR*2..] looks for all paths of length 2 or more.

How to avoid cycle in neo4j cypher queries

I have friend-friend data model which has two relationships between any two friend nodes based on how one friend defines the other friend.
For example, User "A" can define user "B" as 'FRIEND' and "B" can define "A" as 'BUDDY'.
The problems is, when I try to get the 3rd degree of relationship of user "A", it returns user "B", where as the actual result should be "D" only.
MATCH(a:Users {first_name : "A"}) -[:BUDDY|FRIEND*3] -> (b)
RETURN a,b
OR
MATCH (a)-[]-(b)-[]-(c)-[]-(d)
WHERE a.first_name="A"
RETURN a,d
Alternatively, you can do this:
MATCH p=((a:Users {first_name : "A"})-[:BUDDY|FRIEND*3]->(b))
WITH DISTINCT a, b, nodes(p) as nodes
UNWIND nodes AS node
WITH a, b, nodes, COLLECT(DISTINCT node) as distinct_nodes
WITH a, b WHERE SIZE(nodes)=SIZE(distinct_nodes)
RETURN a, b
or a bit easier with an APOC call:
MATCH p=((a:Users {first_name : "A"})-[:BUDDY|FRIEND*3]->(b))
WITH DISTINCT a, b WHERE SIZE(nodes(p)) = SIZE(apoc.coll.toSet(nodes(p)))
RETURN a, b
I'd suggest the APOC Path Expander procedures which use a means of expansion that only ever consider a single path to a node, allow for specification of the max and min depth, take relationship filters, and set whether visiting a node more than once is permitted. Specifically, the apoc.path.expandConfig() procedure should meet your needs.
MATCH (a:Users {first_name: "A"})
CALL apoc.path.expandConfig(a, {relationshipFilter:"BUDDY|FRIEND",minLevel:3,maxLevel:3, bfs:true,uniqueness:"NODE_GLOBAL"}) YIELD path
RETURN a, path
The uniqueness:"NODE_GLOBAL" parameter makes sure no node is visited more than once.

Filtering out nodes on two cypher paths

I have a simplified Neo4j graph (old version 2.x) as the image with 'defines' and 'same' edges. Assume the number on the define edge is a property on the edge
The queries I would like to run are:
1) Find nodes defined by both A and B -- Requried result: C, C, D
START A=node(885), B=node(996) MATCH (A-[:define]->(x)<-[:define]-B) RETURN DISTINCT x
Above works and returns C and D. But I want C twice since its defined twice. But without the distinct on x, it returns all the paths from A to B.
2)Find nodes that are NOT (defined by both A,B OR are defined by both A,B but connected via a same edge) -- Required result: G
Something like:
R1: MATCH (A-[:define]->(x)<-[:define]-B) RETURN DISTINCT x
R2: MATCH (A-[:define]->(e)-(:similar)-(f)<-[:define]-B) RETURN e,f
(Nodes defined by A - (R1+R2) )
3) Find 'middle' nodes that do not have matching calls from both A and B --Required result: C,G
I want to output C due to the 1 define(either 45/46) that does not have a matching define from B.
Also output G because there's no define to G from B.
Appreciate any help on this!
Your syntax is a bit strange to me, so I'm going to assume you're using an older version of Neo4j. We should be able to use the same approaches, though.
For #1, Your proposed match without distinct really should be working. The only thing I can see is adding missing parenthesis around A and B node variables.
START A=node(885), B=node(996)
MATCH (A)-[:define]->(x)<-[:define]-(B)
RETURN x
Also, I'm not sure what you mean by "returns all paths from A to B." Can you clarify that, and provide an example of the output?
As for #2, we'll need several several parts to this query, separating them with WITH accordingly.
START A=node(885), B=node(996)
MATCH (A)-[:define]->(x)<-[:define]-(B)
WITH A, B, COLLECT(DISTINCT x) as exceptions
OPTIONAL MATCH (A)-[:define]->(x)-[:same]-(y)<-[:define]-(B)
WHERE x NOT IN exceptions AND y NOT IN exceptions
WITH A, B, exceptions + COLLECT(DISTINCT x) + COLLECT(DISTINCT y) as allExceptions
MATCH (aNode)
WHERE aNode NOT IN allExceptions AND aNode <> A AND aNode <> B
RETURN aNode
Also, you should really be using labels on your nodes. The final match will match all nodes in your graph and will have to filter down otherwise.
EDIT
Regarding your #3 requirement, the SIZE() function will be very helpful here, as you can get the size of a pattern match, and it will tell you the number of occurrences of that pattern.
The approach on this query is to first get the collection of nodes defined by A or B, then filter down to the nodes where the number of :defines relationships from A are not equal to the number of :defines relationships from B.
While we would like to use something like a UNION WITH in order to get the union of nodes defined by A and union it with the nodes defined by B, Neo4j's UNION support is weak right now, as it doesn't let you do any additional operations after the UNION happens, so instead we have to resort to adding both sets of nodes into the same collection then unwinding them back into rows.
START A=node(885), B=node(996)
MATCH (A)-[:define]->(x)
WITH A, B, COLLECT(x) as middleNodes
MATCH (B)-[:define]->(x)
WITH A, B, middleNodes + COLLECT(x) as allMiddles
UNWIND allMiddles as middle
WITH DISTINCT A, B, middle
WHERE SIZE((A)-[:define]->(middle)) <> SIZE((B)-[:define]->(middle))
RETURN middle

How to move the properties and relationships of one node to another?

I have two separate nodes in the graph, and at one point I will get a signal that those two nodes should actually be one. So I have to merge the two including their properties (there's no overlap) AND I should maintain the relationships as well.
Example graph: (d)->(a {id:1}), (b)->(c {name:"Sam"})
Desired result: (d)->(a {id:1, name:"Sam"}), (b)->(a {id:1, name:"Sam"})
The label a doesn't have to be the case really in the result - the point is we will have only one node representing the original two.
The following merges the properties fine.
MATCH (a:Entity {id:"1"}), (c:Entity {name:"Sam"})
SET a += c
But I can't seem to find a way to move/copy the relationships.
The specs:
The two nodes won't have the same properties
node a may have incoming and outgoing relationships
node c may have incoming relationships, non outgoing
Any thoughts?
Update:
The following works, assuming the type and properties coming to node c are known in advance. Can I improve this to make it more dynamic?
MATCH (a:Entity {id1:"1"}), (c:Entity {id2:"2"})
SET a += c
WITH a, c
MATCH (c)<-[r]-(b)
WITH a, c, r, b
MERGE (b)-[:REL_NAME {prop1:r.prop1, prop2:r.prop2}]->(a)
WITH c
DETACH DELETE c
Update 2:
The above throws unable to load relationship with id error sometimes and I'm not sure why, but it seems it's related to reading/writing at the same time.
Update 3:
This is a work around for the error:
MATCH (a:Entity {id1:"1"}), (c:Entity {id2:"2"})
SET a += c
WITH a, c
MATCH (c)<-[r]-(b)
WITH a, c, r, b
MERGE (b)-[r2:REL_NAME]->(a)
SET r2 += r
WITH r, c
DELETE r, c
The problem when moving relationships between nodes is that you can't set the relationship type dynamically.
So you can't MATCH all relationships from node c and recreate them on node a. This does not work:
MATCH (a:Entity {id1:"1"}), (c:Entity {id2:"2"})
WITH a, c
// get rels from node c
MATCH (c)-[r]-(b)
WITH a, c, r, b
// create the same rel from node a
MERGE (b)-["r.rel_type" {prop1:r.prop1, prop2:r.prop2}]->(a)
With neo4j 3 there are so called procedures which are plugins for the server and can be called from Cypher queries. The apoc procedure package provides procedures that do what you need: https://neo4j-contrib.github.io/neo4j-apoc-procedures/
First install the apoc plugin on your server, then use something like:
// create relationships with dynamic types
CALL apoc.create.relationship(person1,'KNOWS',{key:value,…​}, person2)
// merge nodes
CALL apoc.refactor.mergeNodes([node1,node2])

Get middle node between two node neo4j

I need to find common node between two nodes. For example find B from A -> B -> C
A = node 1
B = node 2
C = node 3
A, B and C have common property (user_id, fullname) and relation property is KNOWS. node index is user_id.
Node related to:
A [:KNOWS] B and B [:KNOWS] C
I have A and C node id. I want find B node id. How can I do this using Cypher or neo4jphp?
Would really prefer to see something that you had written yourself, but I suppose sometimes that's just too much effort...
START a=node(1)
MATCH (a)-[:KNOWS]->(b)-[:KNOWS]->(c)
WHERE a.user_id = ... (Explanation on what exactly should be done here was lacking)
RETURN b

Resources