2017-07-26 5 views
0

ich mit mongoosejs 4.9.5 und 3.2.7 mongo eine Geschichte der Staaten in einem Subdokument Array zu halten bin versucht

Beispiel für Dokumentstruktur:

  • Firma (Schema)
    • Mitarbeiter (Schema): []
    • current: String
    • Staaten (Schema): []
      • Zustand: String
      • beginnt: Datum
      • Enden: Datum

Wenn ich die Mitarbeiter ihren Zustand ändern , Ich möchte den currentState ändern, den neuen Zustand in das states-Array einfügen und die letzten s aktualisieren tate für definieren die ‚Enden‘ Zeitstempel

// I get the last state position from a previous find request 
var lastStateIndex = employee.stateHistory.length - 1; 
var changeStateDate = new Date(); 

// Prepare the update 
var query = { _id: companyId, "employees._id": employeeId }; 
var update = { 
    $set: { 
     "employees.$.state": newState, 
     `employees.$.stateHistory.${lastStateIndex}.ends`: changeStateDate 
    }, 
    $push: { 
     "employees.$.stateHistory": { 
      state: newState, 
      starts: changeStateDate 
     } 
    } 
} 
Company.findOneAndUpdate(query, update, { multi:false, new:true}, ...) 

Mongo zurückkehrt, wird folgende Fehler

{"name":"MongoError","message":"Cannot update 'employees.0.stateHistory.0.ends' and 'employees.0.stateHistory' at the same time","ok":0,"errmsg":"Cannot update 'employees.0.stateHistory.0.ends' and 'employees.0.stateHistory' at the same time","code":16837} 
  • Irgendwelche Vorschläge, wie zu diesem Zweck laufen zwei Updates zu vermeiden?
  • Irgendwelche Arbeiten um zu vermeiden, das 'Ende'-Datum zu speichern, aber in der Lage zu sein, es basierend auf den' Starts 'des nächsten Elements im Array zu berechnen?

Danke,

+0

Es ist der '' stateHistory '' Teil natürlich. Es gibt "keine Reihenfolge" für die Anwendung von Operationen in einer Aktualisierungsanweisung. Sie können also den gleichen Pfad nicht ändern. Sie sollten stattdessen '.bulkWrite()' verwenden und die $ set und $ push auf dem "gleichen Pfad" in zwei separaten Operationen ausgeben. Sie müssen getrennt sein, aber was "Bulk" tut, wird als ** eine ** Anfrage mit ** einer ** Antwort gesendet, im Gegensatz zu "mehreren" von jedem. –

+0

Danke Neil, wirklich appreaciate es! –

Antwort

1

ich dies bereits an anderer Stelle beantwortet werden erwartet, aber keine andere vernünftige Antwort scheint zu existieren. Wie bereits erwähnt, können Sie dies nicht in einem einzigen Aktualisierungsvorgang durchführen, da die Vorgänge auf demselben Pfad "Konflikte" verursachen. Aber .bulkWrite() ermöglicht "mehrere Updates" in einer einzigen Anfrage und Antwort angewendet werden.

Company.bulkWrite([ 
    { "updateOne": { 
    "filter": { "_id": companyId, "employees._id": employeeId }, 
    "update": { 
     "$set": { 
     "employees.$.state": newState, 
     [`employees.$.stateHistory.${lastStateIndex}.ends`]: changeStateDate 
     } 
    }}, 
    { "updateOne": { 
    "filter": { "_id": companyId, "employees._id": employeeId }, 
    "update": { 
     "$push": { 
     "employees.$.stateHistory": { 
      "state": newState, 
      "starts": changeStateDate 
     } 
     } 
    } 
    }} 
]) 

Jetzt natürlich .bulkWrite() gibt nicht die „modifizierte Dokument“ wie .findOneAndUpdate() tut. Also, wenn Sie Notwendigkeit, um tatsächlich das Dokument zurück, dann müssen Sie die Versprechen Kette hinzufügen:

Company.bulkWrite([ 
    { "updateOne": { 
    "filter": { "_id": companyId, "employees._id": employeeId }, 
    "update": { 
     "$set": { 
     "employees.$.state": newState, 
     [`employees.$.stateHistory.${lastStateIndex}.ends`]: changeStateDate 
     } 
    }}, 
    { "updateOne": { 
    "filter": { "_id": companyId, "employees._id": employeeId }, 
    "update": { 
     "$push": { 
     "employees.$.stateHistory": { 
      "state": newState, 
      "starts": changeStateDate 
     } 
     } 
    } 
    }} 
]).then(result => { 
    // maybe inspect the result 
    return Company.findById(companyId); 
}) 

Natürlich stellt fest, dass es „möglich“ ist, dass eine weitere Modifikation kann an dem Dokument vorgenommen werden zwischen wenn die .bulkWrite() angewendet wird und die .findById() ausgeführt wird. Aber das sind die Kosten für die Operation, die Sie gerade machen.

Es ist im Allgemeinen am besten zu prüfen, ob Sie tatsächlich das zurückgegebene Dokument benötigen oder nicht.In den meisten Fällen haben Sie einfach die Informationen und alle "Updates", die Sie beachten sollten, weil Sie sie "ausstellen", und wenn Sie "wirklich reaktiv" wollen, sollten Sie andere Änderungsereignisse auf den Daten über einen Socket abhören stattdessen.

Hinweis Sie konnte einfach „Kette“ die „multiple“ .findOneAndUpdate() Anrufe, aber dies ist in der Tat „multiple“ Anrufe und Antworten vom Server, wie zum .bulkWrite() mit einemgegenüber. Es gibt also wirklich nichts zu gewinnen, wenn man es anders macht.

Verwandte Themen