Node.js 我可以使用pop()获取Mongoose中最后插入的子文档吗?

Node.js 我可以使用pop()获取Mongoose中最后插入的子文档吗?,node.js,mongodb,mongoose,Node.js,Mongodb,Mongoose,我正在执行一个findOneAndUpdate调用,以使用$addToSet添加到子文档数组。该操作返回新更新的文档。我想提取刚刚插入的子文档。在这种情况下使用pop()安全吗?我知道浏览器在数组/对象的排序方式上有所不同,但不确定node/mongo的情况。MongoDB中“集合”的一般官方说法是,它们不被认为是以任何方式排序的。实际上,有一些讨论认为,“集合”甚至可能在某一点上构成与普通数组不同的数据类型,但到目前为止,类型之间没有区别,它们在内部被视为另一个数组 然而,一般的行为似乎是,添

我正在执行一个
findOneAndUpdate
调用,以使用
$addToSet
添加到子文档数组。该操作返回新更新的文档。我想提取刚刚插入的子文档。在这种情况下使用
pop()
安全吗?我知道浏览器在数组/对象的排序方式上有所不同,但不确定node/mongo的情况。

MongoDB中“集合”的一般官方说法是,它们不被认为是以任何方式排序的。实际上,有一些讨论认为,“集合”甚至可能在某一点上构成与普通数组不同的数据类型,但到目前为止,类型之间没有区别,它们在内部被视为另一个数组

然而,一般的行为似乎是,添加到集合中的任何“新”项实际上都是“附加”到当前元素列表中的,因此任何新的项实际上都是数组中的最后一个元素。数组总是保留位置,这是数据类型的一般点

因此,虽然你的观点基本上是问:

“最后添加的项目是否是列表中的最后一项?”

尽管给出了关于元素顺序的警告,但情况似乎是这样的,这通常是从表面值开始的,更多的是关于“内部”顺序,其中在“c”之后添加“a”不会将“a”元素放在列表的第一位,而无需进行额外的修改

但这个问题也有点“滑稽”,因为你基本上是在问:

“我对集合的上次更新是否显示在末尾?”

哪个IMHO似乎真的在问“它是否更新了?”,或者我添加的最后一个成员是否真的更新了“集合”,或者元素是否已经存在。在本例中,与其查看最后一个元素是否与要添加的元素相同,不如检查WriteResult并查看文档是否已实际修改

考虑这份文件:

{
    "list": [ "c", "a" ]
}
Model.findOneAndUpdate(
    {},
    { "$addToSet": { "list": "a" },
    { "new": false },
    function(err,doc) {
        if ( doc.list.indexOf("a") == -1 )
           console.log( "updated set" );
    }
);
然后使用批量操作API发布更新以获得结果:

var bulk = Model.collection("settest").initializeOrderedBulkOp();
bulk.find({}).updateOne({ "$addToSet": { "list": "a" } });
bulk.execute(function(err,result) {
    console.log( JSON.stringify( result, undefined, 4 ) );
});
会给你这样的东西:

BulkWriteResult({
    "writeErrors" : [ ],
    "writeConcernErrors" : [ ],
    "nInserted" : 0,
    "nUpserted" : 0,
    "nMatched" : 1,
    "nModified" : 0,
    "nRemoved" : 0,
    "upserted" : [ ]
})
增强的响应告诉您,即使update语句匹配了
1
文档,但文档本身实际上没有修改任何内容,因为在集合中已经存在元素的地方,操作实际上没有做任何事情

但是,如果您考虑mongoose
.findOneAndUpdate()
方法,该方法源自基本驱动程序的基本
.findanddomify()
方法,那么您得到的响应默认为“修改”文档:

Model.findOneAndUpdate(
    {},
    { "$addToSet": { "list": "a" },
    function(err,doc) {
        console.log( JSON.stringify( doc, undefined, 4 ) );
    }
);
因此,这与以前的文档基本相同,无法判断更新操作是否真的做了任何事情

现在,您可以修改它以请求原始文档,而不是修改后的文档:

Model.findOneAndUpdate(
    {},
    { "$addToSet": { "list": "a" },
    { "new": false },
    function(err,doc) {
        console.log( JSON.stringify( doc, undefined, 4 ) );
    }
);
但是,对于结果,您真正了解的唯一一件事是,如果上次添加的元素不在原始文档中,则会导致更新。当然,它“现在”就在那里,因为您要求它这样做,操作成功并返回了一个文档:

{
    "list": [ "c", "a" ]
}
Model.findOneAndUpdate(
    {},
    { "$addToSet": { "list": "a" },
    { "new": false },
    function(err,doc) {
        if ( doc.list.indexOf("a") == -1 )
           console.log( "updated set" );
    }
);
但是,列表中的“最后一个元素”是添加的吗?如前所述,这似乎是如此,但这是否始终保持不变是个问题。但是这样做真的没有多大意义,因为你不能保证你刚刚做的手术在没有正确检查的情况下确实做了任何事情

因此,在诸如
.findOneAndUpdate()
等返回文档的原子操作中,或者在任何“更新”变体中,“这做了什么”的更好选项是实际检查写入结果或原始文档,使其符合操作的预期结果

最后,如果需要返回整个文档,请使用
.findOneAndUpdate()
,但要使用“原始文档”选项。您可以安全地假设您的操作成功,并将元素添加到最初不存在的列表中(如果这是您想要的结果)。区别告诉你更新是否真的做了什么

但是,如果您只想知道是否确实添加了一个新元素,那么只需使用批量API方法,它会给您一个正确的响应,即文档没有更新,因为该元素已经是“集合”的一部分