Javascript 如何在 Mongoose 中更新/插入文档?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7267102/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I update/upsert a document in Mongoose?
提问by Traveling Tech Guy
Perhaps it's the time, perhaps it's me drowning in sparse documentation and not being able to wrap my head around the concept of updating in Mongoose :)
也许是时候了,也许是我淹没在稀疏的文档中,无法理解 Mongoose 中更新的概念:)
Here's the deal:
这是交易:
I have a contact schema and model (shortened properties):
我有一个联系模式和模型(缩短的属性):
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var mongooseTypes = require("mongoose-types"),
useTimestamps = mongooseTypes.useTimestamps;
var ContactSchema = new Schema({
phone: {
type: String,
index: {
unique: true,
dropDups: true
}
},
status: {
type: String,
lowercase: true,
trim: true,
default: 'on'
}
});
ContactSchema.plugin(useTimestamps);
var Contact = mongoose.model('Contact', ContactSchema);
I receive a request from the client, containing the fields I need and use my model thusly:
我收到来自客户的请求,其中包含我需要的字段并因此使用我的模型:
mongoose.connect(connectionString);
var contact = new Contact({
phone: request.phone,
status: request.status
});
And now we reach the problem:
现在我们遇到了问题:
- If I call
contact.save(function(err){...})
I'll receive an error if the contact with the same phone number already exists (as expected - unique) - I can't call
update()
on contact, since that method does not exist on a document - If I call update on the model:
Contact.update({phone:request.phone}, contact, {upsert: true}, function(err{...})
I get into an infinite loop of some sorts, since the Mongoose update implementation clearly doesn't want an object as the second parameter. - If I do the same, but in the second parameter I pass an associative array of the request properties
{status: request.status, phone: request.phone ...}
it works - but then I have no reference to the specific contact and cannot find out itscreatedAt
andupdatedAt
properties.
- 如果我打电话,
contact.save(function(err){...})
如果具有相同电话号码的联系人已经存在(如预期 - 唯一),我将收到错误消息 - 我无法
update()
联系,因为文档中不存在该方法 - 如果我对模型调用 update:
Contact.update({phone:request.phone}, contact, {upsert: true}, function(err{...})
我会进入某种无限循环,因为 Mongoose 更新实现显然不希望将对象作为第二个参数。 - 如果我也这样做,但在第二个参数中,我传递了一个
{status: request.status, phone: request.phone ...}
它工作的请求属性的关联数组- 但是我没有对特定联系人的引用,也无法找到它的createdAt
和updatedAt
属性。
So the bottom line, after all I tried: given a document contact
, how do I update it if it exists, or add it if it doesn't?
所以最重要的是,毕竟我尝试过:给定一个文档contact
,如果它存在,我该如何更新它,或者如果它不存在,我如何添加它?
Thanks for your time.
谢谢你的时间。
采纳答案by Traveling Tech Guy
Well, I waited long enough and no answer. Finally gave up the whole update/upsert approach and went with:
好吧,我等了很长时间,没有回答。最终放弃了整个更新/upsert方法并采用:
ContactSchema.findOne({phone: request.phone}, function(err, contact) {
if(!err) {
if(!contact) {
contact = new ContactSchema();
contact.phone = request.phone;
}
contact.status = request.status;
contact.save(function(err) {
if(!err) {
console.log("contact " + contact.phone + " created at " + contact.createdAt + " updated at " + contact.updatedAt);
}
else {
console.log("Error: could not save contact " + contact.phone);
}
});
}
});
Does it work? Yep. Am I happy with this? Probably not. 2 DB calls instead of one.
Hopefully a future Mongoose implementation would come up with a Model.upsert
function.
它有效吗?是的。我对此满意吗?可能不是。2 个 DB 调用而不是一个。
希望未来的 Mongoose 实现能够提供一个Model.upsert
功能。
回答by Pascalius
Mongoose now supports this natively with findOneAndUpdate(calls MongoDB findAndModify).
Mongoose 现在通过findOneAndUpdate(调用 MongoDB findAndModify)原生支持这一点。
The upsert = true option creates the object if it doesn't exist. defaults to false.
如果对象不存在, upsert = true 选项会创建该对象。默认为 false。
var query = {'username': req.user.username};
req.newData.username = req.user.username;
MyModel.findOneAndUpdate(query, req.newData, {upsert: true}, function(err, doc) {
if (err) return res.send(500, {error: err});
return res.send('Succesfully saved.');
});
In older versions Mongoose does not support these hooks with this method:
在旧版本中,Mongoose 不支持使用此方法的这些钩子:
- defaults
- setters
- validators
- middleware
- 默认值
- 二传手
- 验证器
- 中间件
回答by Clint Harris
I just burned a solid 3 hours trying to solve the same problem. Specifically, I wanted to "replace" the entire document if it exists, or insert it otherwise. Here's the solution:
我刚刚花了 3 个小时试图解决同样的问题。具体来说,我想“替换”整个文档(如果存在),或者以其他方式插入。这是解决方案:
var contact = new Contact({
phone: request.phone,
status: request.status
});
// Convert the Model instance to a simple object using Model's 'toObject' function
// to prevent weirdness like infinite looping...
var upsertData = contact.toObject();
// Delete the _id property, otherwise Mongo will return a "Mod on _id not allowed" error
delete upsertData._id;
// Do the upsert, which works like this: If no Contact document exists with
// _id = contact.id, then create a new doc using upsertData.
// Otherwise, update the existing doc with upsertData
Contact.update({_id: contact.id}, upsertData, {upsert: true}, function(err{...});
I created an issue on the Mongoose project pagerequesting that info about this be added to the docs.
我在Mongoose 项目页面上创建了一个问题,要求将有关此的信息添加到文档中。
回答by chrixian
You were close with
你很亲近
Contact.update({phone:request.phone}, contact, {upsert: true}, function(err){...})
but your second parameter should be an object with a modification operator for example
但是你的第二个参数应该是一个带有修改运算符的对象,例如
Contact.update({phone:request.phone}, {$set: { phone: request.phone }}, {upsert: true}, function(err){...})
回答by Martin Kuzdowicz
Very elegant solution you can achieve by using chain of Promises:
您可以通过使用承诺链来实现非常优雅的解决方案:
app.put('url', (req, res) => {
const modelId = req.body.model_id;
const newName = req.body.name;
MyModel.findById(modelId).then((model) => {
return Object.assign(model, {name: newName});
}).then((model) => {
return model.save();
}).then((updatedModel) => {
res.json({
msg: 'model updated',
updatedModel
});
}).catch((err) => {
res.send(err);
});
});
回答by Aaron Mast
I created a StackOverflow account JUST to answer this question. After fruitlessly searching the interwebs I just wrote something myself. This is how I did it so it can be applied to any mongoose model. Either import this function or add it directly into your code where you are doing the updating.
我创建了一个 StackOverflow 帐户只是为了回答这个问题。在网上搜索无果后,我只是自己写了一些东西。我就是这样做的,因此它可以应用于任何猫鼬模型。导入此函数或将其直接添加到您进行更新的代码中。
function upsertObject (src, dest) {
function recursiveFunc (src, dest) {
_.forOwn(src, function (value, key) {
if(_.isObject(value) && _.keys(value).length !== 0) {
dest[key] = dest[key] || {};
recursiveFunc(src[key], dest[key])
} else if (_.isArray(src) && !_.isObject(src[key])) {
dest.set(key, value);
} else {
dest[key] = value;
}
});
}
recursiveFunc(src, dest);
return dest;
}
Then to upsert a mongoose document do the following,
然后要更新 mongoose 文档,请执行以下操作,
YourModel.upsert = function (id, newData, callBack) {
this.findById(id, function (err, oldData) {
if(err) {
callBack(err);
} else {
upsertObject(newData, oldData).save(callBack);
}
});
};
This solution may require 2 DB calls however you do get the benefit of,
此解决方案可能需要 2 个 DB 调用,但您确实可以从中受益,
- Schema validation against your model because you are using .save()
- You can upsert deeply nested objects without manual enumeration in your update call, so if your model changes you do not have to worry about updating your code
- 对您的模型进行模式验证,因为您使用的是 .save()
- 您无需在更新调用中手动枚举即可插入深度嵌套的对象,因此如果您的模型发生更改,您不必担心更新您的代码
Just remember that the destination object will always override the source even if the source has an existing value
请记住,即使源具有现有值,目标对象也将始终覆盖源
Also, for arrays, if the existing object has a longer array than the one replacing it then the values at the end of the old array will remain. An easy way to upsert the entire array is to set the old array to be an empty array before the upsert if that is what you are intending on doing.
此外,对于数组,如果现有对象的数组比替换它的数组长,则旧数组末尾的值将保留。向上插入整个数组的一种简单方法是在向上插入之前将旧数组设置为空数组,如果这是您打算执行的操作。
UPDATE - 01/16/2016 I added an extra condition for if there is an array of primitive values, Mongoose does not realize the array becomes updated without using the "set" function.
更新 - 2016 年 1 月 16 日我添加了一个额外条件,如果存在原始值数组,Mongoose 不会在不使用“set”函数的情况下实现数组更新。
回答by vkarpov15
I'm the maintainer of Mongoose. The more modern way to upsert a doc is to use the Model.updateOne()
function.
我是猫鼬的维护者。更新插入文档的更现代方法是使用Model.updateOne()
函数.
await Contact.updateOne({
phone: request.phone
}, { status: request.status }, { upsert: true });
If you need the upserted doc, you can use Model.findOneAndUpdate()
如果您需要更新的文档,您可以使用 Model.findOneAndUpdate()
const doc = await Contact.findOneAndUpdate({
phone: request.phone
}, { status: request.status }, { upsert: true });
The key takeaway is that you need to put the unique properties in the filter
parameter to updateOne()
or findOneAndUpdate()
, and the other properties in the update
parameter.
关键点是您需要将filter
参数中的唯一属性放入updateOne()
or findOneAndUpdate()
,并将其他属性放入update
参数中。
Here's a tutorial on upserting documents with Mongoose.
这是有关使用 Mongoose 插入文档的教程。
回答by andres_gcarmona
I needed to update/upsert a document into one collection, what I did was to create a new object literal like this:
我需要将文档更新/插入到一个集合中,我所做的是创建一个新的对象文字,如下所示:
notificationObject = {
user_id: user.user_id,
feed: {
feed_id: feed.feed_id,
channel_id: feed.channel_id,
feed_title: ''
}
};
composed from data that I get from somewhere else in my database and then call update on the Model
由我从数据库中其他地方获取的数据组成,然后在模型上调用更新
Notification.update(notificationObject, notificationObject, {upsert: true}, function(err, num, n){
if(err){
throw err;
}
console.log(num, n);
});
this is the ouput that I get after running the script for the first time:
这是我第一次运行脚本后得到的输出:
1 { updatedExisting: false,
upserted: 5289267a861b659b6a00c638,
n: 1,
connectionId: 11,
err: null,
ok: 1 }
And this is the output when I run the script for the second time:
这是我第二次运行脚本时的输出:
1 { updatedExisting: true, n: 1, connectionId: 18, err: null, ok: 1 }
I'm using mongoose version 3.6.16
我正在使用猫鼬版本 3.6.16
回答by Eyo Okon Eyo
app.put('url', function(req, res) {
// use our bear model to find the bear we want
Bear.findById(req.params.bear_id, function(err, bear) {
if (err)
res.send(err);
bear.name = req.body.name; // update the bears info
// save the bear
bear.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Bear updated!' });
});
});
});
Here is a better approach to solving the update method in mongoose, you can check Scotch.iofor more details. This definitely worked for me!!!
这里有一个更好的方法来解决mongoose 中的更新方法,您可以查看Scotch.io以获取更多详细信息。这绝对对我有用!!!
回答by helpse
There is a bug introduced in 2.6, and affects to 2.7 as well
2.6 中引入了一个错误,也会影响到 2.7
The upsert used to work correctly on 2.4
upsert 用于在 2.4 上正常工作
https://groups.google.com/forum/#!topic/mongodb-user/UcKvx4p4hnYhttps://jira.mongodb.org/browse/SERVER-13843
https://groups.google.com/forum/#!topic/mongodb-user/UcKvx4p4hnY https://jira.mongodb.org/browse/SERVER-13843
Take a look, it contains some important info
看一看,里面有一些重要的信息
UPDATED:
更新:
It doesnt mean upsert does not work. Here is a nice example of how to use it:
这并不意味着 upsert 不起作用。这是如何使用它的一个很好的例子:
User.findByIdAndUpdate(userId, {online: true, $setOnInsert: {username: username, friends: []}}, {upsert: true})
.populate('friends')
.exec(function (err, user) {
if (err) throw err;
console.log(user);
// Emit load event
socket.emit('load', user);
});