Neo4J Matching Nodes Based On Relationships - neo4j

I have the following statement:
MATCH path=(p:Person {person_id: '123'})-[:ASSOCIATED_WITH]-(:Person)
where exists((p)-[:BELONGS]-(:Face)-[:CORRESPONDS]-(:Image)-[:HAS_ACCESS_TO]-(:Dias {group_name: 'group'}))
RETURN path
It returns 3 nodes, 2 relationships. This is what I would like to happen but the group_name I will actually be passing is an array. How do I add the condition to check for the value in the array while still maintaining 3 nodes and two relationships? The only relationship I want to return back is ASSOCIATED_WITH. The 3 nodes being, the main person_id of "123" and the two associated persons.

I will assume you pass the list of group names in a names parameter.
If you want to get the paths in which the EXISTS test succeeds for ANY name in the list:
MATCH (p:Person {person_id: '123'})
WHERE ANY(x IN $names WHERE
EXISTS((p)-[:BELONGS]-(:Face)-[:CORRESPONDS]-(:Image)-[:HAS_ACCESS_TO]-(:Dias {group_name: x})))
MATCH path=(p)-[:ASSOCIATED_WITH]-(:Person)
RETURN path
Or, if you want to get the paths in which the EXISTS test succeeds for ALL names in the list, just replace ANY in the above request with ALL:
MATCH (p:Person {person_id: '123'})
WHERE ALL(x IN $names WHERE
EXISTS((p)-[:BELONGS]-(:Face)-[:CORRESPONDS]-(:Image)-[:HAS_ACCESS_TO]-(:Dias {group_name: x})))
MATCH path=(p)-[:ASSOCIATED_WITH]-(:Person)
RETURN path

Related

Neo4j - Exclude node from results where it has a specific relationship

I'm attempting to a set of nodes (p) where they have a relationship [:INCLUDE] to a specific node (ca) identified by its ID, but I also want to make sure I exclude any (p) node that also has an [:EXCLUDE] relationship to any other (ca) node.
I've tried the below...
MATCH (a:CloudApp)-[]-(p:Policy{state: "enabled"})
WHERE (a{id:"All"})-[]-(p) OR (a{id:"b9a97804-0c6b-4d83-8b35-84bda7f8b69c"})-[]-(p)
WITH p,a
MATCH (p)-[]-(pl:Platform {id: "macOS"})
WHERE NOT (p)-[:EXCLUDE_Platform]-(pl)
WITH p,a,pl
RETURN *
Which gets me this...
And then tried to filter it with this...
MATCH (a:CloudApp)-[]-(p:Policy{state: "enabled"})
WHERE (a{id:"All"})-[]-(p) OR (a{id:"b9a97804-0c6b-4d83-8b35-84bda7f8b69c"})-[]-(p)
WITH p,a
MATCH (p)-[]-(pl:Platform {id: "macOS"})
WHERE NOT (p)-[:EXCLUDE_Platform]-(pl) AND NOT (p)-[:EXCLUDE_CLOUDAPP]-(a)
WITH p,a,pl
RETURN *
But this results in the same 3 (p) nodes and just excludes the (a) node where that relationship exists. I've tried a few variations on the above query and always seem to get the same result...
I'm guessing that this is because it just excludes that relationship and the node remains because it has another valid relationship. I'm just not sure how to achieve what I want?
Just remove the variable a from the condition NOT (p)-[:EXCLUDE_CLOUDAPP]-(a) and add a node label, and try this:
MATCH (a:CloudApp)-[]-(p:Policy{state: "enabled"})
WHERE (a{id:"All"})-[]-(p) OR (a{id:"b9a97804-0c6b-4d83-8b35-84bda7f8b69c"})-[]-(p)
WITH p,a
MATCH (p)-[]-(pl:Platform {id: "macOS"})
WHERE NOT (p)-[:EXCLUDE_Platform]-(pl) AND NOT (p)-[:EXCLUDE_CLOUDAPP]-(:CloudApp)
WITH p,a,pl
RETURN *
This basically checks whether the policy node is not linked to any CloudApp node via exclude relationship.
So the way I solved it was this
MATCH (a:CloudApp)-[]-(p:Policy{state: "enabled"})
WHERE (p)-[:EXCLUDE_CLOUDAPP]-(a)
WITH COLLECT(p) as excluded
MATCH (a:CloudApp)-[]-(p:Policy{state: "enabled"})
WHERE (a{id:"All"})-[]-(p:Policy{state: "enabled"})
WITH p,a,excluded
MATCH (p)-[]-(pl:Platform {id: "macOS"})
WHERE NOT (p)-[:EXCLUDE_Platform]-(pl)
WITH COLLECT(p) as included, excluded
RETURN [n IN included WHERE not n IN excluded] as list
I found the nodes I didn't want, then the ones I did want and removed the excluded ones from those using the WHERE not n IN part

Return non-matching nodes while creating relationships Neo4j Cypher

