MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

(Mongoose/Promises) Wie überprüfen Sie, ob das Dokument mit findOneAndUpdate mit upsert erstellt wurde

Im Fall von .findOneAndUpdate() oder eines der .findAndModify() Core-Treibervarianten für Mongoose hat die eigentliche Callback-Signatur "drei" Argumente:

 function(err,result,raw)

Die erste ist eine beliebige Fehlerantwort, dann das modifizierte oder ursprüngliche Dokument, je nach Optionen, und die dritte ist ein Schreibergebnis der ausgegebenen Anweisung.

Dieses dritte Argument sollte Daten ähnlich wie diese zurückgeben:

{ lastErrorObject:
   { updatedExisting: false,
     n: 1,
     upserted: 55e12c65f6044f57c8e09a46 },
  value: { _id: 55e12c65f6044f57c8e09a46, 
           number: 55555555, 
           country: 'US', 
           token: "XXX", 
           appInstalled: true,
           __v: 0 },
  ok: 1 }

Mit dem konsistenten Feld darin als lastErrorObject.updatedExisting entweder true/false sein abhängig vom Ergebnis, ob ein Upsert aufgetreten ist. Beachten Sie, dass es auch einen "Upserted"-Wert gibt, der die _id enthält Antwort für das neue Dokument, wenn diese Eigenschaft false ist , aber nicht, wenn es true ist .

Als solches würden Sie dann Ihre Behandlung ändern, um die dritte Bedingung zu berücksichtigen, aber dies funktioniert nur mit einem Rückruf und nicht mit einem Versprechen:

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
      }
    }
);

Wobei ich Ihnen auch dringend empfehlen würde, Update-Operatoren zu verwenden Anstelle von Rohobjekten hier, da ein Rohobjekt immer das gesamte Dokument überschreibt, jedoch Operatoren wie $set wirken sich nur auf die aufgelisteten Felder aus.

Beachten Sie auch, dass alle übereinstimmenden "Abfrageargumente" der Anweisung automatisch im neuen Dokument zugewiesen werden, solange ihr Wert eine exakte Übereinstimmung ist, die nicht gefunden wurde.

Angesichts der Tatsache, dass die Verwendung eines Versprechens aus irgendeinem Grund die zusätzlichen Informationen nicht zurückzugeben scheint, sehen Sie nicht, wie dies mit einem anderen Versprechen als dem Setzen von { new: false} möglich ist und im Grunde, wenn kein Dokument zurückgegeben wird, ist es ein neues.

Sie haben sowieso alle Dokumentdaten, von denen erwartet wird, dass sie eingefügt werden, also brauchen Sie diese Daten sowieso nicht wirklich. Tatsächlich behandeln die nativen Treibermethoden dies im Kern so und antworten nur mit der "upserted" _id Wert, wenn ein Upsert auftritt.

Dies läuft wirklich auf ein anderes Thema hinaus, das auf dieser Website diskutiert wird, unter:

Können Versprechungen mehrere Argumente für onFulfilled haben?

Wo dies wirklich auf die Auflösung mehrerer Objekte in einer Promise-Antwort hinausläuft, was in der nativen Spezifikation nicht direkt unterstützt wird, aber dort sind Ansätze aufgeführt.

Wenn Sie also Bluebird Promises implementieren und den .spread() verwenden Methode dort, dann ist alles in Ordnung:

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();
  }
);

Was natürlich beide Objekte zurückgibt und man dann konsistent darauf zugreifen kann:

{ _id: 55e14b7af6044f57c8e09a4e, name: 'Bill', __v: 0 }
{ lastErrorObject:
   { updatedExisting: false,
     n: 1,
     upserted: 55e14b7af6044f57c8e09a4e },
  value: { _id: 55e14b7af6044f57c8e09a4e, name: 'Bill', __v: 0 },
  ok: 1 }

Hier ist eine vollständige Auflistung, die das normale Verhalten demonstriert:

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();
  }
);

Für das Protokoll, der native Treiber selbst hat dieses Problem nicht, da das Antwortobjekt tatsächlich das einzige Objekt ist, das abgesehen von Fehlern zurückgegeben wird:

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);
  });
});

Es ist also immer ungefähr so:

{ lastErrorObject:
   { updatedExisting: false,
     n: 1,
     upserted: 55e13bcbf6044f57c8e09a4b },
  value: { _id: 55e13bcbf6044f57c8e09a4b, name: 'Bill' },
  ok: 1 }