Concise way to Add virtual property with neighbour count to each result node in Cypher

66 views Asked by At

I'm using NeoDash, and I want to generate a graph report where each node size is weighted by the number of neighbors.

So this count must become a property of the node to be analyzed by NeoDash properly.

Now, after 4 hours, I finally succeeded with the following query, but I'm pretty sure there should be a more elegant and concise way to do the same, right?

  MATCH path=(n)-[r]->(c) //n is already from a subset, and here we exclude _events
    WHERE NOT c:_event

WITH collect(distinct n) as listN, collect(distinct c) as listC
  UNWIND listN as n
    MATCH (n)-[r]->(c) WHERE c in listC

WITH listN, listC, n, count(c) as countC
  SET n.counter=countC

WITH collect(n) as listN, listC
  UNWIND listC as c
    MATCH (n)-[r]->(c) WHERE n in listN

WITH c, count(n) as countN, listN
  SET c.counter=countN

WITH collect(c) as listC, listN
  MATCH (n)-[r]->(c)
    WHERE n in listN 
      and c in listC

RETURN n,r,c

or is there an apoc shortcut for that?

2

There are 2 answers

6
cybersam On BEST ANSWER

The query below only needs one MATCH, and does not need APOC. The CALL subqueries do not return anything, and therefore leave the single outer row as-is.

.
.
.
MATCH (n)-[rel]->(:!_event) //n was defined earlier, and here we exclude _events
WITH COLLECT(rel) as rels
CALL {
  WITH rels
  UNWIND rels AS r
  WITH STARTNODE(r) AS n, COUNT(*) AS countC
  SET n.counter = countC
}
CALL {
  WITH rels
  UNWIND rels AS r
  WITH ENDNODE(r) AS c, COUNT(*) AS countN
  SET c.counter = countN
}
UNWIND rels AS r
RETURN STARTNODE(r) AS n, r, ENDNODE(r) AS c
3
Finbar Good On

You can use the COUNT subquery (available since Neo4j 5.3) to inline the count of neighbours to the SET clause.

This example is equivalent to the initial query, minus returning the original graph:

MATCH (n)-[r]->(c:!_event)
WITH collect(DISTINCT n) + collect(DISTINCT c) AS ncs, collect(r) AS rs
UNWIND ncs AS nc
SET nc.counter = COUNT { (nc)-[r WHERE r IN rs]-() }

The COUNT subquery does mean DB hits. As an alternative, you can avoid subsequent DB hits by just counting the relationships in the first MATCH:

MATCH (n:A)-->(c:!_event)
WITH collect(n) + collect(c) AS ncs
UNWIND ncs AS nc
WITH nc, count(*) AS counter
SET nc.counter = counter