# Clustering of nodes. Combining nodes based on commonality with other nodes

**URL:** https://community.neo4j.com/t/clustering-of-nodes-combining-nodes-based-on-commonality-with-other-nodes/43322
**Category:** Cypher
**Tags:** apoc, performance, cypher, relationship
**Created:** [August 23, 2021, 3:33pm UTC](https://community.neo4j.com/t/clustering-of-nodes-combining-nodes-based-on-commonality-with-other-nodes/43322 "2021-08-23T15:33:42Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![baturin.egor](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/baturin.egor/32/3861_2.png) [@baturin.egor](https://community.neo4j.com/u/baturin.egor)
#### Post date: [August 23, 2021, 3:33pm UTC](https://community.neo4j.com/t/clustering-of-nodes-combining-nodes-based-on-commonality-with-other-nodes/43322/1 "2021-08-23T15:33:42Z")

</div>

Hello community!

I have User and Group nodes. A user can be a member of any number of groups (or not a member of any) with a directed relationship IN\_GROUP.

I want to find all users who are members of the same set of groups, create a separate Сluster node for them, create an IN\_CLUSTER relationship between them and this Cluster node, and also create a RELATED relationship between the cluster and groups of these users.

Below are some screenshots of what I need:  
I have users, each of which is in a specific set of groups:

 ![example_1](https://us1.discourse-cdn.com/flex021/uploads/neo4jcommunity/original/3X/4/4/448d0c261e4bfa0c22b15cdad0ba00d04b685951.jpeg)

As you can see, User\_1, User\_2 and User\_3 have the same set of groups they belong to (Group\_1, Group\_2 and Group\_3) - this is the first cluster. User\_4 belongs to all groups - this is the second cluster. And the User\_5 belongs to only one group - Group\_5 - this is the third cluster.  
Here's what we get:

 ![example_2](https://us1.discourse-cdn.com/flex021/uploads/neo4jcommunity/original/3X/8/e/8e5703c127b2094d7d4f881ce0c05a5bfed68831.png)

Now we connect the clusters with users groups:

 ![example_3](https://us1.discourse-cdn.com/flex021/uploads/neo4jcommunity/original/3X/3/8/3840ab8f868e46825dd048b3fce9a02e352b2efc.jpeg)

This is what I want to end up with:

 ![example_4](https://us1.discourse-cdn.com/flex021/uploads/neo4jcommunity/original/3X/6/5/65bd7564f42e1c46706c34a2339d9655aa1faccd.jpeg)

I have some code that does the job, but its timing is unacceptable.

```auto
MATCH (u:User)
WITH [(u)-[:IN_GROUP]->(g:Group) | g] as groups, u
WITH apoc.coll.sortNodes(groups, "name") as groups, u
WITH apoc.util.md5(groups) as cluster_hash, groups, u
MERGE (c: Cluster {hash: cluster_hash})
CREATE (u)-[:IN_CLUSTER]->(c)
FOREACH (group IN groups |
MERGE (c)-[:RELATED]->(group))

```

On my dataset (several hundred thousand users and the same number of groups), this takes about 30 minutes to complete. I need a result in 5 seconds.

I'm able to use the apoc library.

Here's a cipher that creates a test data set from the above example:

> **Summary**
>
> ```auto
> CREATE (u1:User {name:"User_1"})
> CREATE (u2:User {name:"User_2"})
> CREATE (u3:User {name:"User_3"})
> CREATE (u4:User {name:"User_4"})
> CREATE (u5:User {name:"User_5"})
> 
> CREATE (g1:Group {name:"Group_1"})
> CREATE (g2:Group {name:"Group_2"})
> CREATE (g3:Group {name:"Group_3"})
> CREATE (g4:Group {name:"Group_4"})
> CREATE (g5:Group {name:"Group_5"})
> 
> MERGE (u1)-[:IN_GROUP]->(g1)
> MERGE (u1)-[:IN_GROUP]->(g2)
> MERGE (u1)-[:IN_GROUP]->(g3)
> 
> MERGE (u2)-[:IN_GROUP]->(g1)
> MERGE (u2)-[:IN_GROUP]->(g2)
> MERGE (u2)-[:IN_GROUP]->(g3)
> 
> MERGE (u3)-[:IN_GROUP]->(g1)
> MERGE (u3)-[:IN_GROUP]->(g2)
> MERGE (u3)-[:IN_GROUP]->(g3)
> 
> MERGE (u4)-[:IN_GROUP]->(g1)
> MERGE (u4)-[:IN_GROUP]->(g2)
> MERGE (u4)-[:IN_GROUP]->(g3)
> MERGE (u4)-[:IN_GROUP]->(g4)
> MERGE (u4)-[:IN_GROUP]->(g5)
> 
> MERGE (u5)-[:IN_GROUP]->(g5)
> 
> RETURN u1, u2, u3, u4, u5, g1, g2, g3, g4, g5
> 
> ```

Neo4j version: 4.3.3

---

<div class="post-metadata">

### Author: ![Bennu](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/bennu/32/11659_2.png) [@Bennu](https://community.neo4j.com/u/Bennu)
#### Post date: [August 24, 2021, 5:13pm UTC](https://community.neo4j.com/t/clustering-of-nodes-combining-nodes-based-on-commonality-with-other-nodes/43322/2 "2021-08-24T17:13:09Z")

</div>

Hi @baturin.egor !

Can you try this small modification? It's hard to measure how much it helps on your complete db.

```auto
MATCH (u:User)-[:IN_GROUP]->(g:Group)
WITH collect(g) as groups, u
WITH distinct apoc.coll.sortNodes(groups, "name") as groups, collect(u) as users
WITH apoc.util.md5(groups) as cluster_hash, groups, users
MERGE (c: Cluster {hash: cluster_hash})
FOREACH (group IN groups |
MERGE (c)-[:RELATED]->(group))
FOREACH (u IN users |
CREATE (u)-[:IN_CLUSTER]->(c))

```

Lemme know if it helps a bit. Btw, Not sure if you can change turn those MERGE into CREATE as well

Bennu

---

<div class="post-metadata">

### Author: ![ameyasoft](https://sea1.discourse-cdn.com/flex021/user_avatar/community.neo4j.com/ameyasoft/32/32639_2.png) [@ameyasoft](https://community.neo4j.com/u/ameyasoft)
#### Post date: [August 24, 2021, 6:24pm UTC](https://community.neo4j.com/t/clustering-of-nodes-combining-nodes-based-on-commonality-with-other-nodes/43322/3 "2021-08-24T18:24:33Z")

</div>

> [@Bennu](#):
>
> ```auto
> WITH apoc.coll.sortNodes(groups, "name") as groups, u
> WITH apoc.util.md5(groups) as c
> 
> ```

```auto
Try this:
match (a:User)-[]-(b:Group)
with distinct id(a) as ID, collect(distinct id(b)) as grps
with distinct grps as n1, size(grps) as cnt order by cnt desc
match (d:Group) where id(d) in n1
with d, n1, cnt
merge (c:Cluster {name: ("Cluster" + " " + cnt)})
merge (c)-[:RELATED]->(d) 
return c, d

```

Result:

 ![Screen Shot 2021-08-24 at 11.22.51 AM](https://us1.discourse-cdn.com/flex021/uploads/neo4jcommunity/original/3X/1/7/174268ddb2acc205ab4efadec5d04c1d17d88ff3.png)
