也许是时候了,也许是我淹没在稀疏的文档中,而且无法绕过Mongoose更新的概念:)
这是交易:
我有一个联系方案和模型(缩短属性):
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);
mongoose.model('Contact', ContactSchema); //is this line superflous??
var Contact = mongoose.model('Contact', ContactSchema);
我收到客户的请求,其中包含我需要的字段,因此使用我的模型:
mongoose.connect(connectionString);
var contact = new Contact({
phone: request.phone,
status: request.status
});
现在我们解决了这个问题:
-
如果我拨打
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
属性 .
毕竟我尝试了底线:给定一个文件 contact
,如果它存在,如何更新它,如果不存在则如何添加?
谢谢你的时间 .
23 回答
一段时间后我才回到这个问题,并决定根据Aaron Mast的答案发布一个插件 .
https://www.npmjs.com/package/mongoose-recursive-upsert
将它用作mongoose插件 . 它设置一个静态方法,它将递归合并传入的对象 .
这是在mongoose中解决更新方法的更好方法,您可以查看Scotch.io以获取更多详细信息 . 这对我来说绝对有用!!!
使用Promises链可以实现非常优雅的解决方案:
我刚刚烧了3个小时试图解决同样的问题 . 具体来说,我希望“替换”整个文档(如果存在),或者以其他方式插入 . 这是解决方案:
我创建an issue on the Mongoose project page请求将有关此信息添加到文档中 .
好吧,我等了很久没有回答 . 最后放弃了整个更新/ upsert方法,并采用:
它有用吗?是的 . 我对此感到满意吗?可能不是 . 2个DB调用而不是一个 .
希望未来的Mongoose实现能够提供
Model.upsert
功能 .没有其他解决方案适合我 . 我正在使用发布请求并更新数据,如果找到其他插入它,也_id与需要删除的请求正文一起发送 .
对于到这里的人来说仍然在寻找一个好的解决方案来支持“挂钩”,这是我测试和工作的 . 它仍然需要2个DB调用,但比我在一次调用中尝试过的任何东西都稳定得多 .
您可以使用此更新记录,并获取更新的数据作为响应
这是创建/更新的最简单方法,同时还调用中间件和验证器 .
Mongoose现在支持findOneAndUpdate本地支持(调用MongoDB findAndModify) .
如果对象不存在,则upsert = true选项会创建该对象 . 默认为false .
编辑:Mongoose不支持使用此方法的这些挂钩:
默认值
二传手
验证器
中间件
阅读上面的帖子后,我决定使用这段代码:
如果r为null,我们创建新项目 . 否则,请在更新中使用upsert,因为update不会创建新项 .
我创建了一个StackOverflow帐户来回答这个问题 . 在无果而终地搜索互联网之后,我只是自己写了一些东西 . 这就是我这样做的方式,因此它可以应用于任何猫鼬模型 . 导入此功能或将其直接添加到您正在进行更新的代码中 .
然后要插入一个猫鼬文件,请执行以下操作,
此解决方案可能需要2个DB呼叫,但您确实可以获得以下优势:
针对您的模型的架构验证,因为您正在使用.save()
您可以在更新调用中插入深层嵌套对象而无需手动枚举,因此如果您的模型发生更改,则无需担心更新代码
请记住,即使源具有现有值,目标对象也将始终覆盖源
此外,对于数组,如果现有对象的数组长于替换它的数组,则旧数组末尾的值将保留 . 一个简单的方法来upsert整个数组是在upsert之前将旧数组设置为空数组,如果这是你想要做的事情 .
更新 - 01/16/2016我添加了一个额外的条件,如果有一个原始值数组,Mongoose没有意识到数组变得更新而不使用“set”函数 .
这对我有用 .
如果发电机可用,它会变得更加容易:
2.6中引入了一个bug,并且影响到2.7好
upsert曾经在2.4上正常工作
https://groups.google.com/forum/#!topic/mongodb-user/UcKvx4p4hnY https://jira.mongodb.org/browse/SERVER-13843
看一看,它包含一些重要信息
更新:
它并不意味着upsert不起作用 . 这是一个如何使用它的一个很好的例子:
你很亲近
但是你的第二个参数应该是一个带有修改运算符的对象
以Martin Kuzdowicz上面发布的内容为基础 . 我使用以下内容使用mongoose和json对象的深度合并进行更新 . 与mongoose中的model.save()函数一起,这允许mongoose进行完全验证,即使是依赖于json中的其他值的mongoose . 它确实需要deepmerge包https://www.npmjs.com/package/deepmerge . 但这是一个非常轻量级的包装 .
这个coffeescript适用于我的Node - 诀窍是_id get在客户端发送和返回时被剥夺了ObjectID包装,因此需要替换更新(当没有提供_id时,save将恢复为插入和添加一) .
按照Traveling Tech Guy的答案,已经很棒了,我们可以创建一个插件,并在初始化后将它附加到mongoose,以便
.upsert()
将在所有型号上可用 .plugins.js
db.js
然后你可以随时做
User.upsert({ _id: 1 }, { foo: 'bar' })
或YouModel.upsert({ bar: 'foo' }, { value: 1 })
之类的事情 .我需要将文档更新/插入到一个集合中,我所做的是创建一个像这样的新对象文字:
我从我的数据库中的其他地方获取的数据组成,然后在模型上调用update
这是我第一次运行脚本后得到的输出:
这是我第二次运行脚本时的输出:
我正在使用mongoose版本3.6.16