'How to sum values using unwind in MongoDB with Spring Data

When using unwind("items") each item produces a duplicate line. This leads to revenue being counted as many times as there are items.

Example: Someone buys 1 item of A and 1 item of B, resulting in a cummulative value of 10. Unwind now inserts a row for each item, leading to a sum(cummulative) of 20.

I tried grouping the elements directly after unwind but have not managed to get it to work properly.

How can I sum each array element without duplication every other value?

I have this data structure

{
"_id": {
    "$binary": "VEE6CsjHPvjzS2JYso7mnQ==",
    "$type": "3"
},
"sequentialId": {
    "$numberLong": "1"
},
"date": "2022-02-04",
"invoiceTotal": {
    "$numberDecimal": "9.85"
},
"vatTotal": {
    "$numberDecimal": "0"
},
"vatPercentage": {
    "$numberDecimal": "19.00"
},
"invoiceNumber": "1111111",
"type": "ELEKTRONISCH",
"aktivkonto": 2800,
"passivkonto": 5200,
"buyerEmail": "",
"username": "",
"shop": "",
"externalId": "",
"shipped": false,
"actualShippingCost": {
    "$numberDecimal": "1"
},
"filename": "",
"isReported": false,
"deliveryCostTotal": {
    "$numberDecimal": "4.35"
},
"items": [
    {
        "lineItemId": "",
        "amount": "1",
        "sku": "A123123",
        "title": "",
        "priceTotal": {
            "$numberDecimal": "4.50"
        },
        "vatTotal": {
            "$numberDecimal": "0"
        },
        "hardwareCostPerPiece": {
            "$numberDecimal": "0.22"
        },
        "hardwareCostTotal": {
            "$numberDecimal": "0.22"
        }
    },
    {
        "lineItemId": "",
        "amount": "1",
        "sku": "B212312",
        "title": "",
        "priceTotal": {
            "$numberDecimal": "1.00"
        },
        "vatTotal": {
            "$numberDecimal": "0"
        },
        "hardwareCostPerPiece": {
            "$numberDecimal": "0.22"
        },
        "hardwareCostTotal": {
            "$numberDecimal": "0.22"
        }
    }
],
"packagingCost": {
    "$numberDecimal": "0.15"
},
"hasInvoiceSent": false,
"tenant": "you!",
"createdAt": {
    "$date": "2022-02-04T15:23:40.716Z"
},
"modifiedAt": {
    "$date": "2022-02-04T15:23:40.716Z"
},
"_class": "_.RevenueEntity"
}

and this query

    fun sumAllByWeeklyAndTenant(tenant: String): Flux<DashboardRevenue> {
    val aggregation = newAggregation(
        match(findByTenant(tenant)),
        unwind("items"),
        group("invoiceNumber")
            .sum("items.hardwareCostTotal").`as`("hardwareCostTotal"),
        project()
            .andExpression("year(createdAt)").`as`("year")
            .andExpression("week(createdAt)").`as`("week")
            .andInclude(bind("hardwareCostTotal", "items.hardwareCostTotal"))
            .andInclude(
                "invoiceTotal",
                "vatTotal",
                "actualShippingCost",
                "packagingCost",
                "marketplaceFeesTotal",
                "tenant"
            ),
        group("year", "week", "tenant")
            .sum("invoiceTotal").`as`("umsatz")
            .sum("actualShippingCost").`as`("portokosten")
            .sum("packagingCost").`as`("verpackung")
            .sum("marketplaceFeesTotal").`as`("marketplaceFees")
            .sum("hardwareCostTotal").`as`("hardwareCost")
            .sum("vatTotal").`as`("vatTotal")
            .count().`as`("numberOfInvoices"),
        sort(Sort.Direction.DESC,  "year", "week"),
        limit(8),
        sort(Sort.Direction.ASC, "year", "week")
    )

    return reactiveMongoTemplate
        .aggregate(aggregation, "revenue", DashboardRevenue::class.java)
        .toFlux()
}

Using the data above the query results in

[
{
    "_id": {
        "year": 2022,
        "month": 2,
        "week": 0,
        "tenant": "einsupershop"
    },
    "umsatz": 19.70,
    "portokosten": 2,
    "verpackung": 0.30,
    "marketplaceFees": 3.42,
    "hardwareCost": 0.22,
    "vatTotal": 0,
    "numberOfInvoices": 2
}
]

Where the expected value is "invoiceTotal": { "$numberDecimal": "9.85" }



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source