Neo4j IN clause case-insensitive - neo4j

I created a node in neo4j db with this command:
CREATE (n:Person {Names: 'A', 'B', 'C'}) RETURN n;
CREATE (n:Person {Names: 'D'}) RETURN n;
Now, I want to make a query to retrieve a person has one of the name in a specific list.
I have this list : ['a', 'c'] -> It will return the 1st person
I know I have to use IN clause for this query, but I don't know how to achieve it.
I would like to write the command in Neo4j DB Browser and Neo4j Client .Net.
Can anyone help me please ?
Thank you.

1) You have an incorrect query to add nodes. It should be like this:
CREATE (n:Person {Names: ['A', 'B', 'C']}) RETURN n;
CREATE (n:Person {Names: ['D']}) RETURN n;
2) To search for occurrences of at least one value from the list, you can use the predicate ANY:
WITH ['a', 'D'] as Names
MATCH (P:Person)
WITH P, extract(name IN P.Names | LOWER(name)) as lowNames
WHERE ANY(n IN Names WHERE LOWER(n) IN lowNames)
RETURN P
Update. It is not excluded that it is better way in the spirit of "graph" is change the model. If we assume that the names can be repeated with different persons, then for each name it is necessary to add a node, and add a relationship with the person:
Create query:
MERGE (P1:Person {id:'P1'})
MERGE (P2:Person {id:'P2'})
MERGE (P3:Person {id:'P3'})
MERGE (N1:Name {name:'A'})
MERGE (N2:Name {name:'B'})
MERGE (N3:Name {name:'C'})
MERGE (N4:Name {name:'D'})
MERGE (P1)-[:has_name]->(N1)
MERGE (P1)-[:has_name]->(N2)
MERGE (P1)-[:has_name]->(N3)
MERGE (P2)-[:has_name]->(N4)
MERGE (P3)-[:has_name]->(N1)
MERGE (P1)-[:has_name]->(N4)`
And a query to search persons:
// Find the desired names
WITH ['a', 'D'] as Names
WITH extract(name IN Names | LOWER(name)) as lowerNames
MATCH (N:Name) WHERE LOWER(N.name) IN lowerNames
// For each name, we find person
WITH N
MATCH (P:Person)-[:has_name]->(N)
RETURN collect(distinct P)

Related

Match paths of node types where nodes may have cycles

I'm trying to find a match pattern to match paths of certain node types. I don't care about the type of relation. Any relation type may match. I only care about the node types.
Of course the following would work:
MATCH (n)-->(:a)-->(:b)-->(:c) WHERE id(n) = 0
But, some of these paths may have relations to themselves. This could be for :b, so I'd also like to match:
MATCH (n)-->(:a)-->(:b)-->(:b)-->(:c) WHERE id(n) = 0
And:
MATCH (n)-->(:a)-->(:b)-->(:b)-->(:b)-->(:c) WHERE id(n) = 0
I can do this with relations easily enough, but I can't figure out how to do this with nodes, something like:
MATCH (n)-->(:a)-->(:b*1..)-->(:c) WHERE id(n) = 0
As a practical example, let's say I have a database with people, cars and bikes. The cars and bikes are "owned" by people, and people have relationships like son, daughter, husband, wife, etc. What I'm looking for is a query that from a specific node, gets all nodes of related types. So:
MATCH (n)-->(:person*1..)-->(:car) WHERE Id(n) = 0
I would expect that to get node "n", it's parents, grandparents, children, grandchildren, all recursively. And then of those people, their cars. If I could assume that I know the full list of relations, and that they only apply to people, I could get this to work as follows:
MATCH
p = (n)-->(:person)-[:son|daughter|husband|wife|etc*0..]->(:person)-->(:car)
WHERE Id(n) = 0
RETURN nodes(p)
What I'm looking for is the same without having to specify the full list of relations; but just the node label.
Edit:
If you want to find the path from one Person node to each Car node, using only the node labels, and assuming nodes may create cycles, you can use apoc.path.expandConfig.
For example:
MERGE (mark:Person {name: "Mark"})
MERGE (lju:Person {name: "Lju"})
MERGE (praveena:Person {name: "Praveena"})
MERGE (zhen:Person {name: "Zhen"})
MERGE (martin:Person {name: "Martin"})
MERGE (joe:Person {name: "Joe"})
MERGE (stefan:Person {name: "Stefan"})
MERGE (alicia:Person {name: "Alicia"})
MERGE (markCar:Car {name: "Mark's car"})
MERGE (ljuCar:Car {name: "Lju's car"})
MERGE (praveenaCar:Car {name: "Praveena's car"})
MERGE (zhenCar:Car {name: "Zhen's car"})
MERGE (zhen)-[:CHILD_OF]-(mark)
MERGE (praveena)-[:CHILD_OF]-(martin)
MERGE (praveena)-[:MARRIED_TO]-(joe)
MERGE (zhen)-[:CHILD_OF]-(joe)
MERGE (alicia)-[:CHILD_OF]-(joe)
MERGE (zhen)-[:CHILD_OF]-(mark)
MERGE (anthony)-[:CHILD_OF]-(rik)
MERGE (martin)-[:CHILD_OF]-(mark)
MERGE (stefan)-[:CHILD_OF]-(zhen)
MERGE (lju)-[:CHILD_OF]-(stefan)
MERGE (markCar)-[:OWNED]-(mark)
MERGE (ljuCar)-[:OWNED]-(lju)
MERGE (praveenaCar)-[:OWNED]-(praveena)
MERGE (zhenCar)-[:OWNED]-(zhen)
Running a query:
MATCH (n:Person{name:'Joe'})
CALL apoc.path.expandConfig(n, {labelFilter: "Person|/Car", uniqueness: "NODE_GLOBAL"})
YIELD path
RETURN path
will return four unique paths from Joe node to the four car nodes. There are several options for uniqueness of the path, see uniqueness
The /CAR makes it a Termination label, i.e. returned paths are only up to this given label.

Neo4j:Delete all relationships except for those in list

I am trying to delete all relationships to a node except those that are in a list. I have already create a node (:Person {name: 'John'}) and 4 other nodes (:Car). Then I MERGE all the car nodes to the person node. I then want to delete all the relationships for the person node except for those in a list (shown below)
UNWIND [{name:'test1'}, {name:'test2'}] AS test
MATCH (p:Person {name:'John'})
OPTIONAL MATCH (p)-[d:DRIVES]->(c:Car)
WHERE NOT EXISTS((p)-[:DRIVES]->(c:Car {name:test.name}))
DELETE d
RETURN p
However the query above deletes all relationships but when I reduce the list to include only 1 car node, the above query works (i.e. the query only works when the list contains only 1 node and doesn't work when the list is larger). I am not sure why this is the case.
I am using neo4j 4.1.
Thanks in advance.
This should work:
WITH ['test1', 'test2'] AS tests
MATCH (p:Person {name: 'John'})
OPTIONAL MATCH (p)-[d:DRIVES]->(c:Car)
WHERE NOT c.name IN tests
DELETE d
RETURN p
and also this:
WITH ['test1', 'test2'] AS tests
MATCH (p:Person {name: 'John'})
FOREACH(x IN [(p)-[d:DRIVES]->(c:Car) WHERE NOT c.name IN tests | d] | DELETE x)
RETURN p

Cypher Query to return x Number of a particular type of node

Lets say we have a Neo4j graph such as (Brand)-[:from]->(Post)<-[:likes]-(Person).
How can I return a cypher query which will have a minimum number of brand posts, say 3. I want this to be scalable and not dependent on a specific property attribute value.
Hence the results would return at least 3 instances of the Brand nodes, as well as maybe 5 from Post and 15 from Person.
I have tried a few different things:
1.) Declare several variable names for each brand (not scalable)
Match (b:Brand)-[]->(p:Post)<-[]-(per:Person)
Match (b1:Brand)-[]->(p1:Post)<-[]-(per2:Person)
Match (b2:Brand)-[]->(p2:Post)<-[]-(per3:Person)
return b,b1,b2,p,p1,p2,per,per2,per3
limit 30
This didn't work because it essentially return the same as
Match (b:Brand)-[]->(p:Post)<-[]-(per:Person)
return b,p,per
limit 30
2.) Use a foreach some
Match (b:Brand) WITH collect (distinct b) as bb
FOREACH (b in bb[0..3] | MATCH (b)-[]->(p:Post)<-[]-(per:Person))
RETURN b, p, per LIMIT 40
This didn't work because you can't use Match inside a Foreach call.
The only way I know how to do this is to declare a where clause with their unique property brand name values which is not scalable. It looks like this:
Match (b:Brand)-[]->(p:Post)<-[]-(per:Person)
where b.brand = "b1" OR b.brand ="b2" or b.brand = "b3"
Return b,p,per
Limit 30
However the above still doesn't even return what I want.
Please help. Here is a quick graph to test on:
Create (b1:Brand {brand:'b1'})
Create (b2:Brand {brand:'b2'})
Create (b3:Brand {brand:'b3'})
Create (p1:Post {id: "001",message: "foo"})
Create (p2:Post {id: "002",message: "bar"})
Create (p3:Post {id: "003",message: "baz"})
Create (p4:Post {id: "004",message: "raz"})
Create (per1:Person {id: "001",name: "foo"})
Create (per2:Person {id: "002",name: "foo"})
Create (per3:Person {id: "003",name: "foo"})
Create (per4:Person {id: "004",name: "foo"})
Create (per5:Person {id: "005",name: "foo"})
Create (per6:Person {id: "006",name: "foo"})
Create (per7:Person {id: "007",name: "foo"})
Merge (b1)-[:FROM]->(p1)
Merge (b1)-[:FROM]->(p2)
Merge (b2)-[:FROM]->(p3)
Merge (b3)-[:FROM]->(p4)
Merge (per1)-[:LIKES]->(p1)
Merge (per1)-[:LIKES]->(p2)
Merge (per1)-[:LIKES]->(p3)
Merge (per2)-[:LIKES]->(p1)
Merge (per2)-[:LIKES]->(p4)
Merge (per3)-[:LIKES]->(p3)
Merge (per4)-[:LIKES]->(p1)
Merge (per5)-[:LIKES]->(p2)
Merge (per6)-[:LIKES]->(p1)
Merge (per6)-[:LIKES]->(p2)
Merge (per6)-[:LIKES]->(p3)
Merge (per6)-[:LIKES]->(p4)
Merge (per7)-[:LIKES]->(p4)
You can use the unwind instead of foreach:
Match (b:Brand) WITH collect (distinct b) as bb
UNWIND bb[0..3] as b
MATCH (b)-[]->(p:Post)<-[]-(per:Person)
RETURN b, p, per LIMIT 40
Or combine with and limit:
MATCH (b:Brand) WITH distinct b LIMIT 3
MATCH (b)-[]->(p:Post)<-[]-(per:Person)
RETURN b, p, per LIMIT 40

Neo4J/Cypher Filter nodes based on multiple relationships

Using Neo4J and Cypher:
Given the diagram below, I want to be able to start at node 'A' and get all the children that have a 'ChildOf' relationship with 'A', but not an 'InactiveChildOf' relationship. So, in this example, I would get back A, C and G. Also, a node can get a new parent ('H' in the diagram) and if I ask for the children of 'H', I should get B, D and E.
I have tried
match (p:Item{name:'A'}) -[:ChildOf*]-(c:Item) where NOT (p)-[:InactiveChildOf]-(c) return p,c
however, that also returns D and E.
Also tried:
match (p:Item{name:'A'}) -[rels*]-(c:Item) where None (r in rels where type(r) = 'InactiveChildOf') return p,c
But that returns all.
Hopefully, this is easy for Neo4J and I am just missing something obvious. Appreciate the help!
Example data: MERGE (a:Item {name:'A'}) MERGE (b:Item {name:'B'}) MERGE (c:Item {name:'C'}) MERGE (d:Item {name:'D'}) MERGE (e:Item {name:'E'}) MERGE (f:Item {name:'F'}) MERGE (g:Item {name:'G'}) MERGE (h:Item {name:'H'}) MERGE (b)-[:ChildOf]->(a) MERGE (b)- [:InactiveChildOf] ->(a) MERGE (c)-[:ChildOf]->(a) MERGE (d)-[:ChildOf]->(b) MERGE (e)-[:ChildOf]->(b) MERGE (f)-[:ChildOf]->(c) MERGE (f)- [:InactiveChildOf] ->(c) MERGE (g)-[:ChildOf]->(c) MERGE (b)-[:ChildOf]->(h)
Note, I understand that I could simply put an "isActive" property on the ChildOf relationship or remove the relationship, but I am exploring options and trying to understand if this concept would work.
If a query interpreted as: find all the nodes, the path to which passes through the nodes unrelated by InactiveChildOf to the previous node, the request might be something like this:
match path = (p:Item{name:'A'})<-[:ChildOf*]-(c:Item)
with nodes(path) as nds
unwind range(0,size(nds)-2) as i
with nds,
nds[i] as i1,
nds[i+1] as i2
where not (i1)-[:InactiveChildOf]-(i2)
with nds,
count(i1) as test
where test = size(nds)-1
return head(nds),
last(nds)
Update: I think that this version is better (check that between two nodes there is no path that will contain at least one non-active type of relationship):
match path = (p:Item {name:'A'})<-[:ChildOf|InactiveChildOf*]-(c)
with p, c,
collect( filter( r in rels(path)
where type(r) = 'InactiveChildOf'
)
) as test
where all( t in test where size(t) = 0 )
return p, c
By reading and examining the graph, correct me if I'm wrong but the actual text representation of the cypher query should be
Find me nodes in a path to A, all nodes in that path cannot have an outgoing
InactiveChildOf relationship.
So, in Cypher it would be :
MATCH p=(i:Item {name:"A"})<-[:ChildOf*]-(x)
WHERE NONE( x IN nodes(p) WHERE (x)-[:InactiveChildOf]->() )
UNWIND nodes(p) AS n
RETURN distinct n
Which returns

Neo4j Cypher : How to set StartNode or endNode of a relationship?

Let's say we have two nodes n and m
Is it possible to set m as the startNode for all Relationship with n as the StartNode n-[r]->()
The relationships can have different types.
Is it possible using only one cypher request?
No, you can't re-assign the start node for a certain relationship. What you can do is delete that relationship, and then create new ones that point where you want them to go.
For example:
MATCH (n { id: "startpoint"})-[r]->(), (m {id: "endpoint"})
MERGE (n)-[:newRelationship]->(m)
DELETE r;
This query would have to get much more complicated if the type of :newRelationship could change depending on r
Example Data:
CREATE CONSTRAINT ON (city:City) ASSERT city.name IS UNIQUE;
CREATE CONSTRAINT ON (state:State) ASSERT state.name IS UNIQUE;
MERGE (pb:City {name: 'Paderborn'})
MERGE (state1:State {name: 'Bavaria'})
MERGE (state2:State {name: 'North Rhine-Westphalia'})
MERGE (pb)-[:LOCATED_IN]->(state1);
The following statement will remove the existing relationship and create a new one:
MATCH (n { name: "Paderborn"})-[r]->(), (state {name: "Bavaria"})
MERGE (n)-[:LOCATED_IN]->(state)
DELETE r;

Resources