在数组中插入一个嵌套对象

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)
    }
}

现在性能改进的数量取决于您的“命中率”,我认为对于大多数流程来说,它应该很高,这意味着它会很重要。 “未命中”的性能不受影响,因为如果不存在匹配文档,更新“行为”与查找相同。