MongoDB: 项目使用数组字段

MongoDB: Project using array fields

我有一个这样的集合:每个文档包含 Message 字段,该字段又包含 Fields 数组。 Fields 数组下的每个文档都有 ValueName 属性。

[
    {
        "_id" : ObjectId("55711efed0103b1598140076"),
        "Message" : {
            "Fields" : [ 
                {
                    "Value" : 131,
                    "Name" : "Options",
                }, 
                {
                    "Value" : 8,
                    "Name" : "Length",
                }       
            ]
        }
    },

    {
        "_id" : ObjectId("55711efed0103b1598140077"),
        "Message" : {
            "Fields" : [ 
                {
                    "Value" : 65,
                    "Name" : "Options",
                }, 
                {
                    "Value" : 13,
                    "Name" : "Length",
                },
                {
                    "Value" : 101,
                    "Name" : "Width",
                }                   
            ]
        }
    }
]

在使用 db.Collection.find({}) 找到文档后,我想这样投影 - 它解析 Message.Fields 下面的每个 Field 并使用 Type 将它们投影到新文档中属性 名称和 Value 作为值。输出如下所示:

[
    {
        "_id" : ObjectId("55711efed0103b1598140076"),
        "Options" : 131,
        "Length" : 8
    },
    {
        "_id" : ObjectId("55711efed0103b1598140077"),
        "Options" : 65,
        "Length" : 13,
        "Width" : 101
    },
]

这是否可以使用 function()aggregate 或 MongoDB 中的任何其他方式实现?

您可以使用aggregation and the $cond运算符

db.collection.aggregate(
    [
      { $unwind: "$Message.Fields" },
      { $project: { fields: "$Message.Fields" }}, 
      { $project: 
          { 
              Option: 
                {
                  $cond: [{ $eq: [ "$fields.Name", "Options" ]}, "$fields.Value", 0 ]
                }, 
              Length: 
                {
                  $cond: [{ $eq: [ "$fields.Name", "Length" ]}, "$fields.Value", 0 ]
                }, 
              Width: 
                { 
                  $cond: [{ $eq: [ "$fields.Name", "Width" ]}, "$fields.Value", 0]
                }
          }
        }, 
        { $group: { 
                    _id: "$_id", 
                    Options: { $sum: "$Option" }, 
                    Width: { $sum: "$Width" }, 
                    Length: { $sum: "$Length" }
                  }
         } 
    ]
)

结果:

{
        "_id" : ObjectId("55711efed0103b1598140077"),
        "Options" : 65,
        "Width" : 101,
        "Length" : 13
}
{
        "_id" : ObjectId("55711efed0103b1598140076"),
        "Options" : 131,
        "Width" : 0,
        "Length" : 8
}

我正在使用以下脚本生成上述结果 [​​=11=]。

var result=[];

var cursor=db.collection.find().forEach( 

function(myDoc) 
{ 
    print(myDoc)

        var obj1={};
        obj1._id=myDoc._id;
        var len_of_fields=myDoc.Message.Fields.length;

        if(len_of_fields==2)
        {
          var j=0;         
          obj1.Options=myDoc.Message.Fields[j].Value;    
          obj1.Length=myDoc.Message.Fields[j+1].Value;
          result.push(obj1)  
        }
        else if(len_of_fields==3)
        {

             var j=0;
            obj1.Options=myDoc.Message.Fields[j].Value;
            obj1.Length=myDoc.Message.Fields[j+1].Value;
            obj1.Width=myDoc.Message.Fields[j+2].Value;

              result.push(obj1)
        }   

    print( "DOC: ", result); 

} 

);

使用 MongoDB 版本 3.4.4 和更新版本,使用 $arrayToObject"$Message.Fields" 数组转换为所需的对象,但在 使用上述运算符,您必须使用 $map 将数组中对象的属性重塑为键 k 和 var。这是 $arrayToObject 将列表转换为 key/value 对的对象所必需的。

转换对象后,您可以将其与 _id 字段合并,然后使用 $replaceRoot.[=26 将根替换为合并后的文档=]

以下管道展示了这种方法:

db.collection.aggregate([
    { "$replaceRoot": {
        "newRoot": {
            "$mergeObjects": [
                { _id: "$_id" },
                { "$arrayToObject": {
                    "$map": {
                        "input": "$Message.Fields",
                        "in": {
                            "k": "$$this.Name",
                            "v": "$$this.Value"
                        }
                    }
                } }
            ]
        }
    } }
])

使用 map() method from the find() 游标,它将一个函数应用于游标访问的每个文档,并将连续应用中的 return 值收集到一个数组中,其中您可以将所需的字段投影到所需的结果中:

var result = db.collection.find().map(function (doc){
    var obj = {};
    obj["_id"] = doc._id
    doc.Message.Fields.forEach(function (field){
        obj[field.Name] = field.Value;
    })
    return obj;
});
printjson(result);

输出:

[
    {
        "_id" : ObjectId("55711efed0103b1598140076"),
        "Options" : 131,
        "Length" : 8
    },
    {
        "_id" : ObjectId("55711efed0103b1598140077"),
        "Options" : 65,
        "Length" : 13,
        "Width" : 101
    }
]