I wrote a script to to batch create a bunch of relationship in neo4j. Here is the cypher:
:param batch => [{startId: 'abc123', endId: 'abc321'}, {startId: 'abc456', endId: 'abc654']
UNWIND $batch as row
MATCH (from {id: row.startId}
MATCH (to {id: row.endId}
CREATE (from)-[rel:HAS]->(to)
RETURN rel
The problem that there might be some startId/endId entries that don't match any nodes and are silently ignore. Is there a way to return the list of rows that don't match any nodes and create the relationship for the nodes that do match?
I tried OPTIONAL MATCH to fail-fast as soon an id doesn't find a startId/endId however, the query execution was really slow.
First of all, you should always try to specify a label for the node that is used to kick off a MATCH (unless the MATCH pattern uses any already-bound nodes). Otherwise, every single node in the DB must be scanned. In addition, you should consider using indexes to speed up your MATCHs (but, again, you'd need to specify the labels).
Here is a query that uses the APOC procedure apoc.do.when to create a new relationship when appropriate. It returns each row and the corresponding new relationship (or NULL if either node is not found):
UNWIND $batch as row
OPTIONAL MATCH (from:Foo {id: row.startId})
OPTIONAL MATCH (to:Foo {id: row.endId})
CALL apoc.do.when(
from IS NOT NULL AND to IS NOT NULL,
'CREATE (from)-[rel:HAS]->(to) RETURN rel',
'RETURN NULL AS rel',
{from: from, to: to}) YIELD value
RETURN row, value.rel AS rel

Duplicate relationship in Neo4j

Why does this create two relationships instead of one?
MATCH (a:Person{name:'Barack'}), (b:Person{name:'Raback'})
CREATE (a)-[r:SHAKES_HANDS_WITH{id:toString(rand())}]->(b)
RETURN r
(Random number "id" is just added for demo purposes.)
You probably have 2 Person nodes with the same name (either 'Barack' or 'Raback').
Assuming that the other name has only a single node, the MATCH clause will produce 2 rows -- which will cause the the CREATE clause to be executed twice.
To verify if this is your scenario, this query will show you how many nodes have each name:
MATCH (a:Person)
WHERE a.name IN ['Barack', 'Raback']
RETURN a.name, COUNT(a) as nodeCount

Return a graph based on the number of relationships between nodes

I am experimenting with creating multiple relationships between nodes to represent the importance between two given nodes.
For example, I want to know what 'genre' of reading material is most important to Joe.
I want a way to match the Joe node to genre nodes only if there is some number or greater relationships between them.
So, if I want matches with 3 or more relationships, I should get a graph with Joe --> Fantasy
I know I can get this when both endpoints are defined:
MATCH (p:PERSON {name:'Joe'})-[r]->(g: GENRE {name:'Fantasy'})
RETURN count(r)
What I want is something like:
MATCH p = (p:PERSON {name:'Joe'})-[r]->()
WHERE *pair_relationship_count*(r) >= 3
RETURN p
This is my proposition:
MATCH path = (p:PERSON {name:'Joe'})-[r]->()
WITH collect(path) as paths, collect(r) as pair_count
WITH paths WHERE size(pair_count) >= 3
UNWIND paths as path
RETURN path
But maybe it is more efficient to have one relationship with an internal count property on one relationship for each couple of nodes.
First, I think you can achieve your goal using WITH clause:
MATCH path = (:PERSON {name:'Joe'})-[r]->(:GENRE {name:'Fantasy'})
WITH path, count(r) as count
WHERE count > 3
RETURN path
But using one relationship for each read "event" seems to be a bad approach. Maybe you should use an integer property in the relationship, then increment this property for each "read". This way you can do queries like:
MATCH path = (:PERSON {name:'Joe'})-[r]->(:GENRE {name:'Fantasy'})
WHERE r.count > 3
RETURN path
To get a collection of all READS paths for "Joe" that involve each genre that he has read at least 3 times:
MATCH p = (:PERSON {name:'Joe'})-[:READS]->(genre)
WITH genre, COLLECT(p) AS paths
WHERE SIZE(paths) >= 3
RETURN genre, paths;

How to find out connection between a set of nodes?

I have a scenario where I know IDs of a list of nodes.
I need to get connection(if exists) between these nodes given their IDs.
Is there any way to achieve this?
Update:
I am using node id property not the neo4j's internal ID(using like match (n:Person{id:3}))
You can use the IN clause to select from a list of values:
MATCH (n)-[r*..2]-(m)
WHERE ID(n) IN [0,1,2] AND ID(m) IN [2,3,4]
RETURN r
I've limited the path length to 2 hops of indeterminate relationship type here, and arbitrarily picked some IDs.
To return the path instead:
MATCH p=(n)-[r*..2]-(m)
WHERE ID(n) IN [0,1,2] AND ID(m) IN [2,3,4]
RETURN p
START n=node(1,2,3,4,5,6) //your IDs of a list of nodes
MATCH p=n-[r]-m //the connection for 1 hop. for multiple hops do n-[r*]-m
WHERE Id(m) in [1,2,3,4,5,6] //your IDs of a list of nodes
RETURN p

Resources