DevMaster
DevMaster

Reputation: 1

Combined nodes to a single one based on a common property

I'm trying to learn more about Neo4j. Is there a way to group nodes that have the same name/number/etc.?

In standard RDBMS one can use the "Group by" clause to combine specific values into one. Have a look at the picture for a better understanding.

Image HERE

I'd like to combine these two nodes ("EE", "EE") into one, which has both sub-nodes.

Code for the image output:

match (A:Place)-[:`->`]-(B:Typ)
where A.name ='City1'
return A, B

Upvotes: 0

Views: 204

Answers (3)

John Mark
John Mark

Reputation: 363

Just in case you were not aware: if you are looking to permanently merge nodes into 1 merging the relationships too, you can use APOC. If all the properties (except for the internal node id) are the same, then maybe.

CALL apoc.refactor.mergeNodes([node1, node2])

Upvotes: 1

Gabor Szarnyas
Gabor Szarnyas

Reputation: 5047

There is no equivalent for SQL's GROUP BY clause in Cypher, as it is done implicitly. So whenever you use an aggregating function, such as count, sum, min or collect, the rest of the variables in the tuple are grouped implicitly.

MATCH (a:Place {name: 'City1'})-[:`->`]-(b:Typ)
RETURN a.name, count(b)

However, it seems to me that you actually want to perform a transformation that combines parent nodes together (we could say that the nodes are "merged" together, but I would like to avoid that word, as there is a MERGE clause in Cypher with different semantics -- see @cybersam's answer for details).

  1. Match for the required pattern.
  2. Project to the property that will serve as a key for grouping and group.
  3. Create a new parent node.
  4. Create the name property to that node.
  5. Add relationships to the new node.
  6. Remove old parent nodes.

In practice, this could be implemented with a query like this:

MATCH (a:Place {name: 'City1'})-[:`->`]-(b:Typ)
WITH a.name AS name, collect(a) AS as, collect(b) AS bs
CREATE (new:Place {name: name})
WITH as, bs, new
UNWIND bs AS b
CREATE (new)-[:`->`]->(b)
WITH as
UNWIND as AS a
DETACH DELETE a

While the query is quite complex, fortunately, it is easy to test on a toy data set:

CREATE
  (:Place {name: 'City1'})-[:`->`]->(:Typ {name: 'B1'}),
  (:Place {name: 'City1'})-[:`->`]->(:Typ {name: 'B2'})

This results in:

enter image description here

And is combined to:

enter image description here

Upvotes: 1

cybersam
cybersam

Reputation: 67019

When you created your Place nodes, you should have used a MERGE operation instead of CREATE. For example, no matter how many times you execute the following, it will not create a duplicate Place node if one already existed with the same name:

MERGE (a:Place {name: 'City1'})
...

Also, before you use the MERGE operation, you should make it work optimally by first creating an index on :Place(name) (or a uniqueness constraint on the same node label and property).

Upvotes: 2

Related Questions