Dans le cas de .findOneAndUpdate()
ou l'un des .findAndModify()
variantes de pilote de base pour la mangouste, la signature de rappel réelle a "trois" arguments :
function(err,result,raw)
Le premier étant une réponse d'erreur, puis le document modifié ou original en fonction des options et le troisième qui est un résultat d'écriture de la déclaration émise.
Ce troisième argument devrait renvoyer des données comme ceci :
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e12c65f6044f57c8e09a46 },
value: { _id: 55e12c65f6044f57c8e09a46,
number: 55555555,
country: 'US',
token: "XXX",
appInstalled: true,
__v: 0 },
ok: 1 }
Avec le champ cohérent comme lastErrorObject.updatedExisting
étant soit true/false
selon le résultat ou si un upsert s'est produit. Notez qu'il existe également une valeur "upserted" contenant le _id
réponse pour le nouveau document lorsque cette propriété est false
, mais pas quand c'est true
.
En tant que tel, vous modifieriez alors votre traitement pour prendre en compte la troisième condition, mais cela ne fonctionne qu'avec un rappel et non une promesse :
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
}
}
);
Où je vous suggérerais également fortement d'utiliser les opérateurs de mise à jour
plutôt que des objets bruts ici, car un objet brut écrasera toujours le document entier, mais des opérateurs comme $set
n'affecte que les champs listés.
Notez également que tous les "arguments de requête" correspondant à l'instruction sont automatiquement affectés dans le nouveau document tant que leur valeur est une correspondance exacte qui n'a pas été trouvée.
Étant donné que l'utilisation d'une promesse ne semble pas renvoyer les informations supplémentaires pour une raison quelconque, alors ne voyez pas comment cela est possible avec une promesse autre que la définition de { new: false}
et fondamentalement quand aucun document n'est retourné alors c'est un nouveau.
Vous avez de toute façon toutes les données du document qui doivent être insérées, donc ce n'est pas comme si vous aviez vraiment besoin que ces données soient renvoyées de toute façon. C'est en fait la façon dont les méthodes de pilote natives gèrent cela au cœur et ne répondent qu'avec le _id
"upserted" valeur lorsqu'un upsert se produit.
Cela revient vraiment à un autre problème discuté sur ce site, sous :
Les promesses peuvent-elles avoir plusieurs arguments pour onFulfilled ?
Là où cela se résume vraiment à la résolution de plusieurs objets dans une réponse de promesse, ce qui n'est pas directement pris en charge dans la spécification native, mais il existe des approches répertoriées ici.
Donc, si vous implémentez les promesses Bluebird et utilisez le .spread()
méthode ici, alors tout va bien :
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();
}
);
Ce qui renvoie bien sûr les deux objets et vous pouvez y accéder de manière cohérente :
{ _id: 55e14b7af6044f57c8e09a4e, name: 'Bill', __v: 0 }
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e14b7af6044f57c8e09a4e },
value: { _id: 55e14b7af6044f57c8e09a4e, name: 'Bill', __v: 0 },
ok: 1 }
Voici une liste complète démontrant le comportement normal :
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();
}
);
Pour mémoire, le pilote natif lui-même n'a pas ce problème car l'objet de réponse est en fait le seul objet renvoyé en dehors de toute erreur :
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);
});
});
Donc c'est toujours quelque chose comme ça :
{ lastErrorObject:
{ updatedExisting: false,
n: 1,
upserted: 55e13bcbf6044f57c8e09a4b },
value: { _id: 55e13bcbf6044f57c8e09a4b, name: 'Bill' },
ok: 1 }