sql >> Database >  >> NoSQL >> MongoDB

FindAndUpdate Controleren of het document echt is bijgewerkt

De enige echt betrouwbare manier om te zien of een update is toegepast voor iets als een $pull is om in feite het geretourneerde document te controleren en te zien of de gegevens die u van plan was $pull zit er nog in of niet.

Dat is voor elk van de "findAndUpdate" verschillende acties, en daar is een geldige reden voor en het is ook zo dat een gewone .update() zal u daadwerkelijk "betrouwbaar" vertellen of de wijziging daadwerkelijk is aangebracht.

Om door de cases te lopen:

Controleer de geretourneerde inhoud

Dit houdt in feite in dat we naar de array in het geretourneerde document kijken om te zien of wat we hebben gevraagd om te verwijderen er ook is:

var pullId = "5961de06ea264532c684611a";

Office.findByIdAndUpdate(1,
  { "$pull": { "branches": { "_id": pullId } } },
  { "new": true }
).then(office => {
  // Check if the supplied value is still in the array
  console.log(
    "Still there?: %s",
    (office.branches.find( b => b._id.toHexString() === pullId))
      ? true : false
  );
}).catch(err => console.error(err))

We gebruiken .toHexString() om de werkelijke waarde van een ObjectId . te vergelijken omdat JavaScript gewoon geen "gelijkheid" doet met "Objecten". U zou zowel "links" als "rechts" aanvinken als u iets dat al was "gecast" aan een ObjectId levert waarde, maar in dit geval weten we dat de andere invoer een "string" is.

Gebruik gewoon .update(), "Het is betrouwbaar"

Het andere geval dat hier moet worden overwogen, stelt de vraag of u de geretourneerde gewijzigde gegevens toch "echt nodig" hebt. Omdat de .update() methode, zal op betrouwbare wijze een resultaat retourneren dat u vertelt of er daadwerkelijk iets is gewijzigd:

Office.update(
  { "_id": 1 },
  { "$pull": { "branches": { "_id": pullId } } },
).then(result => {
  log(result);
}).catch(err => console.error(err))

Waar result hier ziet er als volgt uit:

{
  "n": 1,
  "nModified": 1,        // <--- This always tells the truth, and cannot lie!
  "opTime": {
    "ts": "6440673063762657282",
    "t": 4
  },
  "electionId": "7fffffff0000000000000004",
  "ok": 1
}

En waarin de nModified is een "echte" indicator of iets "werkelijk is bijgewerkt". Dus als het 1 . is dan de $pull had eigenlijk effect, maar toen 0 niets is daadwerkelijk uit de array verwijderd en er is niets gewijzigd.

Dit komt omdat de methode daadwerkelijk de bijgewerkte API gebruikt, die wel betrouwbare resultaten heeft die daadwerkelijke wijzigingen aangeven. Hetzelfde zou gelden voor iets als een $set die de waarde niet daadwerkelijk veranderde omdat de opgegeven waarde gelijk was aan wat al in het document stond.

findAndModify Lies!

Het andere geval waar u aan zou kunnen denken als u de documentatie nauwkeurig bekijkt, is om het "onbewerkte resultaat" daadwerkelijk te inspecteren en te zien of het document is gewijzigd of niet. Er is hiervoor een indicator in de specificatie.

Het probleem is (naast dat er meer werk met Promises nodig is) dat het resultaat niet echt waarheidsgetrouw is:

var bogusId = "5961de06ea264532c684611a"; // We know this is not there!

Promise((resolve,reject) => {
  Office.findByIdAndUpdate(1,
    { "$pull": { "branches": { "_id": bogusId } } },
    { "new": true, "passRawResult" },
    (err,result,raw) => {        // We cannot pass multiple results to a Promise
      if (err) reject(err);
      resolve({ result, raw });   // So we wrap it!
    }
  )
})
.then(response => log(response.raw))
.catch(err => console.error(err));

Het probleem hier is dat zelfs als we "weten" dit niet zou moeten veranderen, het antwoord anders zegt:

{
  "lastErrorObject": {
    "updatedExisting": true,
    "n": 1                     // <--- LIES! IT'S ALL LIES!!!
  },
  "value": {
    "_id": 1,
    "name": "My Office",
    "branches": [
      {
        "address": "Third address",
        "isPrincipal": false,
        "_id": "5961de06ea264532c6846118"
      }
    ],
    "__v": 0
  },
  "ok": 1,
  "_kareemIgnore": true
}

Dus zelfs na al dat werk om het "derde" argument uit de callback-reactie te halen, kregen we nog steeds niet de juiste informatie over de update.

Concluderend

