在数组中插入一个嵌套对象
Upserting one single nested object inside an array
我需要做的是要么更新唯一一个嵌套对象内的计数器,要么插入包含带有初始化计数器的数组的整个文档。
文档示例:
{
"_id":{"$oid":"61f020f40e2f03f0b93b69f1"},
"v":"xxxxxxxxxxxxxxxx",
"list":[{"p":{"$oid":"61f020f40e2f03f0b93b69f0"},"c":1,"hom":0,"het":1}]
}
所有文档肯定会有一个“列表”数组,其中至少有一个嵌套对象。并且 (v,list.p)
对是唯一的。
所以,从之前的find开始,对于cursor的每一个元素,我都是调用js中的upsert函数,其实就是找到doc,然后不管找到了就插入或者更新collection
function upsert(doc){
_hom = doc.field1.field2 == "hom" ? 1 : 0;
_het = doc.field1.field2 == "het" ? 1 : 0;
filter = {"v": doc.anId, "list.p" : "61f020f40e2f03f0b93b69f0"};
project = {"list.c":1, "list.hom":1, "list.het":1, "_id":0};
res = db.collection.find(filter, project);
if(!res.hasNext()){
doc = {"v": doc.anId, "list" : [{"p" : "61f020f40e2f03f0b93b69f0", "c" : 1, "hom" : _hom, "het" : _het}]}
db.collection.insertOne(doc)
} else {
list = res.next().list[0];
_c = list.c + 1;
_hom += list.hom;
_het += list.het;
update = {$set : {"list" : [{"p" : "61f020f40e2f03f0b93b69f0", "c" : _c, "hom" : _hom, "het" : _het}]}}
db.collection.updateOne(filter, update)
}
}
想象一下:
db.anotherCollection.find({...},{...}).forEach(doc => upsert(doc))
确实有效,但速度很慢。还有其他方法吗?我在网上读到,更新一个数组来做这样的事情是不可能的,因为你要么需要推,拉,基于查询,ecc ...但是我需要更新嵌套对象的现有计数器,如果找到 doc,或者如果找不到文档,则插入整个文档。
Mongo 4.4.6
所以这不能真正在单个数据库调用中完成,但是您可以简化代码并减少总共完成的数据库调用的平均数量。
具体来说,我们可以代替 find -> update
执行更新,如果更新“成功”,那么我们可以 return,否则我们插入。这意味着只要 list
已经存在,您就会有一个不需要的冗余 find
查询。
代码如下:
function upsert(doc){
_hom = doc.field1.field2 == "hom" ? 1 : 0;
_het = doc.field1.field2 == "het" ? 1 : 0;
filter = {"v": doc.anId, "list.p" : "61f020f40e2f03f0b93b69f0"};
update = {$inc : {"list.$.c": 1}}
updateRes = db.collection.updateOne(filter, update)
if(updateRes.matchedCount == 0){
doc = {"v": doc.anId, "list" : [{"p" : "61f020f40e2f03f0b93b69f0", "c" : 1, "hom" : _hom, "het" : _het}]}
db.collection.insertOne(doc)
}
}
现在性能改进的数量取决于您的“命中率”,我认为对于大多数流程来说,它应该很高,这意味着它会很重要。 “未命中”的性能不受影响,因为如果不存在匹配文档,更新“行为”与查找相同。
我需要做的是要么更新唯一一个嵌套对象内的计数器,要么插入包含带有初始化计数器的数组的整个文档。
文档示例:
{
"_id":{"$oid":"61f020f40e2f03f0b93b69f1"},
"v":"xxxxxxxxxxxxxxxx",
"list":[{"p":{"$oid":"61f020f40e2f03f0b93b69f0"},"c":1,"hom":0,"het":1}]
}
所有文档肯定会有一个“列表”数组,其中至少有一个嵌套对象。并且 (v,list.p)
对是唯一的。
所以,从之前的find开始,对于cursor的每一个元素,我都是调用js中的upsert函数,其实就是找到doc,然后不管找到了就插入或者更新collection
function upsert(doc){
_hom = doc.field1.field2 == "hom" ? 1 : 0;
_het = doc.field1.field2 == "het" ? 1 : 0;
filter = {"v": doc.anId, "list.p" : "61f020f40e2f03f0b93b69f0"};
project = {"list.c":1, "list.hom":1, "list.het":1, "_id":0};
res = db.collection.find(filter, project);
if(!res.hasNext()){
doc = {"v": doc.anId, "list" : [{"p" : "61f020f40e2f03f0b93b69f0", "c" : 1, "hom" : _hom, "het" : _het}]}
db.collection.insertOne(doc)
} else {
list = res.next().list[0];
_c = list.c + 1;
_hom += list.hom;
_het += list.het;
update = {$set : {"list" : [{"p" : "61f020f40e2f03f0b93b69f0", "c" : _c, "hom" : _hom, "het" : _het}]}}
db.collection.updateOne(filter, update)
}
}
想象一下:
db.anotherCollection.find({...},{...}).forEach(doc => upsert(doc))
确实有效,但速度很慢。还有其他方法吗?我在网上读到,更新一个数组来做这样的事情是不可能的,因为你要么需要推,拉,基于查询,ecc ...但是我需要更新嵌套对象的现有计数器,如果找到 doc,或者如果找不到文档,则插入整个文档。
Mongo 4.4.6
所以这不能真正在单个数据库调用中完成,但是您可以简化代码并减少总共完成的数据库调用的平均数量。
具体来说,我们可以代替 find -> update
执行更新,如果更新“成功”,那么我们可以 return,否则我们插入。这意味着只要 list
已经存在,您就会有一个不需要的冗余 find
查询。
代码如下:
function upsert(doc){
_hom = doc.field1.field2 == "hom" ? 1 : 0;
_het = doc.field1.field2 == "het" ? 1 : 0;
filter = {"v": doc.anId, "list.p" : "61f020f40e2f03f0b93b69f0"};
update = {$inc : {"list.$.c": 1}}
updateRes = db.collection.updateOne(filter, update)
if(updateRes.matchedCount == 0){
doc = {"v": doc.anId, "list" : [{"p" : "61f020f40e2f03f0b93b69f0", "c" : 1, "hom" : _hom, "het" : _het}]}
db.collection.insertOne(doc)
}
}
现在性能改进的数量取决于您的“命中率”,我认为对于大多数流程来说,它应该很高,这意味着它会很重要。 “未命中”的性能不受影响,因为如果不存在匹配文档,更新“行为”与查找相同。