集合中的示例文档:
{ "teamAlpha": { }, "teamBeta": { }, "leader_name": "leader" }
对于此类文档,我想删除所有以"team"
开头的字段。所以预期的结果是
{leader_name: "leader"}
我目前正在使用一个功能:
db.teamList.find().forEach(
function(document) {
for(var k in document) {
if (k.startsWith('team')) {
delete document[k];
}
}
db.teamList.save(document);
}
);
我想知道是否有更好的方法解决这个问题。
答案 0 :(得分:0)
它会更好"改为事先确定所有可能的密钥,然后发出单个"multi"
更新以删除所有密钥。根据可用的MongoDB版本,会有不同的方法。
let fields = db.teamList.aggregate([
{ "$project": {
"_id": 0,
"fields": {
"$map": {
"input": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"as": "d",
"cond": { "$eq": [{ "$substrCP": [ "$$d.k", 0, 4 ] }, "team" ] }
}
},
"as": "f",
"in": "$$f.k"
}
}
}},
{ "$unwind": "$fields" },
{ "$group": { "_id": "$fields" } }
])
.map( d => ({ [d._id]: "" }))
.reduce((acc,curr) => Object.assign(acc,curr),{})
db.teamList.updateMany({},{ "$unset": fields });
.aggregate()
语句通过$objectToArray
将文档中的字段转换为数组,然后应用$filter
仅返回"键&#前四个字母的字段34;匹配字符串"team"
。然后使用$unwind
和$group
对其进行处理,以制作一个"唯一列表"匹配字段。
后续指令仅将光标中返回的列表处理为单个对象,如:
{
"teamBeta" : "",
"teamAlpha" : ""
}
然后将其传递给$unset
以从所有文档中删除这些字段。
var fields = db.teamList.mapReduce(
function() {
Object.keys(this).filter( k => /^team/.test(k) )
.forEach( k => emit(k,1) );
},
function() {},
{ "out": { "inline": 1 } }
)
.results.map( d => ({ [d._id]: "" }))
.reduce((acc,curr) => Object.assign(acc,curr),{})
db.teamList.update({},{ "$unset": fields },{ "multi": true });
同样基本的事情,唯一的区别是,.updateMany()
作为一种方法不存在.update()
,我们只需使用"multi"
parameter调用{{3}}来应用全部匹配的文件。这实际上是新的API调用。
为了删除字段而迭代所有文档当然是不明智的,因此上述任何一个都是"首选"做法。唯一可能的失败是构建"不同列表"密钥实际上超过了16MB BSON限制。这是非常极端的,但根据实际数据,这是可能的。
因此基本上有两个扩展"这自然适用于这些技术:
使用"光标"使用.aggregate()
var fields = [];
db.teamList.aggregate([
{ "$project": {
"_id": 0,
"fields": {
"$map": {
"input": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"as": "d",
"cond": { "$eq": [{ "$substrCP": [ "$$d.k", 0, 4 ] }, "team" ] }
}
},
"as": "f",
"in": "$$f.k"
}
}
}},
{ "$unwind": "$fields" },
{ "$group": { "_id": "$fields" } }
]).forEach( d => {
fields.push(d._id);
if ( fields.length >= 2000 ) {
db.teamList.updateMany({},
{ "$unset":
fields.reduce((acc,curr) => Object.assign(acc,{ [curr]: "" }),{})
}
);
}
});
if ( fields.length > 0 ) {
db.teamList.updateMany({},
{ "$unset":
fields.reduce((acc,curr) => Object.assign(acc,{ [curr]: "" }),{})
}
);
}
这基本上是"批次"在"光标"上处理的字段数很多2000年,#34;应该"保留在16MB BSON限制之下作为请求。
使用mapReduce()
db.teamList.mapReduce(
function() {
Object.keys(this).filter( k => /^team/.test(k) )
.forEach( k => emit(k,1) );
},
function() {},
{ "out": { "replace": "tempoutput" } }
);
db.tempoutput.find({},{ "_id": 1 }).forEach(d => {
fields.push(d._id);
if ( fields.length >= 2000 ) {
db.teamList.update({},
{ "$unset":
fields.reduce((acc,curr) => Object.assign(acc,{ [curr]: "" }),{})
},
{ "multi": true }
);
}
});
if ( fields.length > 0 ) {
db.teamList.update({},
{ "$unset":
fields.reduce((acc,curr) => Object.assign(acc,{ [curr]: "" }),{})
},
{ "multi": true }
);
}
除非mapReduce
无法输出到"光标",它再次基本上是相同的过程,您需要输出到仅包含" distinct字段的临时集合名称"然后从该集合中迭代光标,以便在同一批处理#34;方式。
正如类似的初始方法一样,这些是比迭代整个集合并单独调整每个文档更高效的选项。它通常不应该是必要的,因为任何"不同列表的可能性"实际上导致单个更新请求超过16MB确实是极端的。但这将再次成为"首选"处理这种极端情况的方法。
当然,如果您只是知道所有字段名称并且不需要通过检查集合来解决它们,那么只需使用已知名称编写语句:
db.teamList.update({},{ "$unset": { "teamBeta": "", "teamAlpha": "" } },{ "multi": true })
这是完全有效的,因为所有其他陈述正在做的是确定这些名称应该适合你。