Dus als je dit "betrouwbaar" wilt doen, doe dit dan met een enkel verzoek ( en je kan niet doe dat op betrouwbare wijze met meerdere verzoeken, aangezien het document kan veranderen tussenin! ) dan zijn je twee opties:

  1. Controleer het geretourneerde document om te zien of de gegevens die u wilde verwijderen er nog steeds zijn.

  2. Vergeet het retourneren van een document en vertrouw erop dat .update() vertelt je altijd de "waarheid";)

Welke van deze u gebruikt, hangt af van het gebruikspatroon van de toepassing, maar dit zijn de twee verschillende manieren om een ​​"betrouwbaar" resultaat te retourneren.

Een beetje een vermelding

Dus voor de zekerheid, hier is een lijst die alle voorbeelden doorloopt en laat zien wat ze daadwerkelijk teruggeven:

const async = require('async'),
      mongoose = require('mongoose'),
      Schema = mongoose.Schema;

mongoose.Promise = global.Promise;
mongoose.set('debug',true);

mongoose.connect('mongodb://localhost/test');

const branchesSchema = new Schema({
  address: String,
  isPrincipal: Boolean
});

const officeSchema = new Schema({
  _id: Number,
  name: String,
  branches: [branchesSchema]
},{ _id: false });

const Office = mongoose.model('Office', officeSchema);

function log(data) {
  console.log(JSON.stringify(data,undefined,2))
}

const testId = "5961a56d3ffd3d5e19c61610";

async.series(
  [
    // Clean data
    (callback) =>
      async.each(mongoose.models,(model,callback) =>
        model.remove({},callback),callback),

    // Insert some data and pull
    (callback) =>
      async.waterfall(
        [
          // Create and demonstrate
          (callback) =>
            Office.create({
              _id: 1,
              name: "My Office",
              branches: [
                {
                  address: "Some street, that avenue",
                  isPrincipal: true
                },
                {
                  address: "Another address",
                  isPrincipal: false
                },
                {
                  address: "Third address",
                  isPrincipal: false
                }
              ]
            },callback),

          // Demo Alternates
          (office,callback) =>
            async.mapSeries(
              [true,false].map((t,i) => ({ t, branch: office.branches[i] })),
              (test,callback) =>
                (test.t)
                  ? Office.findByIdAndUpdate(office._id,
                      { "$pull": { "branches": { "_id": test.branch._id } } },
                      { "new": true , "passRawResult": true },
                      (err,result,raw) => {
                        if (err) callback(err);
                        log(result);
                        log(raw);
                        callback();
                      })
                  : Office.findByIdAndUpdate(office._id,
                      { "$pull": { "branches": { "_id": test.branch._id } } },
                      { "new": true } // false here
                    ).then(result => {
                      log(result);
                      console.log(
                        "Present %s",
                        (result.branches.find( b =>
                          b._id.toHexString() === test.branch._id.toHexString() ))
                          ? true : false
                      );
                      callback();
                    }).catch(err => callback(err)),
              callback
            )
        ],
        callback
      ),

    // Find and demonstate fails
    (callback) =>
      async.waterfall(
        [
          (callback) => Office.findOne({},callback),

          (office,callback) =>
            async.eachSeries([true,false],(item,callback) =>
              (item)
                ? Office.findByIdAndUpdate(office._id,
                    { "$pull": { "branches": { "_id": testId } } },
                    { "new": true, "passRawResult": true },
                    (err,result,raw) => {
                      if (err) callback(err);
                      log(result);
                      log(raw);
                      callback();
                    }
                  )
                : Office.findByIdAndUpdate(office._id,
                    { "$pull": { "branches": { "_id": testId } } },
                    { "new": true }
                  ).then(result => {
                    console.log(result);
                    console.log(
                      "Present %s",
                      (result.branches.find( b =>
                        b._id.toHexString() === office.branches[0]._id.toHexString()))
                        ? true : false
                    );
                    callback();
                  })
                  .catch(err => callback(err)),
              callback)

        ],
        callback
      ),

    // Demonstrate update() modified shows 0
    (callback) =>
      Office.update(
        {},
        { "$pull": { "branches": { "_id": testId } } }
      ).then(result => {
        log(result);
        callback();
      })
      .catch(err => callback(err)),

    // Demonstrate wrapped promise
    (callback) =>
      Office.findOne()
        .then(office => {
          return new Promise((resolve,reject) => {
            Office.findByIdAndUpdate(office._id,
              { "$pull": { "branches": { "_id": testId } } },
              { "new": true, "passRawResult": true },
              (err,result,raw) => {
                if (err) reject(err);
                resolve(raw)
              }
            );
          })
        })
        .then(office => {
          log(office);
          callback();
        })
        .catch(err => callback(err))

  ],
  (err) => {
    if (err) throw err;
    mongoose.disconnect();
  }
);

En de output die het produceert:

