Mongodb - 将包含动态字段的地图转换为数组

时间:2016-08-24 13:41:26

标签: arrays mongodb dictionary aggregation-framework

我在mongodb中有以下结构:

db.coll1.findOne({}, {myMap:1}):

{
  "x1-y1": {
    x: x1,
    y: y1,
    etc: ..
  },
  "x2-y2": {..}
}

其中x和y在0..100范围内。

我认为使用动态命名字段不是一个好主意,我想将此地图转换为数组:

db.coll1.findOne({}, {myArr:1}):

{[
  {
    x: x1,
    y: y1,
    etc: ..
  },
  { .. }
]}

我不需要这些字段的索引。怎么办呢?

1 个答案:

答案 0 :(得分:3)

使用MongoDB 3.4.4及更高版本:

$objectToArray 运算符在这种情况下运行良好,但要获得所需的结果,您需要将其用作 $map 并使用 $map myMap字段替换为 $addFields 的结果。

以下面的例子为例:

填充测试集合:

db.test.insert({
    "myMap": {
        "0-1": {
            "x": 0,
            "y": 1
        },
        "1-2": {
            "x": 1,
            "y": 2
        }
    }
})

<强>管道

db.test.aggregate([
    { "$addFields": {
       "myMap": {
           "$map": {
               "input": { "$objectToArray": "$myMap" },
               "as": "el",
               "in": "$$el.v"
           }
       } 
    } }
])

<强>输出

{
    "_id" : ObjectId("5ba75f206346675838fec680"),
    "myMap" : [ 
        {
            "x" : 0.0,
            "y" : 1.0
        }, 
        {
            "x" : 1.0,
            "y" : 2.0
        }
    ]
}

对于不支持 $objectToArray 运算符的旧版MongoDB,一般的想法是遍历集合,为每个文档映射将子文档键锁定到新数组并更新使用现在存储数组的字段记录文档。直觉最好用一个例子来解释,这假设你在集合中有一些文档:

<强>更新

db.test.find({}).forEach(function(doc){    
    var new_arr = Object.keys(doc.myMap).map(function(key){return doc.myMap[key]});    
    doc["myMap"] = new_arr;
    db.test.save(doc);
})

<强>查询

db.test.find()

示例输出

{
    "_id" : ObjectId("57bdae6ce3f9ad02c421e448"),
    "myMap" : [ 
        {
            "x" : 0,
            "y" : 1
        }, 
        {
            "x" : 1,
            "y" : 2
        }
    ]
}

对于更大的集合,最好通过 bulkWrite() API利用更新:

var ops = [];
db.test.find({}).forEach(function(doc){    
    var new_arr = Object.keys(doc.myMap).map(function(key){return doc.myMap[key]});    
    ops.push({ 
        "updateOne": {
            "filter": { "_id": doc._id },
            "update": { "$set": { "myMap": new_arr } }
         }
    });
    if (ops.length == 1000) {
        db.test.bulkWrite(ops);
        ops = [];
    }
});         

if (ops.length > 0) { db.test.bulkWrite(ops); }

如果您的MongDB版本不支持bulkWrite API,请使用版本2.6和3.0中提供的 Bulk() 方法:

var bulk = db.test.initializeUnorderedBulkOp(),   
    counter = 0;
db.test.find({}).forEach(function(doc) {
    var new_arr = Object.keys(doc.myMap).map(function(key){return doc.myMap[key]});    
    bulk.find({ "_id": doc._id }).updateOne({
        "$set": { "myMap": new_arr } 
    });
    counter++;

    if (counter % 1000 === 0) {
        bulk.execute();
        bulk = db.test.initializeUnorderedBulkOp();
    }    
});

if (counter % 1000 !== 0)
    bulk.execute();