Nel caso di .findOneAndUpdate()
o uno qualsiasi dei .findAndModify()
varianti del driver principale per mangusta, la firma di callback effettiva ha "tre" argomenti:
function(err,result,raw)
Con la prima qualsiasi risposta di errore, quindi il documento modificato o originale a seconda delle opzioni e il terzo che è un risultato di scrittura della dichiarazione rilasciata.
Quel terzo argomento dovrebbe restituire dati molto simili a questo:
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e12c65f6044f57c8e09a46 },
value: { _id: 55e12c65f6044f57c8e09a46,
number: 55555555,
country: 'US',
token: "XXX",
appInstalled: true,
__v: 0 },
ok: 1 }
Con il campo coerente lì dentro come lastErrorObject.updatedExisting
essendo true/false
a seconda del risultato se si è verificato un upsert. Nota che c'è anche un valore "upserted" contenente il _id
risposta per il nuovo documento quando questa proprietà è false
, ma non quando è true
.
Pertanto, modificheresti la tua gestione per considerare la terza condizione, ma questo funziona solo con una richiamata e non una promessa:
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
}
}
);
Laddove suggerirei caldamente di utilizzare gli operatori di aggiornamento
piuttosto che oggetti non elaborati qui, poiché un oggetto non elaborato sovrascriverà sempre l'intero documento, tuttavia operatori come $set
interessa solo i campi elencati.
Notando inoltre che eventuali "argomenti di query" corrispondenti all'istruzione vengono assegnati automaticamente nel nuovo documento purché il loro valore sia una corrispondenza esatta che non è stata trovata.
Dato che l'utilizzo di una promessa non sembra restituire le informazioni aggiuntive per qualche motivo, non vedo come ciò sia possibile con una promessa diversa dall'impostazione di { new: false}
e fondamentalmente quando nessun documento viene restituito, allora è uno nuovo.
Hai comunque tutti i dati del documento che dovrebbero essere inseriti, quindi non è che tu abbia davvero bisogno che i dati vengano restituiti comunque. È infatti il modo in cui i metodi del driver nativo gestiscono questo al centro e rispondono solo con il _id
"upserted" valore quando si verifica un upsert.
Questo si riduce davvero a un altro problema discusso su questo sito, in:
Le promesse possono avere più argomenti per onFulfilled?
Dove questo si riduce davvero alla risoluzione di più oggetti in una risposta promessa, che è qualcosa che non è supportato direttamente nella specifica nativa ma ci sono approcci elencati lì.
Quindi, se implementi le promesse Bluebird e usi .spread()
metodo lì, allora va tutto bene:
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();
}
);
Che ovviamente restituisce entrambi gli oggetti e puoi accedere quindi in modo coerente:
{ _id: 55e14b7af6044f57c8e09a4e, name: 'Bill', __v: 0 }
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e14b7af6044f57c8e09a4e },
value: { _id: 55e14b7af6044f57c8e09a4e, name: 'Bill', __v: 0 },
ok: 1 }
Ecco un elenco completo che mostra il comportamento normale:
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();
}
);
Per la cronaca, il driver nativo stesso non presenta questo problema poiché l'oggetto di risposta è in realtà l'unico oggetto restituito a parte qualsiasi errore:
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);
});
});
Quindi è sempre qualcosa del genere:
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e13bcbf6044f57c8e09a4b },
value: { _id: 55e13bcbf6044f57c8e09a4b, name: 'Bill' },
ok: 1 }