Mongoose: offices.remove({}, {})
Mongoose: offices.insert({ _id: 1, name: 'My Office', branches: [ { address: 'Some street, that avenue', isPrincipal: true, _id: ObjectId("5961e5211a73e8331b44d74b") }, { address: 'Another address', isPrincipal: false, _id: ObjectId("5961e5211a73e8331b44d74a") }, { address: 'Third address', isPrincipal: false, _id: ObjectId("5961e5211a73e8331b44d749") } ], __v: 0 })
Mongoose: offices.findAndModify({ _id: 1 }, [], { '$pull': { branches: { _id: ObjectId("5961e5211a73e8331b44d74b") } } }, { new: true, passRawResult: true, upsert: false, remove: false, fields: {} })
{
  "_id": 1,
  "name": "My Office",
  "__v": 0,
  "branches": [
    {
      "address": "Another address",
      "isPrincipal": false,
      "_id": "5961e5211a73e8331b44d74a"
    },
    {
      "address": "Third address",
      "isPrincipal": false,
      "_id": "5961e5211a73e8331b44d749"
    }
  ]
}
{
  "lastErrorObject": {
    "updatedExisting": true,
    "n": 1
  },
  "value": {
    "_id": 1,
    "name": "My Office",
    "branches": [
      {
        "address": "Another address",
        "isPrincipal": false,
        "_id": "5961e5211a73e8331b44d74a"
      },
      {
        "address": "Third address",
        "isPrincipal": false,
        "_id": "5961e5211a73e8331b44d749"
      }
    ],
    "__v": 0
  },
  "ok": 1,
  "_kareemIgnore": true
}
Mongoose: offices.findAndModify({ _id: 1 }, [], { '$pull': { branches: { _id: ObjectId("5961e5211a73e8331b44d74a") } } }, { new: true, upsert: false, remove: false, fields: {} })
{
  "_id": 1,
  "name": "My Office",
  "__v": 0,
  "branches": [
    {
      "address": "Third address",
      "isPrincipal": false,
      "_id": "5961e5211a73e8331b44d749"
    }
  ]
}
Present false
Mongoose: offices.findOne({}, { fields: {} })
Mongoose: offices.findAndModify({ _id: 1 }, [], { '$pull': { branches: { _id: ObjectId("5961a56d3ffd3d5e19c61610") } } }, { new: true, passRawResult: true, upsert: false, remove: false, fields: {} })
{
  "_id": 1,
  "name": "My Office",
  "__v": 0,
  "branches": [
    {
      "address": "Third address",
      "isPrincipal": false,
      "_id": "5961e5211a73e8331b44d749"
    }
  ]
}
{
  "lastErrorObject": {
    "updatedExisting": true,
    "n": 1
  },
  "value": {
    "_id": 1,
    "name": "My Office",
    "branches": [
      {
        "address": "Third address",
        "isPrincipal": false,
        "_id": "5961e5211a73e8331b44d749"
      }
    ],
    "__v": 0
  },
  "ok": 1,
  "_kareemIgnore": true
}
Mongoose: offices.findAndModify({ _id: 1 }, [], { '$pull': { branches: { _id: ObjectId("5961a56d3ffd3d5e19c61610") } } }, { new: true, upsert: false, remove: false, fields: {} })
{ _id: 1,
  name: 'My Office',
  __v: 0,
  branches:
   [ { address: 'Third address',
       isPrincipal: false,
       _id: 5961e5211a73e8331b44d749 } ] }
Present true
Mongoose: offices.update({}, { '$pull': { branches: { _id: ObjectId("5961a56d3ffd3d5e19c61610") } } }, {})
{
  "n": 1,
  "nModified": 0,
  "opTime": {
    "ts": "6440680872013201413",
    "t": 4
  },
  "electionId": "7fffffff0000000000000004",
  "ok": 1
}
Mongoose: offices.findOne({}, { fields: {} })
Mongoose: offices.findAndModify({ _id: 1 }, [], { '$pull': { branches: { _id: ObjectId("5961a56d3ffd3d5e19c61610") } } }, { new: true, passRawResult: true, upsert: false, remove: false, fields: {} })
{
  "lastErrorObject": {
    "updatedExisting": true,
    "n": 1
  },
  "value": {
    "_id": 1,
    "name": "My Office",
    "branches": [
      {
        "address": "Third address",
        "isPrincipal": false,
        "_id": "5961e5211a73e8331b44d749"
      }
    ],
    "__v": 0
  },
  "ok": 1,
  "_kareemIgnore": true
}


  1. MongoDB - moet mijn gebruikersdocument een lijst met project-ID's bevatten?

  2. Hoe geüpdatete waarden alleen projecteren met findOneAndUpdate in embedded array Mongoose?

  3. authenticatieprobleem met laravel privékanaal en laravel-echo-server

  4. Vraag MongoDB of het Master is uit een bashscript