首页 文章

合并MongoDB聚合中的数组字段

提问于
浏览
12

使用MongoDB聚合框架时是否可以合并数组字段?以下是我要解决的摘要问题:

用于聚合的示例输入文档:

{
  "Category" : 1,
  "Messages" : ["Msg1", "Msg2"],
  "Value" : 1
},
{
  "Category" : 1,
  "Messages" : [],
  "Value" : 10
},
{
  "Category" : 1,
  "Messages" : ["Msg1", "Msg3"],
  "Value" : 100
},
{
  "Category" : 2,
  "Messages" : ["Msg4"],
  "Value" : 1000
},
{
  "Category" : 2,
  "Messages" : ["Msg5"],
  "Value" : 10000
},
{
  "Category" : 3,
  "Messages" : [],
  "Value" : 100000
}

我们希望按“类别”进行分组,同时总结“ Value ”并合并“消息” . 我试过这个聚合管道:

{group : {
        _id : "$Category",
        Value : { $sum : "$Value"},
        Messages : {$push : "$Messages"}
    }
}, 
{$unwind : "$Messages"}, 
{$unwind : "$Messages"}, 
{$group : {
        _id : "$_id",
        Value : {$first : "$Value"},
        Messages : {$addToSet : "$Messages"}
    }
}

结果是:

"result" : [{
        "_id" : 1,
        "Value" : 111,
        "Messages" : ["Msg3", "Msg2", "Msg1"]
    }, 
    {
        "_id" : 2,
        "Value" : 11000,
        "Messages" : ["Msg5", "Msg4"]
    }
]

但是,这完全错过了类别3,因为“类别”为3的文档没有任何“消息”,并且它们在第二次展开时被删除 . 我们希望结果包括以下内容:

{
    "_id" : 3,
    "Value" : 100000,
    "Messages" : []
}

聚合框架是否有一种巧妙的方法来实现这一目标?

2 回答

  • 0

    如果保证Messages是一个数组,你可以使用这个技巧:

    > db.messages.find()
        { "Category" : 1, "Messages" : [  "Msg1",  "Msg2" ], "Value" : 1 }
        { "Category" : 1, "Messages" : [ ], "Value" : 10 }
        { "Category" : 1, "Messages" : [  "Msg1",  "Msg3" ], "Value" : 100 }
        { "Category" : 2, "Messages" : [  "Msg4" ], "Value" : 1000 }
        { "Category" : 2, "Messages" : [  "Msg5" ], "Value" : 10000 }
        { "Category" : 3, "Messages" : [ ], "Value" : 100000 }
    
    > var group1 = {
        "$group":   {
            "_id":      "$Category",
            "Value":    {
                "$sum":     "$Value"
            },
            "Messages": {
                "$push":    "$Messages"
            }
        }
    };
    
    > var project1 = {
        "$project": {
            "Value":    1,
            "Messages": {
                "$cond":    [
                    {
                        "$eq":  [
                            "$Messages",
                            [ [ ] ]
                        ]
                    },
                    [ [ null ] ],
                    "$Messages"
                ]
            }
        }
    };
    
    > db.messages.aggregate( group1, project1 )
        { "_id" : 3, "Value" : 100000, "Messages" : [  [  null ] ] }
        { "_id" : 2, "Value" : 11000, "Messages" : [  [  "Msg4" ],  [  "Msg5" ] ] }
        { "_id" : 1, "Value" : 111, "Messages" : [  [  "Msg1",  "Msg2" ],  [ ],  [  "Msg1",  "Msg3" ] ] }
    

    现在放松两次并重新组合以获得单个Messages数组 .

    > var unwind = {"$unwind":"$Messages"};
    
    > var group2 = {
        $group: {
            "_id":      "$_id", 
            "Value":    {
                "$first":       "$Value"
            }, 
            "Messages": {
                "$addToSet":    "$Messages"
            }
        }
    };
    
    > var project2 = {
        "$project": {
            "Category": "$_id",
            "_id":      0,
            "Value":    1,
            "Messages": {
                "$cond":    [
                    {
                        "$eq":  [
                            "$Messages",
                            [ null ]
                        ]
                    },
                    [ ],
                    "$Messages"
                ]
            }
        }
    };
    
    > db.messages.aggregate(group1, project1, unwind, unwind, group2 ,project2 )
        { "Value" : 111, "Messages" : [  "Msg3",  "Msg2",  "Msg1" ], "Category" : 1 }
        { "Value" : 11000, "Messages" : [  "Msg5",  "Msg4" ], "Category" : 2 }
        { "Value" : 100000, "Messages" : [ ], "Category" : 3 }
    
  • 12

    正如其中一条评论中已经提到的,原始问题的最简单答案是将preserveNullAndEmptyArrays添加到$ unwind阶段 .

相关问题