如果没有文档,MongoDB聚合返回0计数

6

我有一个MongoDB查询,它根据日期以5分钟为窗口进行分组,并返回计数(即该5分钟窗口中文档的总数,使用count: { $sum: 1 })。

我想让查询在特定的5分钟窗口中也返回0的计数,如果该组中不存在文档。然而,目前似乎只返回正计数的组。

当前查询:

        const cursor = await collection.aggregate([
            { $sort : { time : 1 } },
            { 
                $match: {
                     $and: [ 
                        {selector: string },
                        {time: {$gte: timestamp }}
                     ]
                }
            },
            { 
                $group: {
                    _id: {
                        $subtract: [
                            { $subtract: [ "$time", 0 ] },
                            { $mod: [ 
                                { $subtract: [ "$time", 0 ] },
                                1000 * 60 * 5
                            ]}
                        ],
                    },
                    count: { $sum: 1 }
                }
            }
        ])

期望的响应:时间戳及文档总数,包括和为0的文档。
{ _id: 1525162000000, count: 314 }
{ _id: 1523144100000, count: 0 }
{ _id: 1512155500000, count: 54 }

提前致谢!


你需要创建虚假文档,以便分组能够将它们捕捉到,这通常会变得有些棘手。如果可能的话,我会在客户端上执行此操作。如果您发布一些示例数据和预期结果以及现有的聚合管道,我们可能能够帮助您。 - dnickless
我不确定创建虚假文档如何帮助我找出哪些5分钟窗口没有文档。我已经添加了我的查询和预期结果。谢谢! - Alexandra G
1个回答

4

免责声明:我不建议在服务器端(即MongoDB内部)执行此操作,而是应该在客户端处理该情况。

话虽如此,这里提供了一个通用的解决方案,可以轻松适应您的特定情况。

假设您有以下文档(或者是聚合管道的输出,就像您的示例一样):

{
    "category" : 1
}
{
    "category" : 1
}
// note the missing { category: 2 } document here
{
    "category" : 3
}

下面的流水线将创建空桶(因此,在category字段中值范围内缺少“gap”值的文档计数为0,例如数字2):

var bucketSize = 1;

db.getCollection('test').aggregate({
    $group: {
        _id: null, // throw all documents into the same bucket
        "min": { $min: "$category" }, // just to calculate the lowest
        "max": { $max: "$category" }, // and the highest "category" value 
        "docs": { $push: "$$ROOT" } // and also keep the root documents
    }
}, {
    $addFields: {
        "docs": { // modify the existing docs array - created in the previous stage
            $concatArrays: [ // by concatenating
                "$docs", // the existing docs array
                {
                    $map: { // with some other array that will be generated
                        input: {
                            $range: [ "$min", "$max", bucketSize ] // based on the min and max values and the bucket size
                        },
                        as: "this",
                        in: { // but represented not as a plain number but as a document that effectively creates a bogus document
                            "category": "$$this", // the bogus category will be set to the respective value
                            "bogus": 1 // marker that allows us not to count this document in the next stage and still get a bucket from $group
                        }
                    }
                }
            ]
        }
    }
}, {
    $unwind: "$docs" // flatten the "docs" array which will now contain the bogus documents, too
}, {
    $group: {
        _id: "$docs.category", // group by category
        "count": { // this is the result we are interested in
            $sum: { // which will be aggregated by calculating the sum for each document of
                $cond: [ // either 0 or 1 per document
                    { $eq: [ "$docs.bogus", 1 ] }, // depending on whether the document should count as a result or not
                    0,
                    1
                ]
            }
        }
    }
})

以上查询的输出将是:
{
    "_id" : 2,
    "count" : 0.0 // this is what we wanted to achieve
}
{
    "_id" : 3,
    "count" : 1.0 // correct number of matches
}
{
    "_id" : 1,
    "count" : 2.0 // correct number of matches
}

游乐场:https://mongoplayground.net/p/spOKjh6iIZM - allenyllee
你能分享一下为什么不建议在服务器端执行这个操作吗?我也遇到了同样的问题,而且它很适合我的用例(查询在配置文件中,因此没有客户端语言可用)。是出于性能方面的考虑吗? - VannTen
@VannTen,这并不是主要的性能问题...只是感觉运行这个相当复杂的查询有点过度。假设有另一段客户端代码,那么在你的情况下,运行查询可能没有太大问题。但是,如果有另一层可用,为了实现所需的内容,所需的代码在另一种语言中编写和阅读可能会更容易... - dnickless

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接