Neo4j - persons with mutual friends only - neo4j

In Neo4j using Cypher:
I need to find PersonB that has exact same friends as PersonA.
Example:
Paul knows Peter, Ana, and Mike and nobody else.
Find who else knows Peter, Ana, and Mike and nobody else.
Thanks in advance.

Here is the breakdown of how to do a query like this.
// Match side a
MATCH (n1:Person)--(m:Person)
WITH n1, COLLECT(m) as friends
// Match side b
MATCH (n2:Person)--(m:Person)
WITH n1, n2, friends as friends1, COLLECT(m) as friends2
// Filter out a-b same node or non identical friends lists.
WHERE n1<>n2 AND SIZE(friends1) = SIZE (friends2) AND ALL (f in friends1 WHERE f in friends2)
RETURN n1, n2

Related

Separating matching nodes in a query result

I defined the directed relation Know on person nodes. For example, if Sara knows Alice then Sara-> Alice. I wrote this Cypher query to find all the people who know both the right and left side of the directed relation.
match ((n:Person)-[:Know]-> (m:Person)),(p:Person)
where EXISTS ((m)<-[:Know]-(p)-[:Know]->(n))
RETURN m,n,p
I need to get subgraphs with 3 nodes in the query's result but the result I get is a graph with many nodes. Is there any method to change the query to generate subgraphs with just 3 nodes (for example, a subgraph of Alex-> Sara, Alex-> Alice, Sara-> Alice and if Sara has the same condition on two other people it is shown in another subgraph). This requires repeating some nodes in the output.
MATCH clauses are more flexible than that. Try this:
MATCH (n:Person)-[:Know]->(m:Person)<-[:Know]-(p:Person)-[:Know]->(n)
WHERE NOT EXISTS (()-[:Know]->(p))
AND NOT EXISTS {
WITH m, n, p
MATCH (q:Person)-[:Know]->(m)
WHERE q <> n
AND q <> p
}
AND NOT EXISTS {
WITH m, n, p
MATCH (q:Person)-[:Know]->(n)
WHERE q <> p
}
RETURN m, n, p
You might have to use a unique ID property, and I'm not sure if the WITH clause will work here as I've gotten it; but with subqueries, you are generally able to import variables from above using WITH.

How to find same nodes in neo4j?

