我有一个现有的深层嵌套的mongoDB架构,我必须弄平,因为我有一个复杂的查询,无法用当前结构有效地进行.这是架构的MWE:
db.test.insert({
"_id" : ObjectId("58e574a768afb6085ec3a388"),
"details" : [
{
"_id" : ObjectId("58e55f0f68afb6085ec3a2cc"),
"a" : [
{
"unit" : "08",
"size" : "5",
"pos" : "Far",
"_id" : ObjectId("58e55f0f68afb6085ec3a2d0")
}
],
"b" : [
{
"unit" : "08",
"size" : "5",
"pos" : "Far",
"_id" : ObjectId("58e55f0f68afb6085ec3a2cd")
}
],
"c" : [
{
"unit" : "08",
"size" : "3",
"pos" : "Far",
"_id" : ObjectId("58e55f0f68afb6085ec3a2ce")
}
],
"d" : [
{
"unit" : "08",
"size" : "5",
"pos" : "Far",
"_id" : ObjectId("58e55f0f68afb6085ec3a2cf")
}
]
}
]
})
Run Code Online (Sandbox Code Playgroud)
我想弄平图式.期望的结果是:
"_id" : ObjectId("58e574a768afb6085ec3a388"),
"tests" : [
{
"_id" : ObjectId("58e542fb68afb6085ec3a1d2"),
"aUnit" : "08",
"aSize" : "5",
"aPos" : "Far",
"bPos" : "Far",
"bSize" : "5",
"bUnit" : "08",
"cPos" : "Far",
"cSize" : "3",
"cUnit" : "08",
"dPos" : "Far",
"dSize" : "5",
"dUnit" : "08"
}
]
Run Code Online (Sandbox Code Playgroud)
我愿意一次做一个入口类型,我认为我有一个方法可以这样做,但它不起作用.这是我尝试过的:
db.test.find({"tests.$.details.a.unit":{$exists:true}}).forEach(function(doc) {
doc.tests = {aUnit:tests.details.a.unit};
delete tests.details.a.unit;
db.test.save(doc);
});
Run Code Online (Sandbox Code Playgroud)
但是,这没有任何改变.如何改进我的查询以展平我的架构?
编辑:我意识到MWE与我打算用它的那个相比有一个小错误.我关闭了每个条目.例如,"a" : [{ ... }],被错误地写为{"a" : [{ ... }]},.但是,它现在已更新.
打印数据
db.test.find().forEach(doc => {
doc.details = doc.details.map( detail => {
Object.keys(detail).filter( k => k !== "_id" ).forEach( k => {
detail[k].forEach( item => {
Object.keys(item).filter(i => i !== "_id" ).forEach( inner => {
detail[k + inner.charAt(0).toUpperCase() + inner.substr(1)]
= item[inner];
})
});
delete detail[k];
});
return detail;
});
printjson(doc);
});
Run Code Online (Sandbox Code Playgroud)
更新数据
db.test.find().forEach(doc => {
doc.details = doc.details.map( detail => {
Object.keys(detail).filter( k => k !== "_id" ).forEach( k => {
detail[k].forEach( item => {
Object.keys(item).filter(i => i !== "_id" ).forEach( inner => {
detail[k + inner.charAt(0).toUpperCase() + inner.substr(1)]
= item[inner];
})
});
delete detail[k];
});
return detail;
});
ops = [
...ops,
{ "updateOne": {
"filter": { "_id": doc._id },
"update": { "$set": { "doc.details": doc.details } }
}}
];
if ( ops.length >= 500 ) {
db.test.bulkWrite(ops);
ops = [];
}
});
if ( ops.length > 0 ) {
db.test.bulkWrite(ops);
ops = [];
}
Run Code Online (Sandbox Code Playgroud)
输出表格
{
"_id" : ObjectId("58e574a768afb6085ec3a388"),
"details" : [
{
"_id" : ObjectId("58e55f0f68afb6085ec3a2cc"),
"aUnit" : "08",
"aSize" : "5",
"aPos" : "Far",
"bUnit" : "08",
"bSize" : "5",
"bPos" : "Far",
"cUnit" : "08",
"cSize" : "3",
"cPos" : "Far",
"dUnit" : "08",
"dSize" : "5",
"dPos" : "Far"
}
]
}
Run Code Online (Sandbox Code Playgroud)
{
"_id" : ObjectId("58e574a768afb6085ec3a388"),
"tests" : [
{
"_id" : ObjectId("58e542fb68afb6085ec3a1d2"),
"details" : [
{
"a" : [
{
"unit" : "08",
"size" : "5",
"pos" : "Far",
"_id" : ObjectId("58e542fb68afb6085ec3a1d6")
}
]
},
{
"b" : [
{
"pos" : "Drive Side Far",
"size" : "5",
"unit" : "08",
"_id" : ObjectId("58e542fb68afb6085ec3a1d3")
}
]
},
{
"c" : [
{
"pos" : "Far",
"size" : "3",
"unit" : "08",
"_id" : ObjectId("58e542fb68afb6085ec3a1d4")
}
]
},
{
"d" : [
{
"pos" : "Far",
"size" : "5",
"unit" : "08",
"_id" : ObjectId("58e542fb68afb6085ec3a1d5")
}
]
}
]
}
]
}
Run Code Online (Sandbox Code Playgroud)
如果您正在尝试"更新"您的数据,那么它比您尝试的更多.你有几个数组,你需要实际"遍历"数组元素,而不是试图直接访问它们.
这里只是一个"打印""扁平化"数据的示例:
db.test.find().forEach(doc => {
doc.tests = doc.tests.map( test => {
test.details.forEach( detail => {
Object.keys(detail).forEach( key => {
detail[key].forEach( item => {
Object.keys(item).forEach( inner => {
if ( inner !== '_id' ) {
test[key + inner.charAt(0).toUpperCase() + inner.substr(1)]
= item[inner];
}
});
});
});
});
delete test.details;
return test;
});
printjson(doc);
})
Run Code Online (Sandbox Code Playgroud)
我相信它给你正在寻找的结构:
{
"_id" : ObjectId("58e574a768afb6085ec3a388"),
"tests" : [
{
"_id" : ObjectId("58e542fb68afb6085ec3a1d2"),
"aUnit" : "08",
"aSize" : "5",
"aPos" : "Far",
"bPos" : "Drive Side Far",
"bSize" : "5",
"bUnit" : "08",
"cPos" : "Far",
"cSize" : "3",
"cUnit" : "08",
"dPos" : "Far",
"dSize" : "5",
"dUnit" : "08"
}
]
}
Run Code Online (Sandbox Code Playgroud)
现在我没有考虑到你的"details"阵列中任何可能"a"出现多次等密钥的文件的可能性.所以我只是考虑到那里只有一个文件里面有一个"a"或一个"b"等等,并且在将新密钥添加到"details"文档的顶层时总是分配与该密钥匹配的最后找到的值.
如果您的实际情况各不相同,那么您需要修改其中的各种.forEach()循环以使用"index"作为参数并将该索引值包含在键名称的一部分中.即:
"a0Unit": "08",
"a0Size": "05",
"a1Unit": "09",
"a1Size": "06"
Run Code Online (Sandbox Code Playgroud)
但这是一个必要的细节,必要时必须解决,因为这与数据在问题中的呈现方式不同.
但是,如果这非常适合您要更新的内容,那么只需运行具有.bulkWrite()定期执行语句的循环:
let ops = [];
db.test.find().forEach(doc => {
doc.tests = doc.tests.map( test => {
test.details.forEach( detail => {
Object.keys(detail).forEach( key => {
detail[key].forEach( item => {
Object.keys(item).forEach( inner => {
if ( inner !== '_id' ) {
test[key + inner.charAt(0).toUpperCase() + inner.substr(1)]
= item[inner];
}
});
});
});
});
delete test.details;
return test;
});
ops = [
...ops,
{ "updateOne": {
"filter": { "_id": doc._id },
"update": { "$set": { "tests": doc.tests } }
}}
];
if ( ops.length >= 500 ) {
db.test.bulkWrite(ops);
ops = [];
}
});
if ( ops.length > 0 ) {
db.test.bulkWrite(ops);
ops = [];
}
Run Code Online (Sandbox Code Playgroud)
它也出现_id在您使用mongoose的每个数组成员文档中的字段中.所以无论你做什么,都不要尝试使用mongoose本身运行代码.这是您的数据的"一次性"批量更新,应该直接从shell运行.当然,您需要修改架构以适应新结构.
但这就是为什么你应该printjson()先用方法在shell中运行你的数据.