Mongodb聚合在数组的子文档中

agn*_*aft 4 mapreduce mongodb aggregation-framework

我正在使用mongodb作为后端实现一个小应用程序.在这个应用程序中,我有一个数据结构,其中文档将包含一个包含子文档数组的字段.

我使用以下用例作为基础:http: //docs.mongodb.org/manual/use-cases/inventory-management/

从示例中可以看出,每个文档都有一个名为carted的字段,该字段是一个子文档数组.

{
    _id: 42,
    last_modified: ISODate("2012-03-09T20:55:36Z"),
    status: 'active',
    items: [
        { sku: '00e8da9b', qty: 1, item_details: {...} },
        { sku: '0ab42f88', qty: 4, item_details: {...} }
    ]
}
Run Code Online (Sandbox Code Playgroud)

除了一个问题外,这非常适合我:我想在整个集合中计算每个唯一项目("sku"作为唯一标识符键),其中每个文档将计数加1(同一个"sku"的多个实例)相同的文件仍将只计数1).我想这个结果:

{sku:'00e8da9b',doc_count:1},{sku:'0ab42f88',doc_count:9}

在阅读了MongoDB之后,当你有一个如上所述的复杂模式时,我很惊讶如何(快速)这样做.如果我已经理解了其他优秀文档的正确性,那么可以使用聚合框架或map/reduce框架来实现这样的操作,但这是我需要一些输入的地方:

  • 考虑到结构的复杂性,哪个框架更适合实现我正在寻找的结果?
  • 为了从所选框架中获得最佳性能,首选哪种索引?

cir*_*rus 14

MapReduce很慢,但它可以处理非常大的数据集.另一方面,聚合框架更快一些,但是会遇到大量数据.

显示的结构的问题是你需要"$ unwind"数组来破解数据.这意味着为每个数组项创建一个新文档,并使用聚合框架在内存中执行此操作.因此,如果您有1000个包含100个数组元素的文档,则需要构建100,000个文档流以便groupBy并对其进行计数.

您可能需要考虑是否存在可以更好地为您的查询提供服务的架构布局,但是如果您想在聚合框架中执行此操作,那么您将如何执行此操作(使用一些示例数据以便整个脚本将放入shell中) ;

db.so.remove();
db.so.ensureIndex({ "items.sku": 1}, {unique:false});
db.so.insert([
    {
        _id: 42,
        last_modified: ISODate("2012-03-09T20:55:36Z"),
        status: 'active',
        items: [
            { sku: '00e8da9b', qty: 1, item_details: {} },
            { sku: '0ab42f88', qty: 4, item_details: {} },
            { sku: '0ab42f88', qty: 4, item_details: {} },
            { sku: '0ab42f88', qty: 4, item_details: {} },
    ]
    },
    {
        _id: 43,
        last_modified: ISODate("2012-03-09T20:55:36Z"),
        status: 'active',
        items: [
            { sku: '00e8da9b', qty: 1, item_details: {} },
            { sku: '0ab42f88', qty: 4, item_details: {} },
        ]
    },
]);


db.so.runCommand("aggregate", {
    pipeline: [
        {   // optional filter to exclude inactive elements - can be removed    
            // you'll want an index on this if you use it too
            $match: { status: "active" }
        },
        // unwind creates a doc for every array element
        { $unwind: "$items" },
        {
            $group: {
                // group by unique SKU, but you only wanted to count a SKU once per doc id
                _id: { _id: "$_id", sku: "$items.sku" },
            }
        },
        {
            $group: {
                // group by unique SKU, and count them
                _id: { sku:"$_id.sku" },
                doc_count: { $sum: 1 },
            }
        }
    ]
    //,explain:true
})
Run Code Online (Sandbox Code Playgroud)

请注意,我已经两次$ group'd,因为你说SKU每个文档只能计算一次,所以我们需要首先整理出唯一的doc/sku对,然后对它们进行计数.

如果你想让输出略有不同(换句话说,就像你的样本一样),我们可以对它们进行投影.