0

I am trying to figure out how to implement the OLAP operation Roll-up in Cypher. I am stuck with finding how to create a node and then assign it the sum of a column of a table in Cypher. More particularly, I am trying to achieve this result : Code result [EDITED] With this code I am obtaining the result as seen in the picture :

MATCH(p:Product)
WITH sum(p.unitsInStock) as SommeUnits, p.supplierID as Supplier, p.reorderLevel as ReordLevel
CREATE(reord:Product {productName : "TotalReord", unitsInStock : SommeUnits})
RETURN ReordLevel,Supplier, SommeUnits
ORDER BY ReordLevel

Relationships : Supplier-[:SUPPLIES]->(Product) Attributs of Product : unitsInStock, productName, productId, unitsInOrder, supplierID

The problem is that this code is not working properly because it generates not just one node but multiples, and the result that I get is obtained only if I play the query twice.

kazrak
  • 3
  • 6

1 Answers1

1

Aggregating functions like SUM use the non-aggregated items in the same (WITH or RETURN) clause as "grouping keys". So, your WITH clause is generating a SommeUnits value for each distinct SommeUnits/ReordLevel combination. And your CREATE is being called for each SommeUnits value.

This query should work properly (assuming that you want every returned record to have the same SommeUnits value):

MATCH(p:Product) WHERE p.productName <> "TotalReord"
WITH SUM(p.unitsInStock) AS SommeUnits, COLLECT(p) AS ps
MERGE(t:Product {productName: "TotalReord"})
SET t.unitsInStock = SommeUnits
WITH ps, SommeUnits
UNWIND ps AS p
RETURN p.reorderLevel AS ReordLevel, p.supplierID AS Supplier, SommeUnits
ORDER BY ReordLevel

This query uses MERGE to avoid creating duplicate "TotalReord" nodes every time you call this query. And the WHERE clause filters out the "TotalReord" node, so that its existing unitsInStock value will not be used when calculating the new sum, inflating it.

If you actually want each returned record to have the sum just for that record's SommeUnits/ReordLevel combination, you can do this:

MATCH(p:Product) WHERE p.productName <> "TotalReord"
WITH SUM(p.unitsInStock) AS u, p.reorderLevel AS r, p.supplierID AS s
WITH COLLECT({u:u, r:r, s:s}) AS data, SUM(u) AS total
MERGE(t:Product {productName: "TotalReord"})
SET t.unitsInStock = total
WITH data
UNWIND data AS d
RETURN d.r AS ReordLevel, d.s AS Supplier, d.u AS SommeUnits
ORDER BY ReordLevel

[UPDATED]

Finally, if you want to do the previous query but also get the overall total, this query will show the overall total as the fourth value in each returned record:

MATCH(p:Product) WHERE p.productName <> "TotalReord"
WITH SUM(p.unitsInStock) AS u, p.reorderLevel AS r, p.supplierID AS s
WITH COLLECT({u:u, r:r, s:s}) AS data, SUM(u) AS total
MERGE(t:Product {productName: "TotalReord"})
SET t.unitsInStock = total
WITH data, total
UNWIND data AS d
RETURN d.r AS ReordLevel, d.s AS Supplier, d.u AS SommeUnits, total
ORDER BY ReordLevel
cybersam
  • 63,203
  • 6
  • 53
  • 76
  • Thank you for your answer, @cybersam. In fact i just realised the photo I shared was not the good one so I replaced it in the original post. The first column is for the ReordLevel, the second one for Suppliers and the third one for SommeUnits.In fact what I actually want to do is to display the SommeUnits for all ReordLevel (as you can see in the photo in the last line), and the SommeUnits for every ReordLevel (example : the sum of all Units for ReordLevel 25, then for 30, etc). I also tried your second query, but it doesn't work. There's an error asking for a WITH between SET and UNWIND. – kazrak Feb 08 '19 at 08:06
  • I have now added a `WITH` before the `UNWIND`. – cybersam Feb 08 '19 at 19:23
  • And I have added another query to also get the overall total (in addition to the SommeUnits per level/supplier combination). – cybersam Feb 08 '19 at 19:35