find all users mutual connections in neo4j db
here is the solution
MATCH (u1:Profile)-[r1]->(c:Profile), (u2:Profile)-[r2]->(c) WHERE u1 < u2 RETURN u1.name, u2.name, collect(c.name) as mutual_connections
MATCH (u1:Profile)-[r1]->(c:Profile), (u2:Profile)-[r2]->(c) WHERE u1 < u2 RETURN u1.name, u2.name, collect(c.name) as mutual_connections
Related
I have a graph db of Node User (properties: uid, name) and Relationship Invitation (properties: invitation_id, invitation_time).
The relationship is built when one user invite other users. That means every time one user invites, it will build the same relationship between him and the users he invited.
I want to count the unique invitations of each user.
My cyper query is:
match (u:User)-[r:Invitation]->()
return u, count(distinct r)
order by count(distinct r) desc
Instead of meet my expectation, this query did not drop the duplicates.
So what should be the right query?
I got the answer by myself just after posting the question:
match (u:User)-[r:Invitation]->()
return u, count(distinct r.invitation_id)
order by count(distinct r.invitation_id) desc
Ok so here is my data model:
(User)-[:ASSIGNED_TO]-(Account)
(Location)-[:BELONGS_TO]->(Account)
(User)-[:ASSIGNED_TO]-(Location)
In my database there is 1 account, 1 location and 16 users. Each user is :ASSIGNED_TO the account and also :ASSIGNED_TO the location. The location :BELONGS_TO the account.
I'm trying to select a specific account by id and also return the number of users and locations for that account. Here is my query:
MATCH (account:Account)
WHERE account.id = '123456'
WITH account
OPTIONAL MATCH (location:Location)-[:BELONGS_TO]->(account)
OPTIONAL MATCH (user:User)-[:ASSIGNED_TO]->(account)
RETURN account, count(location) as locationCount, count(user) as userCount
The result is the account, a userCount = 16 (correct) and a locationCount = 16 (incorrect; should be 1). If I add distinct to the location count, count(distinct location), I get the correct result (1) and if I remove the OPTIONAL MATCH for the users, I also get a location count of 1. I know it has something to do with the users having a relationship to the account and the location but I'm just trying to understand why the query without distinct doesn't work. Also, is there a better way to write this?
It is indeed a bit tricky. This is the query rewritten to show the pattern you are looking for :
MATCH (account:Account)
WHERE account.id = '123456'
MATCH (location:Location)-[:BELONGS_TO]->(account)<-[:ASSIGNED_TO]-(user:User)
RETURN account, count (location), count (user)
There's one account in the middle, but you don't know what the numbers are on each side. The resultset will contain all matches for the pattern (happens to be 16, but there could have been more locations and users assigned to multiple locations). So actually neither count is correct (you just get lucky for the users).
MATCH (account:Account)
WHERE account.id = '123456'
MATCH (location:Location)-[:BELONGS_TO]->(account)<-[:ASSIGNED_TO]-(user:User)
RETURN account, count (DISTINCT location), count (DISTINCT user)
DISTINCT solves the problem. Aggregated by account (there is only one so no real aggregation happens) there are 16 locations in the resultset. DISTINCT makes sure you only count the unique ones. And the same DOES apply for the users too !
Take a look at this query to see the difference :
MATCH (account:Account)
WHERE account.id = '123456'
MATCH (location:Location)-[:BELONGS_TO]->(account)
RETURN account.id as id, "location count" as type, count(location) as ct
UNION
MATCH (account:Account)
WHERE account.id = '123456'
MATCH (account)<-[:ASSIGNED_TO]-(user:User)
RETURN account.id as id, "user count" as type, count(user) as ct
Hope this helps.
Regards,
Tom
If you view your result in row instead of graph, you can see that there are actually 16 rows of data. Each row will contain location, count(location) is actually returning the number of lines that has location.
I prefer using distinct for removing duplicates. We have service in production and we are using distinct in a similar scenario.
I was facing a similar problem and it helped to think of it from an RDBMS perspective.
Consider a table of Users like so (I'll use 4 for my example):
Users
-----
u1
u2
u3
u4
And consider 2 tables of Locations and Accounts each (with one record each, as in your case):
Locations
---------
loc1
Accounts
--------
acc1
Now, when Neo4j evaluates a query like MATCH (location:Location)-[:BELONGS_TO]->(account)<-[:ASSIGNED_TO]-(user:User), it starts looking for User nodes, and Location nodes, and follows relationships inwards to Account nodes, where it then performs a join. So, to break that query into intermediate queries, it would look like: MATCH (location:Location)-[:BELONGS_TO]->(account) and MATCH (account)<-[:ASSIGNED_TO]-(user:User). Evaluating those 2 queries would give us something like the following tables:
Location-Account
----------------
loc1 | acc1
Account-User
------------
u1 | acc1
u2 | acc1
u3 | acc1
u4 | acc1
Finally, Neo4j performs a join on the intermediate results, to return something like the following combined table:
User-Account-Location
---------------------
u1 | acc1 | loc1
u2 | acc1 | loc1
u3 | acc1 | loc1
u4 | acc1 | loc1
count(location) as per this table would be 4, while count(DISTINCT(location)) would be 1 :)
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
I've been trying to get a subgraph based on a node query.
The query should ignore the relationship directions as long as all of the nodes in the subgraph are connected:
ex:
u1 -FRIEND-> u2 -FRIEND-> u3
u4 -FRIEND-> u5 -FRIEND-> u6
searching for u1 or u2 or u3, should return a set of: [u1,u2,u3]
I used the following Cypher query:
MATCH (a:User)-[:FRIEND_OF*0..]-(b)
WHERE a.userId = 'some_id'
WITH a, collect(DISTINCT b) AS sets
RETURN DISTINCT sets
The problem is that I'm getting all of the set's permutations like:
DATA: u1 -FRIEND-> u2 -FRIEND-> u3
RETURN: [u1,u2,u3],[u1,u3,u2],[u2,u1,u3]...
how can I distinct the different sets to return only one permutation?
I also would like to support a case a user can be in different subgraphs so the response should be couple of subgraphs.
thanks
This query should work:
MATCH p=(a:User)-[:FRIEND_OF*0..]-(b)
WHERE a.userId = 'some_id'
WITH DISTINCT a, b
ORDER BY ID(b)
WITH a, COLLECT(b) AS sets
RETURN DISTINCT sets;
It gets distinct a/b pairs, orders the b nodes by native ID, puts the ordered nodes in collections, and finally returns distinct collections.
You may want to create an index for :User(userId) for better performance.
For the cypher -
match (m)-[r]-(n) where m.name = 'XYZ' return n.name, type(r), m.name
n.name type(r) m.name
XYZ belongs_to Ordering Status
XYZ runs_on_queue inbound
XYZ runs_on_db DBxc
In this case, Ordering Status is a business service that "owns" XYZ & the relation is defined as follows:
CREATE (XYZ)-[:belongs_to]->(Order)
Type(r) only gives the relation but not the direction of the relation. Is this still the optimal way to get the direction - I also noticed a comment on not being available for Cypher
Neo4j Cypher Get Relationship Direction
Thanks.
Not as a function, but you can do this:
MATCH (m)-[r]-(n)
RETURN m.name, TYPE(r), n.name,
CASE WHEN STARTNODE(r) = m THEN 'outgoing' ELSE 'incoming' END AS direction