Mongoose:findOneAndUpdate不返回更新后的文档

427

以下是我的代码

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

var Cat = mongoose.model('Cat', {
    name: String,
    age: {type: Number, default: 20},
    create: {type: Date, default: Date.now} 
});

Cat.findOneAndUpdate({age: 17}, {$set:{name:"Naomi"}},function(err, doc){
    if(err){
        console.log("Something wrong when updating data!");
    }

    console.log(doc);
});

我已经在我的Mongo数据库中有一些记录,并且我想运行这段代码来更新年龄为17岁的人的姓名,然后在代码的最后打印结果。

但是,为什么我从控制台得到的结果仍然与原始数据相同(不是修改后的姓名),但是当我进入Mongo DB命令行并输入 "db.cats.find();" 时,结果会显示修改后的姓名。

然后我再次运行这段代码,结果就被修改了。

我的问题是:如果数据已被修改,那么为什么我第一次使用console.log时仍然得到原始数据。


使用{new:true}似乎不能和updateOne一起正常工作,将updateOne替换为findOneAndUpdate后就可以了。 - Kritish Bhattarai
16个回答

762

为什么会发生这种情况?

默认情况下,返回的是原始未更改的文档。如果您想要返回新的更新后的文档,您需要传递一个附加的参数:一个将new属性设置为true的对象。

根据mongoose文档

Query#findOneAndUpdate

Model.findOneAndUpdate(conditions, update, options, (error, doc) => {
  // error: any errors that occurred
  // doc: the document before updates are applied if `new: false`, or after updates if `new = true`
});

可用选项

  • new: bool - 如果设置为true,则返回修改后的文档而不是原始文档。 默认为false(在4.0中更改)

解决方案

如果您希望在doc变量中获得更新后的结果,请传递{new: true}

//                                                         V--- THIS WAS ADDED
Cat.findOneAndUpdate({age: 17}, {$set:{name:"Naomi"}}, {new: true}, (err, doc) => {
    if (err) {
        console.log("Something wrong when updating data!");
    }

    console.log(doc);
});

31
对我来说好像出了问题,即使加上 new: true 参数也会返回旧文档。 - PDN
5
我很明白,因为你已经可以访问新文档了。 - danday74
4
它对我有用,我正在使用moogose版本4.6.3,谢谢。 - cesar andavisa
1
我得到了新文档,但 _id 为空,所以它并不是真正的新文档。 - chovy
7
Node.js的MongoDB原生驱动使用- { returnOriginal: false } - Nick Grealy
显示剩余8条评论

154

如果您使用的是Node.js驱动程序而不是Mongoose,则需要使用{returnOriginal:false}而不是{new:true}

2021 - Mongodb ^4.2.0 更新
{ returnDocument: 'after' }


2
谢谢!这对我有效,mongodb node 版本为2.2.27。 - Kevin Ng
27
这个 API 有点蠢。为什么不能像原生 API 一样使用相同的签名?为什么默认不返回更新后的文档呢?Mongoose 是我每天使用中最让人恼火的库之一。 - Askdesigners
8
我正在使用 "mongodb": "^3.6.9",而 { returnOriginal:false } 已经被弃用了。请改用 { returnDocument: 'after' } - kunthet
谢谢!这对我有用。@kunthet,实际上我正在使用这个,因为文档解释说建议的 { returnOriginal: false } 已经过时了。但出于某种原因,我无法让它工作,而使用建议时则按预期工作。 - Daniel

101

因此,“findOneAndUpdate”需要一个选项来返回原始文档。而该选项是:

MongoDB shell

{returnNewDocument: true}

参考资料:https://docs.mongodb.com/manual/reference/method/db.collection.findOneAndUpdate/

Mongoose

{new: true}

参考资料:http://mongoosejs.com/docs/api.html#query_Query-findOneAndUpdate

Node.js MongoDB Driver API:

{returnOriginal: false}

2021 - Mongodb ^4.2.0 更新
{ returnDocument: 'after' }

