cccfran
cccfran

Reputation: 93

neo4j aggregate function by distance

I want to have some aggregated statistics by distance from root. For example,

(A)-[value:20]->(B)-[value:40]->(C)

(A)-[value:0]->(D)-[value:20]->(E)

CREATE (:firm {name:'A'}), (:firm {name:'B'}), (:firm {name:'C'}), (:firm {name:'D'}), (:firm {name:'E'});
MATCH (a:firm {name:'A'}), (b:firm {name:'B'}), (c:firm {name:'C'}), (d:firm {name:'D'}), (e:firm {name:'E'})
CREATE (a)-[:REL {value: 20}]->(b)->[:REL {value: 40}]->(c),
(a)-[:REL {value: 0}]->(d)->[:REL {value: 20}]->(e);

I want to get the average value of A's immediate neighbors and that of the 2nd layer neighbors, i.e.,

+-------------------+
| distance |  avg   |
+-------------------+
| 1        |   10   |
| 2        |   30   |
+-------------------+

How should I do it? I have tried the following

MATCH p=(n:NODE {name:'A'})-[r:REL*1..2]->(n:NODE)
RETURN length(p), sum(r:value);

But I am not sure how to operate on the variable-length path r.

Similarly, is it possible to get the cumulative value? i.e.,

+-------------------+
| name     |  cum   |
+-------------------+
| B        |   20   |
| C        |   60   |
| D        |   0    |
| E        |   20   |
+-------------------+

Upvotes: 0

Views: 131

Answers (2)

Graphileon
Graphileon

Reputation: 5385

The query below solves the first problem. Please note that it also solves the case where paths are not of equal length. I added (E)-[REL {value:99}]->(F)

MATCH path=(:firm {name:'A'})-[:REL*]->(leaf:firm)
WHERE NOT (leaf)-[:REL]->(:firm)
WITH COLLECT(path) AS paths, max(length(path)) AS longest
UNWIND RANGE(1,longest) AS depth
WITH depth,
     REDUCE(sum=0, path IN [p IN paths WHERE length(p) >= depth] |
            sum 
            + relationships(path)[depth-1].value
     ) AS sumAtDepth,
     SIZE([p IN paths WHERE length(p) >= depth]) AS countAtDepth

RETURN depth, sumAtDepth, countAtDepth, sumAtDepth/countAtDepth AS avgAtDepth

returning

╒═══════╤════════════╤══════════════╤════════════╕
│"depth"│"sumAtDepth"│"countAtDepth"│"avgAtDepth"│
╞═══════╪════════════╪══════════════╪════════════╡
│1      │20          │2             │10          │
├───────┼────────────┼──────────────┼────────────┤
│2      │60          │2             │30          │
├───────┼────────────┼──────────────┼────────────┤
│3      │99          │1             │99          │
└───────┴────────────┴──────────────┴────────────┘

The second question can be answered as follows:

MATCH (root:firm {name:'A'})
MATCH (descendant:firm) WHERE EXISTS((root)-[:REL*]->(descendant))
WITH root,descendant
WITH descendant,
     REDUCE(sum=0,rel IN relationships([(descendant)<-[:REL*]-(root)][0][0]) |
            sum + rel.value
     ) AS cumulative
RETURN descendant.name,cumulative  ORDER BY descendant.name

returning

╒═════════════════╤════════════╕
│"descendant.name"│"cumulative"│
╞═════════════════╪════════════╡
│"B"              │20          │
├─────────────────┼────────────┤
│"C"              │60          │
├─────────────────┼────────────┤
│"D"              │0           │
├─────────────────┼────────────┤
│"E"              │20          │
├─────────────────┼────────────┤
│"F"              │119         │
└─────────────────┴────────────┘

Upvotes: 1

Transformer
Transformer

Reputation: 7439

may I suggest your try it with a reduce function, you can retro fit it your code

// Match something name or distance..
MATCH 
// If you have a condition put in here
// WHERE A<>B AND n.name = m.name
// WITH filterItems, collect(m) AS myItems
// Reduce will help sum/aggregate entire you are looking for
RETURN reduce( sum=0, x IN myItems | sum+x.cost )
LIMIT 10;

Upvotes: 0

Related Questions