one question about neo4j. I am implementing the data and with a simple cypher query i want to extract the data where the end node is the same for example.
i have a collection of accountIDs.
`
match (n) where n.ID IN ['4260890','04379258','04643207','2250893','228910','2290','225067003','2002755','225832','2138572','4174122','01884','06563','13397','5216','7789','236740']
WITH collect (n) as IDs
UNWIND IDs AS n
Now, i want to see which employee id belongs to same department. i tried to do it like,
match (n)-[r:Has]-(v) with n, collect (v) as V
Unwind V as Vn
match (m)-[r:Has]-(Vm) with m, collect (Vm) as Va
unwind Va as Vm
Where Vm in Vn
AND NOT 'm'='n'
return distinct n.id as EmpId
collect (distinct Vm) as department
But the problem is, i think this query is not correct. I have error on 2nd where clause.
Can someone help me to find same department shared by the employees of one collection.
Thanks in advnace.

How to eliminate a path where exists a relationship outside of the path, but between nodes in the path?

I have modified the 'vanilla' initial query in this console, and added one relationship type 'LOCKED' between the 'Morpheus' and 'Cypher' nodes.
How can I modify the existing (first-run) query, which is a variable length path so that it no longer reaches the Agent Smith node due to the additional Locked relationship I've added?
First-run query:
MATCH (n:Crew)-[r:KNOWS|LOVES*2..4]->m
WHERE n.name='Neo'
RETURN n AS Neo,r,m
I have tried this kind of thing:
MATCH p=(n:Crew)-[r:KNOWS|LOVES*2..4]->m
WHERE n.name='Neo'
AND none(rel IN rels(p) WHERE EXISTS (StartNode(rel)-[:LOCKED]->EndNode(rel)))
RETURN n AS Neo,r,m
..but it doesn't recognize the pattern inside the none() function.
I'm using Community 2.2.1
Thanks for reading
I'm pretty sure you can't use a function in a MATCHy type clause like that (though it's clever). What about this?
MATCH path=(neo:Crew)-[r:KNOWS|LOVES|LOCKED*2..4]->m
WHERE neo.name='Neo'
AND NOT('LOCKED' IN rels(path))
RETURN neo,r,m
EDIT:
Oops, looks like Dave might have beat me to the punch. Here's the solution I came up with anyway ;)
MATCH p=(neo:Crew)-[r:KNOWS|LOVES*2..4]->m
WHERE neo.name='Neo'
WITH p, neo, m
UNWIND rels(p) AS rel
MATCH (a)-[rel]->(b)
OPTIONAL MATCH a-[locked_rel:LOCKED]->b
WITH neo, m, collect(locked_rel) AS locked_rels
WHERE none(locked_rel IN locked_rels WHERE ()-[locked_rel]->())
RETURN neo, m
Ok, this is a little convoluted but i think it works. The approach is to take all of the paths and find the last known good nodes (ones that have LOCKED relationships leaving them). Then use that node(s) as a new ending point(s) and return the paths.
match p=(n:Crew)-[r:KNOWS|LOVES|LOCKED*2..4]->m
where n.name='Neo'
with n, relationships(p) as rels
unwind rels as r
with n
, case
when type(r) = 'LOCKED' then startNode(r)
else null
end as last_good_node
with n
, (collect( distinct last_good_node)) as last_good_nodes
unwind last_good_nodes as g
match p=n-[r:KNOWS|LOVES*]->g
return p
I think this would be simpler if there was a locked: true property on the KNOWS and LOVES relationships.

seaching a repeated pattern in neo4j

One bill of material relationship is composed of two relationships.
I then find my component definition by searchin this relationship :
(a:Part)-[:consumes]->(b:partBom)-[:referencedBy]->(c:Part)
How can I repeat this pattern in a cypher request in order to find n levels of my bill of material ?
Yoann,
As a follow-on to Michael's answer, if you capture the path in a query of the form
MATCH (a:Part), p=(a)-[:consumes|referencedBy*2..]->(c:Part)
WHERE NOT (c)-[:consumes]->()
WITH a, rels(p) AS rs, nodes(p) AS ns
WITH a, rs, ns, FILTER(n IN ns WHERE 'partBom' IN LABELS(n)) AS boms, FILTER(n in ns WHERE 'Part' in LABELS(n)) AS parts
any following clauses have access to all of the nodes and relationships in the collections ns and rs, and to the BOMs in the collection boms, and the parts in collection parts. The query fragment above will match all of the longest chains of your pattern. You can read more about this relationship pattern concept in the Neo4j manual at this link. You might want to put an upper bound on the number of hops in the initial match if there is a chance of looping.
Grace and peace,
Jim
If you know the number of levels, then you can just spell them out.
(a:Part)-[:consumes]->(b:partBom)-[:referencedBy]->(c:Part)
(c)-[:consumes]->(d:partBom)-[:referencedBy]->(e:Part)
....
Or you can do it more dynamic.
(a:Part)-[:consumes|referencedBy*8]->(c:Part)
WITH rels(path) as rels, nodes(path) as nodes
WHERE ALL(idx in range(0,length(rels)-1,2) WHERE type(rels[idx]) = 'consumes')
AND ALL(idx in range(1,length(rels)-1,2) WHERE type(rels[idx]) = 'referencedBy')
AND ALL(idx in range(1,length(nodes)-1,2) WHERE labels(nodes[idx])[0] = 'partBom')
Usually for something like this, I'd look into the Java API for efficient incremental evaluation of something like that.

How to use SQL-like GROUP BY in Cypher query language, in Neo4j?

I want to find the number of all users in a company and the number of its men and women. My query is:
start n=node:company(name:"comp")
match n<-[:Members_In]-x, n<-[:Members_In]-y
where x.Sex='Male' and y.Sex='Female'
return n.name as companyName, count(distinct x) as NumOfMale,
count(distinct y) as NumOfFemale" );
My query is correct, but I know I shouldn't use n<-[:Members_In]-y in the match clause.
How can I get the number of male, number of female, and total number of users?
Peter creates a cross product here:
I think this works better for your use-case
start n=node:node_auto_index(name='comp')
match n<-[:Members_In]-x
with n.name as companyName, collect(x) as employees
return length(filter(x in employees : x.Sex='Male')) as NumOfMale,
length(filter(x in employees : x.Sex='Female')) as NumOfFemale,
length(employees) as Total
see http://console.neo4j.org/r/msamaa
Try
start n=node:node_auto_index(name='comp')
match n<-[:Members_In]-x, n<-[:Members_In]-y
where x.Sex='Male' and y.Sex='Female'
with
n.name as companyName,
count(distinct x) as NumOfMale,
count(distinct y) as NumOfFemale
return NumOfMale, NumOfFemale, NumOfMale + NumOfFemale as Total
See http://tinyurl.com/cjpxrax for an example.
There is actually an even easier way to achieve that result
START n=node:node_auto_index(name='comp')
MATCH n<-[:Members_In]-x
RETURN count(x.name), x.Sex
take a look at http://architects.dzone.com/articles/neo4jcypher-sql-style-group
should be quite helpfull, thought my answer is kind of late...

Resources