参考资料:http://mongodb.github.io/node-mongodb-native/3.0/api/Collection.html#findOneAndUpdate


1
Laravel: 'returnDocument' => FindOneAndUpdate::RETURN_DOCUMENT_AFTER Laravel:'returnDocument' => FindOneAndUpdate::RETURN_DOCUMENT_AFTER - Giacomo Alzetta

47
默认情况下,findOneAndUpdate返回原始文档。如果要返回修改后的文档,请将选项对象 { new: true } 传递给函数:
Cat.findOneAndUpdate({ age: 17 }, { $set: { name: "Naomi" } }, { new: true }, function(err, doc) {

});

2
为什么 _id 是 null? - chovy

19

我写错了,应该是new而不是returnNewDocument。谢谢帮忙! - user1111527
你如何在没有async的情况下使用await? - PeterXX
1
你不能@PeterXX,那只是一段代码片段。 - Abdullah Oladipo

17

对于那些使用ES6 / ES7样式和原生Promise的人,这里有一个你可以采用的模式...

const user = { id: 1, name: "Fart Face 3rd"};
const userUpdate = { name: "Pizza Face" };

try {
    user = await new Promise( ( resolve, reject ) => {
        User.update( { _id: user.id }, userUpdate, { upsert: true, new: true }, ( error, obj ) => {
            if( error ) {
                console.error( JSON.stringify( error ) );
                return reject( error );
            }

            resolve( obj );
        });
    })
} catch( error ) { /* set the world on fire */ }

19
如果您没有提供回调函数,Mongoose将返回一个Promise。您不需要创建自己的Promise! - joeytwiddle
2
@joeytwiddle 如果您没有提供回调函数,Mongoose 不会返回Promise。相反,它会返回一个查询对象,该对象仅提供Promise API的一小部分子集。这是根据Mongoose文档的规定。 - Jamie Ridding

13

这是 findOneAndUpdate 的更新后的代码。它可以正常工作。

db.collection.findOneAndUpdate(    
  { age: 17 },      
  { $set: { name: "Naomi" } },      
  {
     returnNewDocument: true
  }    
)

6
如果您想返回更改后的文档,需要设置选项{new:true}。在API参考中,您可以使用Cat.findOneAndUpdate(conditions, update, options, callback) // executes
从官方Mongoose APIhttp://mongoosejs.com/docs/api.html#findoneandupdate_findOneAndUpdate中获取,您可以使用以下参数。
A.findOneAndUpdate(conditions, update, options, callback) // executes
A.findOneAndUpdate(conditions, update, options)  // returns Query
A.findOneAndUpdate(conditions, update, callback) // executes
A.findOneAndUpdate(conditions, update)           // returns Query
A.findOneAndUpdate()                             // returns Query

除了官方API页面没有提到的另一种实现方式,我更喜欢使用基于Promise的实现方式,它允许您在.catch中处理所有各种错误。

    let cat: catInterface = {
        name: "Naomi"
    };

    Cat.findOneAndUpdate({age:17}, cat,{new: true}).then((data) =>{
        if(data === null){
            throw new Error('Cat Not Found');
        }
        res.json({ message: 'Cat updated!' })
        console.log("New cat data", data);
    }).catch( (error) => {
        /*
            Deal with all your errors here with your preferred error handle middleware / method
         */
        res.status(500).json({ message: 'Some Error!' })
        console.log(error);
    });

6

我知道,我已经晚了,但让我在这里添加我的简单且有效的答案。

const query = {} //your query here
const update = {} //your update in json here
const option = {new: true} //will return updated document

const user = await User.findOneAndUpdate(query , update, option)

4
以下是mongoose的findOneAndUpdate查询示例。在此示例中,使用了new: true以获取更新后的文档,使用fields来获取特定字段。
例如:findOneAndUpdate(条件, 更新内容, 选项, 回调函数)
await User.findOneAndUpdate({
      "_id": data.id,
    }, { $set: { name: "Amar", designation: "Software Developer" } }, {
      new: true,
      fields: {
        'name': 1,
        'designation': 1
      }
    }).exec();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接