如何使用 MongoDB/Mongoose 中的先前值更新字段

Bim*_*Bam 6 mongoose mongodb node.js

例如,我有一些如下所示的文档:

{
    id: 1
    name: "foo"
}
Run Code Online (Sandbox Code Playgroud)

我想将另一个附加string到当前name字段值。

我使用 Mongoose 尝试了以下操作,但没有成功:

Model.findOneAndUpdate({ id: 1 }, { $set: { name: +"bar" } }, ...);
Run Code Online (Sandbox Code Playgroud)

Dan*_*ger 6

编辑:

来自MongoDB 3.6 中的兼容性更改

MongoDB 3.6.1 弃用了快照查询选项。

对于 MMAPv1,请在 { _id: 1} 索引上使用hint(),以防止游标在干预写入操作导致文档移动时多次返回文档。

对于其他存储引擎,请使用hint() 和 { $natural : 1 } 代替。

2017年原答案:

您无法引用要更新的文档的值,因此您将需要一个查询来检索文档,并需要另一个查询来更新它。自 2016 年以来,该州似乎一直有这样的功能请求OPEN

如果您有一个包含如下文档的集合:

{ "_id" : ObjectId("590a4aa8ff1809c94801ecd0"), "name" : "bar" }
Run Code Online (Sandbox Code Playgroud)

使用 MongoDB shell,您可以执行以下操作:

db.test.find({ name: "bar" }).snapshot().forEach((doc) => {
    doc.name = "foo-" + doc.name;

    db.test.save(doc);
});
Run Code Online (Sandbox Code Playgroud)

该文档将按预期更新:

{ "_id" : ObjectId("590a4aa8ff1809c94801ecd0"), "name": "foo-bar" }
Run Code Online (Sandbox Code Playgroud)

请注意.snapshot()通话。这可确保查询不会多次返回文档,因为干预写入操作会因文档大小的增长而移动该文档。

将其应用到您的 Mongoose 示例中,如官方示例中所述:

Cat.findById(1, (err, cat) => {
    if (err) return handleError(err);

    cat.name = cat.name + "bar";

    cat.save((err, updatedCat) => {
        if (err) return handleError(err);

        ...
    });
});
Run Code Online (Sandbox Code Playgroud)

值得一提的是,$concat聚合框架中有一个运算符,但不幸的是您不能在update查询中使用它。

无论如何,根据您需要执行的操作,您可以将其与$out运算符一起使用,将聚合结果保存到新集合中。

使用相同的示例,您将执行以下操作:

db.test.aggregate([{
    $match: { name: "bar" }
}, {
    $project: { name: { $concat: ["foo", "-", "$name"] }}
}, {
    $out: "prefixedTest"
}]);
Run Code Online (Sandbox Code Playgroud)

并且prefixedTest将使用如下所示的文档创建一个新集合:

{ "_id" : ObjectId("XXX"), "name": "foo-bar" }
Run Code Online (Sandbox Code Playgroud)

作为参考,关于同一主题还有另一个有趣的问题,其中有一些值得一读的答案:Update MongoDB field using value of another field