MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

(mangusta/promesse) Come si verifica se il documento è stato creato utilizzando findOneAndUpdate con upsert

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 }