简体   繁体   中英

Filter array and $sum up field of filtered array mongodb

Sorry for the title I don't know how to express the problem better.

I'm trying to sum the total spent money on book items in the MongoDB document. That means before calculating the array must be filtered that only bought books are considered when calculating the total spent money.

This is an example document (I removed some fields so it doesn't get too big):

{
    "_id" : ObjectId("598cb3f4ca693a0688004e2a"),
    "title" : {
        "de" : "Another",
        "ja" : "アナザー",
        "roomaji" : "Another"
    },
    "total" : 4,
    "volumes" : [ 
        {
            "volume" : 1,
            "edition" : 1,
            "price" : 7,
            "releaseDate" : 1342051200,
            "bought" : true,
            "read" : true
        }, 
        {
            "volume" : 2,
            "edition" : 1,
            "price" : 7,
            "releaseDate" : 1347494400,
            "bought" : true,
            "read" : true
        }, 
        {
            "volume" : 3,
            "edition" : 1,
            "price" : 7,
            "releaseDate" : 1352419200,
            "bought" : false,
            "read" : false
        }, 
        {
            "volume" : 4,
            "edition" : 1,
            "price" : 7,
            "releaseDate" : 1357776000,
            "bought" : false,
            "read" : false
        }
    ],
    "releaseYear" : 2012
}

So, I want to sum the price field of all volume objects, but only those where "bought" is set to true. As result I would like to get this output:

{
  "_id" : ObjectId(),
  "title" : {
     'de' : 'title'
  },
  "count" : 14
}

And this is my PHP-Code

$result = $mongo->Book->aggregate([
    [
        '$group' => [
            '_id' => '$_id',
            'title' => ['$push' => '$title.de'],
            'count' => [
                '$sum' => [
                    '$map' => [
                        'input' => '$volumes',
                        'as' => 'vol',
                        'in' => [
                            '$cond' => [
                                [
                                    '$eq' => ['$$vol.bought', true]
                                ],
                                '$$vol.price',
                                0
                            ]
                        ]
                    ],
                ]
            ]
        ]
    ],
    [
        '$sort' => [
            'count' => -1
        ]
    ]
]);

But count always returns 0. What I'm doing wrong?

It's always returning 0 because the $sum operator in the $group pipeline step expects an expression which evaluates to a numerical value yet here the supplied $map expression evaluates to an array. Also the in value of the $map operator is an expression that is applied to each element of the input array which references each element individually with the variable name specified in as hence no filtering is involved as the $cond operator is redundant here.

You need to filter the array first using $filter then you can map the elements. Fortunately for your case the $sum operator can be used in the $project stage as it returns the sum of the specified list of expressions for each document.

Consider running the following pipeline to get the desired result:

$result = $mongo->Book->aggregate([
    [
        "$project" => [
            "title.de" => "$title.de",
            "count" => [
                "$sum" => [
                    "$map" => [
                        "input" => [
                            "$filter" => [
                                "input" => "$volumes",
                                "as" => "vol",
                                "cond" => "$$vol.bought"
                            ]
                        ],
                        "as" => "el",
                        "in" => "$$el.price"
                    ]
                ]
            ]
        ]
    ],
    [
        '$sort' => [
            'count' => -1
        ]
    ]
])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM