对于 .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 }