Javascript 如何在Mongoose中更新/升级文档?
也许是时候了,也许是因为我淹没在稀疏的文档中,无法理解Mongoose中更新的概念:) 交易如下: 我有一个联系人模式和模型(简称属性): 我收到客户的请求,其中包含我需要的字段并使用我的模型:Javascript 如何在Mongoose中更新/升级文档?,javascript,mongodb,node.js,mongoose,Javascript,Mongodb,Node.js,Mongoose,也许是时候了,也许是因为我淹没在稀疏的文档中,无法理解Mongoose中更新的概念:) 交易如下: 我有一个联系人模式和模型(简称属性): 我收到客户的请求,其中包含我需要的字段并使用我的模型: mongoose.connect(connectionString); var contact = new Contact({ phone: request.phone, status: request.status }); 现在我们来讨论这个问题: 如果我调用contact.save
mongoose.connect(connectionString);
var contact = new Contact({
phone: request.phone,
status: request.status
});
现在我们来讨论这个问题:
contact.save(函数(err){…})
update()
,因为文档中不存在该方法Contact.update({phone:request.phone},Contact,{upsert:true},函数(err{…})
我进入了某种类型的无限循环,因为Mongoose更新实现显然不希望对象作为第二个参数
{status:request.status,phone:request.phone…}
它可以工作-但是我没有特定联系人的引用,并且无法找到它的createdAt
和updatedAt
属性联系人
,如果它存在,我该如何更新它,如果它不存在,我该如何添加它
谢谢您的时间。好吧,我等得够久了,没有回答。最后,我放弃了整个更新/升级方法,选择了:
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);
}
});
}
});
它有效吗?是的。我对此满意吗?可能不行。2 DB呼叫而不是1 DB呼叫。希望未来的Mongoose实现能够提供一个
模型.upsert
函数。您已经接近了
Contact.update({phone:request.phone}, contact, {upsert: true}, function(err){...})
但是第二个参数应该是一个带有修改操作符的对象
Contact.update({phone:request.phone}, {$set: { phone: request.phone }}, {upsert: true}, function(err){...})
为了解决同样的问题,我花了整整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{...});
我创建了一个请求,请求将有关此的信息添加到文档中。在阅读了上述帖子后,我决定使用以下代码:
itemModel.findOne({'pid':obj.pid},function(e,r){
if(r!=null)
{
itemModel.update({'pid':obj.pid},obj,{upsert:true},cb);
}
else
{
var item=new itemModel(obj);
item.save(cb);
}
});
如果r为null,我们将创建新项。否则,在更新中使用upsert,因为更新不会创建新项。我需要更新/upsert文档到一个集合中,我所做的是创建一个新的对象文本,如下所示:
notificationObject = {
user_id: user.user_id,
feed: {
feed_id: feed.feed_id,
channel_id: feed.channel_id,
feed_title: ''
}
};
由我从数据库中其他地方获得的数据组成,然后调用模型更新
Notification.update(notificationObject, notificationObject, {upsert: true}, function(err, num, n){
if(err){
throw err;
}
console.log(num, n);
});
这是我第一次运行脚本后得到的输出:
1 { updatedExisting: false,
upserted: 5289267a861b659b6a00c638,
n: 1,
connectionId: 11,
err: null,
ok: 1 }
这是我第二次运行脚本时的输出:
1 { updatedExisting: true, n: 1, connectionId: 18, err: null, ok: 1 }
我使用的是mongoose版本3.6.16这个coffeescript适用于我的Node-诀窍是当从客户端发送和返回时,会将_idget从其ObjectID包装中剥离,因此需要将其替换为更新(当没有提供_id时,save将恢复为insert并添加一个)
2.6中引入了一个bug,也影响到了2.7 upsert用于在2.4上正常工作 看一看,它包含了一些重要的信息 更新: 这并不意味着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);
});
Mongoose现在通过(称为MongoDB)本地支持这一点 如果对象不存在,upsert=true选项将创建该对象。默认值为false
在旧版本中,Mongoose不支持使用此方法的这些挂钩:
- 默认值
- 二传手
- 验证器
- 中间件
// Create or update a Person by unique email.
// @param person - a new or existing Person
function savePerson(person, done) {
var fieldsToUpdate = ['name', 'phone', 'address'];
Person.findOne({
email: person.email
}, function(err, toUpdate) {
if (err) {
done(err);
}
if (toUpdate) {
// Mongoose object have extra properties, we can either omit those props
// or specify which ones we want to update. I chose to update the ones I know exist
// to avoid breaking things if Mongoose objects change in the future.
_.merge(toUpdate, _.pick(person, fieldsToUpdate));
} else {
toUpdate = person;
}
toUpdate.save(function(err, updated, numberAffected) {
if (err) {
done(err);
}
done(null, updated, numberAffected);
});
});
}
我创建了一个StackOverflow帐户只是为了回答这个问题。在毫无结果地搜索互联网站后,我自己写了一些东西。我就是这样做的,这样它就可以应用于任何mongoose模型。导入此函数或将其直接添加到您正在进行更新的代码中
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;
}
然后,要插入mongoose文档,请执行以下操作:
YourModel.upsert = function (id, newData, callBack) {
this.findById(id, function (err, oldData) {
if(err) {
callBack(err);
} else {
upsertObject(newData, oldData).save(callBack);
}
});
};
此解决方案可能需要2 DB呼叫,但您确实可以从中受益
- 针对您的模型进行架构验证,因为您正在使用.save()
- 您可以在更新调用中取消插入深度嵌套的对象,而无需手动枚举,因此,如果您的模型发生更改,您就不必担心更新代码
我添加了一个额外的条件,如果有一个原语值数组,Mongoose不会意识到数组在不使用“set”功能的情况下会被更新。如果生成器可用,它会变得更容易:
var query = {'username':this.req.user.username};
this.req.newData.username = this.req.user.username;
this.body = yield MyModel.findOneAndUpdate(query, this.req.newData).exec();
这里有一个更好的方法来解决mongoose中的更新方法,您可以查看更多详细信息。这对我来说绝对有效!!!使用承诺链可以实现非常优雅的解决方案:
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);
});
});
过了一会儿,我又回到这个问题上,决定根据Aaron Mast的答案发布一个插件 将其用作mongoose插件。它设置一个静态方法,递归合并传入的对象
Model.upsert({unique: 'value'}, updateObject});
以Martin Kuzdowicz在上面发布的内容为基础。我使用以下内容来使用mongoose和json对象的深度合并进行更新。与mongoose中的model.save()函数一起,这允许mongoose进行完全验证,即使是依赖于json中其他值的验证。它不需要
YourModel.upsert = function (id, newData, callBack) {
this.findById(id, function (err, oldData) {
if(err) {
callBack(err);
} else {
upsertObject(newData, oldData).save(callBack);
}
});
};
var query = {'username':this.req.user.username};
this.req.newData.username = this.req.user.username;
this.body = yield MyModel.findOneAndUpdate(query, this.req.newData).exec();
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!' });
});
});
});
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);
});
});
Model.upsert({unique: 'value'}, updateObject});
var merge = require('deepmerge');
app.put('url', (req, res) => {
const modelId = req.body.model_id;
MyModel.findById(modelId).then((model) => {
return Object.assign(model, merge(model.toObject(), req.body));
}).then((model) => {
return model.save();
}).then((updatedModel) => {
res.json({
msg: 'model updated',
updatedModel
});
}).catch((err) => {
res.send(err);
});
});
Contact.findOne({ phone: request.phone }, (err, doc) => {
const contact = (doc) ? doc.set(request) : new Contact(request);
contact.save((saveErr, savedContact) => {
if (saveErr) throw saveErr;
console.log(savedContact);
});
})
User.findByIdAndUpdate(req.param('userId'), req.body, (err, user) => {
if(err) return res.json(err);
res.json({ success: true });
});
router.post('/user/createOrUpdate', function(req,res){
var request_data = req.body;
var userModel = new User(request_data);
var upsertData = userModel.toObject();
delete upsertData._id;
var currentUserId;
if (request_data._id || request_data._id !== '') {
currentUserId = new mongoose.mongo.ObjectId(request_data._id);
} else {
currentUserId = new mongoose.mongo.ObjectId();
}
User.update({_id: currentUserId}, upsertData, {upsert: true},
function (err) {
if (err) throw err;
}
);
res.redirect('/home');
});
export default (schema, options) => {
schema.statics.upsert = async function(query, data) {
let record = await this.findOne(query)
if (!record) {
record = new this(data)
} else {
Object.keys(data).forEach(k => {
record[k] = data[k]
})
}
return await record.save()
}
}
import mongoose from 'mongoose'
import Plugins from './plugins'
mongoose.connect({ ... })
mongoose.plugin(Plugins)
export default mongoose
router.patch('/:id', (req, res, next) => {
const id = req.params.id;
Product.findByIdAndUpdate(id, req.body, {
new: true
},
function(err, model) {
if (!err) {
res.status(201).json({
data: model
});
} else {
res.status(500).json({
message: "not found any relative data"
})
}
});
});