sql >> Databasteknik >  >> NoSQL >> MongoDB

(mongoose/promises) Hur kontrollerar du om dokument skapades med findOneAndUpdate med upsert

I fallet med .findOneAndUpdate() eller någon av .findAndModify() kärndrivrutinvarianter för mongoose, den faktiska återuppringningssignaturen har "tre" argument:

 function(err,result,raw)

Det första är ett felsvar, sedan det ändrade eller ursprungliga dokumentet beroende på alternativ och det tredje som är ett skrivresultat av det utfärdade uttalandet.

Det tredje argumentet borde returnera data ungefär så här:

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

Med det konsekventa fältet där som lastErrorObject.updatedExisting vara antingen true/false beroende på resultatet av om en upsert inträffade. Observera att det också finns ett "uppskjutet" värde som innehåller _id svar för det nya dokumentet när den här egenskapen är false , men inte när det är true .

Som sådan skulle du sedan ändra din hantering för att överväga det tredje villkoret, men detta fungerar bara med en återuppringning och inte ett löfte:

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

Där jag också starkt rekommenderar att du använder uppdateringsoperatorer snarare än råobjekt här, eftersom ett råobjekt alltid kommer att skriva över hela dokumentet, men operatörer som $set påverkar bara de listade fälten.

Notera också att alla matchande "frågeargument" till satsen automatiskt tilldelas i det nya dokumentet så länge som deras värde är en exakt matchning som inte hittades.

Med tanke på att användningen av ett löfte inte verkar returnera ytterligare information av någon anledning, ser du inte hur detta är möjligt med ett löfte annat än att ställa in { new: false} och i princip när inget dokument returneras så är det ett nytt.

Du har alla dokumentdata som förväntas infogas ändå, så det är inte så att du verkligen behöver den datan returnerad ändå. Det är i själva verket hur de inbyggda drivrutinsmetoderna hanterar detta i kärnan, och bara svarar med den "upserted" _id värde när en upsert inträffar.

Detta kommer egentligen ner till en annan fråga som diskuteras på den här webbplatsen, under:

Kan löften ha flera argument att fortsätta att uppfylla?

Där detta verkligen handlar om upplösningen av flera objekt i ett löftessvar, vilket är något som inte direkt stöds i den ursprungliga specifikationen men det finns tillvägagångssätt som anges där.

Så om du implementerar Bluebird-löften och använder .spread() metod där, då är allt bra:

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

Vilket givetvis returnerar båda objekten och du kan komma åt då konsekvent:

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

Här är en fullständig lista som visar det normala beteendet:

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 att ta reda på att den inbyggda drivrutinen själv inte har detta problem eftersom svarsobjektet i själva verket är det enda objektet som returneras förutom eventuella fel:

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

Så det är alltid något sånt här:

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


  1. Mongoose + lodash utökar kopieringsarrayen för objektet felaktigt

  2. MongoDB $binarySize

  3. Var är AccountKey för CosmosDB i Azure Portal

  4. Använder SqlBulkCopy med MongoDB