我在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: ..
},
{ .. }
]}
我不需要这些字段的索引。怎么办呢?
答案 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();