(mongoose/promises) 如何检查文档是不是是使用 findOneAndUpdate 和 upsert 创建的
Posted
技术标签:
【中文标题】(mongoose/promises) 如何检查文档是不是是使用 findOneAndUpdate 和 upsert 创建的【英文标题】:(mongoose/promises) How do you check if document was created using findOneAndUpdate with upsert(mongoose/promises) 如何检查文档是否是使用 findOneAndUpdate 和 upsert 创建的 【发布时间】:2015-11-23 18:07:37 【问题描述】:考虑一下这段代码,我需要在其中创建或更新特定文档。
Inbox.model.findOneAndUpdate( number: req.phone.number ,
number: req.phone.number,
country: req.phone.country,
token: hat(),
appInstalled: true
, new: true, upsert: true ).then(function(inbox)
/*
do something here with inbox, but only if the inbox was created (not updated)
*/
);
猫鼬是否有能力区分文档是创建还是更新?我需要new: true
,因为我需要调用inbox
上的函数。
【问题讨论】:
【参考方案1】:对于 .findOneAndUpdate()
或任何用于 mongoose 的 .findAndModify()
核心驱动程序变体,实际的回调签名具有“三个”参数:
function(err,result,raw)
第一个是任何错误响应,然后是修改或原始文档,取决于选项,第三个是发出语句的写入结果。
第三个参数应该返回如下数据:
lastErrorObject:
updatedExisting: false,
n: 1,
upserted: 55e12c65f6044f57c8e09a46 ,
value: _id: 55e12c65f6044f57c8e09a46,
number: 55555555,
country: 'US',
token: "XXX",
appInstalled: true,
__v: 0 ,
ok: 1
其中的一致字段为 lastErrorObject.updatedExisting
是 true/false
取决于是否发生 upsert 的结果。请注意,当此属性为 false
时,还有一个包含新文档的 _id
响应的“upserted”值,但当它是 true
时则没有。
因此,您将修改您的处理以考虑第三个条件,但这仅适用于回调而不是承诺:
Inbox.model.findOneAndUpdate(
"number": req.phone.number ,
"$set":
"country": req.phone.country,
"token": hat(),
"appInstalled": true
,
"new": true, "upsert": true ,
function(err,doc,raw)
if ( !raw.lastErrorObject.updatedExitsing )
// do things with the new document created
);
我还强烈建议您在此处使用 update operators 而不是原始对象,因为原始对象总是会覆盖整个文档,但像 $set
这样的运算符只会影响列出的字段。
还请注意,只要它们的值与未找到的完全匹配,该语句的任何匹配“查询参数”都会自动分配到新文档中。
鉴于由于某种原因,使用承诺似乎不会返回附加信息,那么除了设置 new: false
之外,没有看到使用承诺如何实现这一点,基本上当没有返回文档时,它就是一个新的。
无论如何,您都有预期要插入的所有文档数据,因此您并不真的需要返回这些数据。事实上,原生驱动程序方法是如何在核心处理此问题的,并且仅在发生 upsert 时以“upserted”_id
值响应。
这实际上归结为本网站上讨论的另一个问题,如下:
Can promises have multiple arguments to onFulfilled?
这实际上归结为 promise 响应中多个对象的解析,这在本机规范中不直接支持,但其中列出了一些方法。
因此,如果您实现 Bluebird 承诺并在那里使用 .spread()
方法,那么一切都很好:
var async = require('async'),
Promise = require('bluebird'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var testSchema = new Schema(
name: String
);
var Test = mongoose.model('Test',testSchema,'test');
Promise.promisifyAll(Test);
Promise.promisifyAll(Test.prototype);
async.series(
[
function(callback)
Test.remove(,callback);
,
function(callback)
var promise = Test.findOneAndUpdateAsync(
"name": "Bill" ,
"$set": "name": "Bill" ,
"new": true, "upsert": true
);
promise.spread(function(doc,raw)
console.log(doc);
console.log(raw);
if ( !raw.lastErrorObject.updatedExisting )
console.log( "new document" );
callback();
);
],
function(err)
if (err) throw err;
mongoose.disconnect();
);
这当然会返回两个对象,然后您可以一致地访问:
_id: 55e14b7af6044f57c8e09a4e, name: 'Bill', __v: 0
lastErrorObject:
updatedExisting: false,
n: 1,
upserted: 55e14b7af6044f57c8e09a4e ,
value: _id: 55e14b7af6044f57c8e09a4e, name: 'Bill', __v: 0 ,
ok: 1
以下是展示正常行为的完整清单:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var testSchema = new Schema(
name: String
);
var Test = mongoose.model('Test',testSchema,'test');
async.series(
[
function(callback)
Test.remove(,callback);
,
function(callback)
Test.findOneAndUpdate(
"name": "Bill" ,
"$set": "name": "Bill" ,
"new": true, "upsert": true
).then(function(doc,raw)
console.log(doc);
console.log(raw);
if ( !raw.lastErrorObject.updatedExisting )
console.log( "new document" );
callback();
);
],
function(err)
if (err) throw err;
mongoose.disconnect();
);
作为记录,本机驱动程序本身没有这个问题,因为响应对象实际上是除了任何错误之外它唯一返回的对象:
var async = require('async'),
mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient;
MongoClient.connect('mongodb://localhost/test',function(err,db)
var collection = db.collection('test');
collection.findOneAndUpdate(
"name": "Bill" ,
"$set": "name": "Bill" ,
"upsert": true, "returnOriginal": false
).then(function(response)
console.log(response);
);
);
所以总是这样:
lastErrorObject:
updatedExisting: false,
n: 1,
upserted: 55e13bcbf6044f57c8e09a4b ,
value: _id: 55e13bcbf6044f57c8e09a4b, name: 'Bill' ,
ok: 1
【讨论】:
我已经尝试查看raw
,它似乎在最新版本中不再存在(始终未定义)。我也在使用 Promise,它只为 then(function(item)...
提供一个参数
我最终通过创建自己的createOrUpdate
static 解决了这个问题。
@MattWay 这有点可疑。与普通回调一起工作正常,似乎是关于如何将事物作为承诺传递的。我想我会花几分钟看看这里出了什么问题。
据此github.com/Automattic/mongoose/issues/2931findAndModify 只提供doc(不是raw)。
@MattWay 关于这个问题的说法并不完全正确。我添加了一些关于本机驱动程序方法实际作用的信息,并通过一个工作示例对此进行了澄清。这里的问题是承诺,而不是驱动方法的功能。【参考方案2】:
对于将其与 Promises 一起使用的人,根据 this link from Mongoosejs.com 我们可以传递 rawQuery: true
以及其他选项。
const filter = name: 'Will Riker' ;
const update = age: 29 ;
await Character.countDocuments(filter); // 0
let res = await Character.findOneAndUpdate(filter, update,
new: true,
upsert: true,
rawResult: true // Return the raw result from the MongoDB driver
);
res.value instanceof Character; // true
// The below property will be `false` if MongoDB upserted a new
// document, and `true` if MongoDB updated an existing object.
res.lastErrorObject.updatedExisting; /
此查询的结果将采用以下格式
lastErrorObject:
n: 1,
updatedExisting: false,
upserted: 5e6a9e5ec6e44398ae2ac16a ,
value:
_id: 5e6a9e5ec6e44398ae2ac16a,
name: 'Will Riker',
__v: 0,
age: 29 ,
ok: 1
因此可以通过data.value
访问该文档
【讨论】:
以上是关于(mongoose/promises) 如何检查文档是不是是使用 findOneAndUpdate 和 upsert 创建的的主要内容,如果未能解决你的问题,请参考以下文章