MongoDB(mongoose)聚合集合中特定ObjectID的计数实例

时间:2014-10-15 05:04:16

标签: javascript node.js mongodb mongoose aggregation-framework

假设我的模式看起来像这样:

{
    field: [{
        subDoc: ObjectId,
        ...
    }],
    ...
}

我有一些ObjectIds列表(用户输入),我如何得到这些特定ObjectIds的计数?例如,如果我有这样的数据:

[
    {field: [ {subDoc: 123}, {subDoc: 234} ]},
    {field: [ {subDoc: 234}, {subDoc: 345} ]},
    {field: [ {subDoc: 123}, {subDoc: 345}, {subDoc: 456} ]}
]

并且用户给出的id列表是123, 234, 345,我需要计算给定的ID,所以结果接近于:

{
    123: 2,
    234: 2,
    345: 2
}

最好的方法是什么?

2 个答案:

答案 0 :(得分:5)

聚合框架本身如果不按照你提出的输出方式动态命名键,那真的是件好事。但你可以这样做一个查询:

db.collection.aggregate([
    // Match documents that contain the elements
    { "$match": { 
        "field.subDoc": { "$in": [123,234,345] }
    }},

    // De-normalize the array field content
    { "$unwind": "$field" },

    // Match just the elements you want
    { "$match": { 
        "field.subDoc": { "$in": [123,234,345] }
    }},

    // Count by the element as a key
    { "$group": {
        "_id": "$field.subDoc",
        "count": { "$sum": 1 }
    }}
])

这会给你这样的输出:

{ "_id" : 345, "count" : 2 }
{ "_id" : 234, "count" : 2 }
{ "_id" : 123, "count" : 2 }

但是,如果您真的想解决这个问题,那么您需要在查询中指定所需的“键”,这样您就可以形成这样的管道:

db.collection.aggregate([
    { "$match": { 
        "field.subDoc": { "$in": [123,234,345] }
    }},
    { "$unwind": "$field" },
    { "$match": { 
        "field.subDoc": { "$in": [123,234,345] }
    }},
    { "$group": {
        "_id": "$field.subDoc",
        "count": { "$sum": 1 }
    }},
    { "$group": {
        "_id": null,
        "123": { 
            "$max": {
                "$cond": [
                    { "$eq": [ "$_id", 123 ] },
                    "$count",
                    0
                ]
            }
        },
        "234": { 
            "$max": {
                "$cond": [
                    { "$eq": [ "$_id", 234 ] },
                    "$count",
                    0
                ]
            }
        },
        "345": { 
            "$max": {
                "$cond": [
                    { "$eq": [ "$_id", 345 ] },
                    "$count",
                    0
                ]
            }
        }
    }}
])

通过处理参数列表来构造代码的最后一个阶段是一件相对简单的事情:

var list = [123,234,345];

var group2 = { "$group": { "_id": null } };

list.forEach(function(id) {
    group2["$group"][id] = { 
        "$max": {
            "$cond": [
                { "$eq": [ "$_id", id ] },
                "$count",
                0
            ]
        }
    };
});

这或多或少地出现了你想要的东西。

{ 
    "_id" : null, 
    "123" : 2, 
    "234" : 2, 
    "345" : 2 
}

答案 1 :(得分:0)

不完全是你要求的,但它可以给你一个想法:

    db.test.aggregate([
    {
        $unwind: '$field'
    },
    {
        $group: {
            _id: {
                subDoc: '$field.subDoc'
            },
            count: {
                $sum: 1
            }
        }
    },
    {
        $project: {
            subDoc: '$subDoc.subDoc',
            count: '$count'
        }
    }
]);

输出:

 {
    "result": [
        {
            "_id": {
                "subDoc": 456
            },
            "count": 1
        },
        {
            "_id": {
                "subDoc": 345
            },
            "count": 2
        },
        {
            "_id": {
                "subDoc": 234
            },
            "count": 2
        },
        {
            "_id": {
                "subDoc": 123
            },
            "count": 2
        }
    ],
    "ok": 1
}