В случай на .findOneAndUpdate()
или някой от .findAndModify()
варианти на основен драйвер за mongoose, действителният подпис за обратно извикване има "три" аргумента:
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. Обърнете внимание, че има и „upserted“ стойност, съдържаща _id
отговор за новия документ, когато това свойство е false
, но не и когато е 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
}
}
);
Където също силно препоръчвам да използвате оператори за актуализиране
вместо необработени обекти тук, тъй като необработен обект винаги ще презапише целия документ, но оператори като $set
просто засяга изброените полета.
Също така отбелязваме, че всички съвпадащи „аргументи на заявката“ към израза се присвояват автоматично в новия документ, стига тяхната стойност да е точно съвпадение, което не е намерено.
Като се има предвид, че използването на обещание изглежда не връща допълнителната информация по някаква причина, тогава не виждам как това е възможно с обещание, различно от настройката { new: false}
и по принцип, когато не се връща документ, той е нов.
Имате всички данни от документа, които се очаква да бъдат вмъкнати така или иначе, така че не е като наистина да имате нужда от тези данни. Всъщност методите на родния драйвер се справят с това в основата и отговарят само с „upserted“ _id
стойност при възникване на upsert.
Това всъщност се свежда до друг проблем, обсъждан на този сайт, под:
Могат ли обещанията да имат множество аргументи за 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 }