【问题标题】:MongoDB aggregate group array to key : sum valueMongoDB聚合组数组到键:总和值
【发布时间】:2014-05-13 23:37:41
【问题描述】:

您好,我是 mongodb 新手,正在尝试将不同类型 (int) 的对象转换为键值对。

我有这样的收藏:

{
    "_id" : ObjectId("5372a9fc0079285635db14d8"),
    "type" : 1,
    "stat" : "foobar"
},
{
    "_id" : ObjectId("5372aa000079285635db14d9"),
    "type" : 1,
    "stat" : "foobar"
},
{
    "_id" : ObjectId("5372aa010079285635db14da"),
    "type" : 2,
    "stat" : "foobar"
},{
    "_id" : ObjectId("5372aa030079285635db14db"),
    "type" : 3,
    "stat" : "foobar"
}

我想得到这样的结果:

{
    "type1" : 2, "type2" : 1, "type3" : 1,
    "stat" : "foobar"
}

目前正在尝试聚合组,然后将类型值推送到数组

db.types.aggregate(
    {$group : {
        _id : "$stat",
        types : {$push : "$type"}
    }}
)

但不知道如何对不同的类型求和并转换成键值

/* 0 */
{
    "result" : [ 
        {
            "_id" : "foobar",
            "types" : [ 
                1, 
                2, 
                2, 
                3
            ]
        }
    ],
    "ok" : 1
}

【问题讨论】:

  • 如果您希望能够将值映射到键,请投票给这张 jira 票 jira.mongodb.org/browse/SERVER-5947,因为目前还不可能。
  • 嗨 Asya,我为这张 jira 票投了票。

标签: mongodb mapreduce aggregation-framework


【解决方案1】:

对于您的实际形式,因此假设您实际上知道“类型”的可能值,那么您可以使用两个 $group 阶段和一些 $cond 运算符来执行此操作:

db.types.aggregate([
    { "$group": {
         "_id": {
             "stat": "$stat",
             "type": "$type"
         },
         "count": { "$sum": 1 }
    }},
    { "$group": {
        "_id": "$_id.stat",
        "type1": { "$sum": { "$cond": [
            { "$eq": [ "$_id.type", 1 ] },
            "$count",
            0
        ]}},
        "type2": { "$sum": { "$cond": [
            { "$eq": [ "$_id.type", 2 ] },
            "$count",
            0
        ]}},
        "type3": { "$sum": { "$cond": [
            { "$eq": [ "$_id.type", 3 ] },
            "$count",
            0
        ]}}
    }}
])

确切的给出:

{ "_id" : "foobar", "type1" : 2, "type2" : 1, "type3" : 1 }

我实际上更喜欢具有两个 $group 阶段的更动态的形式:

db.types.aggregate([
    { "$group": {
         "_id": {
             "stat": "$stat",
             "type": "$type"
         },
         "count": { "$sum": 1 }
    }},
    { "$group": {
        "_id": "$_id.stat",
        "types": { "$push": {
            "type": "$_id.type",
            "count": "$count"
        }}
    }}
])

输出不同,但功能和价值灵活:

{
    "_id" : "foobar",
    "types" : [
            {
                    "type" : 3,
                    "count" : 1
            },
            {
                    "type" : 2,
                    "count" : 1
            },
            {
                    "type" : 1,
                    "count" : 2
            }
    ]
}

否则,如果您需要相同的输出格式但需要灵活的字段,那么您始终可以使用 mapReduce,但它的输出并不完全相同。

db.types.mapReduce(
    function () {

        var obj = { };

        var key = "type" + this.type;
        obj[key] = 1;

        emit( this.stat, obj );

    },
    function (key,values) {

        var obj = {};

        values.forEach(function(value) {
            for ( var k in value ) {
                if ( !obj.hasOwnProperty(k) )
                    obj[k] = 0;
                obj[k]++;
            }
        });

        return obj;

    },
    { "out": { "inline": 1 } }
)

典型的 mapReduce 风格:

   "results" : [
            {
                    "_id" : "foobar",
                    "value" : {
                            "type1" : 2,
                            "type2" : 1,
                            "type3" : 1
                    }
            }
    ],

但这些都是你的选择

【讨论】:

  • 我使用了您对条件的建议,这使得结果符合所需的格式,并且完美运行我的收藏有点复杂,我会有大量的文档。查询中的这些条件是否会产生性能问题?
  • @PoisoneR 这里的答案有问题吗?显示了一些方法,但没有什么可以“动态地”重命名这些字段,除了 mapReduce 方法,由于此时它的工作方式并不完全相同。但所显示的方法是目前的方法,或者至少接近结果。因此,似乎并不存在另一个答案,但这个问题仍然存在不被接受的答案。为什么?性能是相对的,但是聚合框架比 mapReduce 快。现代 MongoDB 具有允许使用内存的选项。
  • 我忘记将答案标记为已接受。这是工作。只是想知道管道中的几个 $group 和 $project 阶段如何影响性能。谢谢。
【解决方案2】:

这对你来说够近了吗?

{ "_id" : "foobar", "types" : [ { "type" : "type3", "total" : 1 }, { "type" : "type2", "total" : 1 }, { "type" : "type1", "total" : 2 } ] }

这些类型在一个数组中,但它似乎可以为您提供您正在寻找的数据。代码是:

db.types.aggregate(
    [{$group : {
        _id : "$stat",
        types : {$push : "$type"}
    }},
    {$unwind:"$types"},
    {$group: {
        _id:{stat:"$_id",
        types: {$substr: ["$types", 0, 1]}},
        total:{$sum:1}}},
    {$project: {
        _id:0,
        stat:"$_id.stat",
        type: { $concat: [ "type", "$_id.types" ] },
        total:"$total" }},
    {$group: {
        _id: "$stat",
        types: { $push: { type: "$type", total: "$total" } } }}
   ]
)

【讨论】:

  • 结果是可以实现的,虽然我更喜欢数组形式。但主要是在这里,为什么所有的阶段?我添加了一个回复,以表明无论采用哪种方法,您实际上只需要两个。
  • 同意,还没有完成 - 已经使用 $concat 来匹配 OP 所需的格式,并在优化之前等待反馈。
  • 我认为这个结果对我的问题没有好处。我想在html模板中显示表格数据,这个结果代表一行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-26
  • 2016-07-03
  • 2018-06-03
  • 1970-01-01
  • 2020-01-04
  • 1970-01-01
相关资源
最近更新 更多