在mongoose中,
.findOneAndUpdate()
或任何
.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
时,没有该值。
因此,您需要修改处理方式以考虑第三种情况,但这仅适用于回调而不是 promise:
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 ) {
}
}
);
我强烈建议您在此处使用
更新操作符而不是原始对象,因为原始对象将始终覆盖整个文档,而
$set
等操作符只影响列出的字段。
还要注意,只要它们的值是未找到的精确匹配,任何匹配的“查询参数”都会自动分配给新文档。
考虑到使用Promise似乎无法出于某种原因返回附加信息,那么除了设置
{new:false}
并且基本上没有返回文档时就是新文档之外,我不认为这是Promise可以实现的。
您已经拥有预期插入的所有文档数据,因此您实际上并不需要返回该数据。事实上,这正是本机驱动程序方法在核心处理此问题的方式,并且仅在发生插入时才响应“upserted”
_id
值。
这实际上归结为本站讨论的另一个问题,详见:
承诺对象的onFulfilled函数可以有多个参数吗?
这实际上涉及到在一个承诺响应中解析多个对象的问题,这是原生规范不直接支持的,但有一些方法在那里列出。
所以如果您实现了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 }
.spread()
方法,那么promisifyAll()
是非常昂贵的。毕竟,.spread()
什么都做不了,你可以用.then()
来代替它。 - Roamer-1888.then()
支持。因此,您需要以“某种方式”定义它,以便.spread()
可用于接受两个响应。理想情况下,这将是ES6,您只需转换返回值即可。因此是不正确的。在这种情况下,.spread()
执行一些.then()
无法执行的操作。 - Blakes Seven