Mongoose: Model.save() speichert keine eingebetteten Arrays

Erstellt am 10. Nov. 2012  ·  26Kommentare  ·  Quelle: Automattic/mongoose

Ich habe verschachtelte Arrays von Dokumenten, und wenn ich sie ändere und eine .save () auf dem Modell ausführe, werden die Änderungen im verschachtelten Dokument nicht gespeichert.

Hier sind meine Modelle und Schemata (einige der Kürze halber weggelassen):

var Votes = new mongoose.Schema({
    date: Date, 
    user_name: String,
    is_up: Boolean
});

var EventSchema = new mongoose.Schema({
  to_date: Date,
  from_date: Date,
  location: String,
  name: String,
  suggested_by: String,
  description: String,
  users: [EventUser],
  comments: [Comments],
  suggestions: [Event],
  votes: [Votes]
});

var Event = mongoose.model('Event', EventSchema);

Modell vor event.save() aufgerufen:

{ 
     __v: 1,
    _id: 509e87e583ccbfa00e000004,
    description: 'Pfft',
    from_date: Sun Nov 11 2012 08:00:00 GMT-0500 (EST),
    location: 'Home',
    name: 'Whatever',
    suggested_by: 'No one',
    to_date: Sun Nov 11 2012 00:00:00 GMT-0500 (EST),
    votes: [],
    suggestions: 
     [ { users: [],
         comments: [],
         suggestions: [],
         votes: [],
         _id: 509e880883ccbfa00e000005,
         suggested_by: 'Some one',
         to_date: Sun Nov 11 2012 04:00:00 GMT-0500 (EST),
         from_date: Mon Nov 12 2012 00:00:00 GMT-0500 (EST),
         location: 'Home',
         name: 'Football',
         description: 'FOOTBALL!!' } ],
    comments: [],
    users: [] 
}

Das gleiche Objekt mit den verschachtelten Stimmen direkt vor event.save() wird aufgerufen.

{
   "__v":1,
   "_id":"509e87e583ccbfa00e000004",
   "description":"Pfft",
   "from_date":"2012-11-11T13:00:00.000Z",
   "location":"Home",
   "name":"Whatever",
   "suggested_by":"No one",
   "to_date":"2012-11-11T05:00:00.000Z",
   "votes":[ ],
   "suggestions":
      [ {
         "users":[],
         "comments":[ ],
         "suggestions":[ ],
         "votes":
            [{
               "is_up":true,
               "date":"2012-11-10T18:05:25.796Z",
               "user_name":"No one"
            }],
         "_id":"509e880883ccbfa00e000005",
         "suggested_by":"Some one",
         "to_date":"2012-11-11T09:00:00.000Z",
         "from_date":"2012-11-12T05:00:00.000Z",
         "location":"Home",
         "name":"Football",
         "description":"FOOTBALL!!"
      }],
   "comments":[],
   "users":[]
}

Wenn event.save() aufgerufen wird, wird kein Fehler ausgegeben, aber das verschachtelte Stimmenschema innerhalb des verschachtelten Ereignisschemas wird nicht tatsächlich gespeichert. Wenn ich dieselbe Gesamtlogik im Ereignisobjekt der obersten Ebene verwende, um eine Stimme zu speichern, funktioniert es.

Als ich den Code kurz durchgesehen habe, scheint es, dass .save() eine Verknüpfung sowohl zum Speichern neuer Objekte als auch zum Aktualisieren bereits vorhandener Objekte sein soll.

Meine Vermutung ist, dass Model.prototype._delta nicht tief genug ist, um alle verschachtelten Objekte zu erfassen, https://github.com/LearnBoost/mongoose/blob/master/lib/model.js#L529

Hilfreichster Kommentar

Ich sehe, was das Problem ist - klassischer Fall der ersten Frage auf der Mungo-FAQ.

Danke, in meinem Fall war es dieses Problem! :)

model.myArray[index] = anyValue

wird

model.myArray.set(index, anyValue)

Alle 26 Kommentare

Bitte geben Sie auch den Code an, den Sie verwenden, um das Problem zu reproduzieren (wo Sie das Dokument vor dem Speichern manipulieren usw.).

Sicher, ich habe es in einem Kern gepostet, um eine Codewand hier zu vermeiden, https://gist.github.com/4055392

Ich habe sowohl das Speichern einer Stimme im Ereignis der obersten Ebene (funktioniert) als auch das Speichern in einem verschachtelten Ereignis (funktioniert nicht) eingeschlossen. Wie Sie sehen können, fügen sie eine Stimme hinzu oder aktualisieren sie und speichern sie dann auf ähnliche Weise.

Versuchen Sie, die in suggestions verwendete EventSchema-Ref zu korrigieren:

// bad
var EventSchema = new mongoose.Schema({
  suggestions: [EventSchema] <== at this time, EventSchema is undefined which is interpreted as Mixed

// instead...

var EventSchema = new mongoose.Schema;
EventSchema.add({
  suggestions: [EventSchema] <== EventSchema exists
})


schließen, keine Reaktion. bitte bei Bedarf wieder öffnen.

Ich habe das gleiche Problem, bei dem .save() ein Dokument zurückgibt, das das eingebettete Objekt aktualisiert hat, aber nicht tatsächlich in mongodb gespeichert wird. Bitte helfen Sie mir dabei.

@Manojkumar91 können Sie einen Code bereitstellen, der dies

Ich bin auch darauf gestoßen, hier ist mein Modell:

/*jslint node:true */
"use strict";

var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var measurement = new Schema({
    name: {type: String, required: true},
    instruction: {type: String, required: true}
});

var size = new Schema({
    name: {type: String, required: true},
    lengths: [String]
});

var chartSchema = new Schema({
    name: {type: String, required: true },
    diagram: {type: String, required: true },
    description: {type: String, required: true},
    totalMeasurements: {type: Number, required: true},
    totalSizes: {type: Number, required: true},
    measurements: [measurement],
    sizes: [size]
});

module.exports = mongoose.model('chart', chartSchema);

Dies ist Teil einer viel größeren App, daher füge ich nur den relevanten Controller-Aktionsfilter hinzu, aber ich kann den Rest in einem größeren Sinne ansprechen. Unten ist der Filtercode:

    module.exports = function (next) {
      var paramCounter = 1,
          outerLoopCounter = 0,
          existingSizes = this.chart.sizes.length,
          outerLoopEdge = this.chart.totalSizes,
          innerLoopCounter = 0,
          innerloopEdge = this.chart.totalMeasurements - 1,
          size = {name: null, lengths: [] };

      if (this.req.method && this.req.method === "POST") {
          for (outerLoopCounter;
                  outerLoopCounter < existingSizes;
                  outerLoopCounter = outerLoopCounter + 1) {

              this.chart.sizes[outerLoopCounter].name =
                  this.param("name_" + paramCounter);

              for (innerLoopCounter;
                      innerLoopCounter <= innerloopEdge;
                      innerLoopCounter = innerLoopCounter + 1) {
                  this.chart.sizes[outerLoopCounter].lengths[innerLoopCounter] =
                      this.param(this.chart.measurements[innerLoopCounter].name);
              }
              paramCounter = paramCounter + 1;
              innerLoopCounter = 0;
          }

        for (outerLoopCounter;
                outerLoopCounter < outerLoopEdge;
                outerLoopCounter = outerLoopCounter + 1) {
            size.name = this.param("name_" + paramCounter);
            for (innerLoopCounter;
                    innerLoopCounter < innerloopEdge;
                    innerLoopCounter = innerLoopCounter + 1) {
                size.lengths.push(
                    this.param(this.chart.measurements[innerLoopCounter].name
                               + "_" + paramCounter)
                );
            }
            this.chart.sizes.push(size);
            paramCounter = paramCounter + 1;
            innerLoopCounter = 0;
            size = { name: null, lengths: [] };
        }

        this.chart.save(function (err) {
            if (err) {
                console.log(err);
            }

            this.chart_info = "measurements for <strong>" + this.chart.name + "</strong> saved.";
            this.render("display");
        }.bind(this));
    } else {
        next();
    }
  };

Tatsächlich wird vor dem Ausführen dieses Filters ein Filter aufgerufen, der eine zustandsabhängige Formularstruktur basierend auf dem zweidimensionalen Array von Maßen und Größen erstellt, dann wird diese Liste von Elementen geparst, um Aktualisierungen bereitzustellen. Ich habe mit Dummy-Daten getestet und das aktuelle Dummy-Diagramm (von der Konsole entnommen) sieht so aus:

> db.charts.find();
{ "_id" : ObjectId("553da6c3d3d0940a640e878c"), "name" : "Chart With Measurements", "diagram" : "http://res.cloudinary.com/harung71k/image/upload/v1430103747/nd4gipcxnykbbcpcztp9.jpg", "description" : "<p>This is a test of the new measurement methodology, it works.</p>", "totalMeasurements" : 4, "totalSizes" : 3, "sizes" : [ { "name" : "Small", "_id" : ObjectId("554183ed63c5945b73b8a8e7"), "lengths" : [ "1", "2", "3", "4" ] }, { "name" : "Medium", "_id" : ObjectId("554183ed63c5945b73b8a8e8"), "lengths" : [ "5", "6", "7", "8" ] }, { "name" : "Large", "_id" : ObjectId("554183ed63c5945b73b8a8e9"), "lengths" : [ "9", "10", "11", "12" ] } ], "measurements" : [ { "name" : "Fuzz", "instruction" : "<p>Fuzz Instructions</p>", "_id" : ObjectId("553dadd253eb9f996c68a381") }, { "name" : "Buzz", "instruction" : "<p>Buzz Instructions</p>", "_id" : ObjectId("553dadd253eb9f996c68a382") }, { "name" : "Beatles", "instruction" : "<p>Beatles Instructions</p>", "_id" : ObjectId("553dadd253eb9f996c68a383") }, { "name" : "Stones", "instruction" : "<p>Stones instructions</p>", "_id" : ObjectId("553ee7a09ff8c567004bd261") } ], "__v" : 3 }

Ich habe das geänderte Größenarray direkt vor dem Speichern getestet, wobei im Slot size[0].lengths[0] der Wert "1111" eingegeben wurde. Dies wird im Dokument bei der Überprüfung in Node angezeigt, aber das Speichern wird genau zurückgegeben das gleiche Dokument als Pre-Save.

Bitte lassen Sie mich wissen, wenn Sie weitere Details benötigen.

@crispen-smith Ich brauche eine Klarstellung, es gibt ein bisschen zu viel Code, als dass ich ihn leicht verstehen könnte. Können Sie den Debug-Modus von Mungo mit require('mongoose').set('debug', true); aktivieren und die Ausgabe posten? Das zeigt mir die Abfragen und Schreibvorgänge, die an den Server gesendet werden.

Ja, es ist viel Code, aber gleichzeitig... nicht genug, um es einfach zu erklären.

Das Ausführen von Debug scheint also darauf hinzudeuten, dass der Speichervorgang nicht einmal getroffen wird. Hier ist ein Speichervorgang mit einer Namensänderung:

Mongoose: charts.findOne({ name: 'Chart With Measurements' }) { fields: undefined }  
Mongoose: charts.update({ _id: ObjectId("553da6c3d3d0940a640e878c"), __v: 3 }) { '$set': { 'sizes.0.name': 'Smaller' } } {} 

Hier wird dieselbe Operationssequenz mit einer Änderung eines Namens und eines Werts aus dem String-Array ausgeführt:

Mongoose: charts.findOne({ name: 'Chart With Measurements' }) { fields: undefined }  
Mongoose: charts.update({ _id: ObjectId("553da6c3d3d0940a640e878c"), __v: 3 }) { '$set': { 'sizes.0.name': 'Small' } } {} 

Und nur die Änderung einer Variablen innerhalb des Arrays:

Mungo: charts.findOne({ name: 'Chart With Measurements' }) { Felder: undefined }
(Yup, das war's... keine Update-Abfrage)

Ich werde versuchen, es zu patchen, indem ich jedes Mal das gesamte Unterdokument lösche, zumindest für den Moment ... Ich brauche keine referenzielle Integrität oder die Objekt-IDs für diesen speziellen Zweck.

--AKTUALISIEREN--
Ich habe versucht zu löschen und neu zu laden, aber das scheint auch nicht zu funktionieren. Ich habe zwar keine Ausgabeverfolgung für dieses Stück, aber es sieht so aus, als ob das Löschen auch keinen Back-End-Vorgang auslöst. Gibt es eine Art Dirty-Checking, die nicht ausgelöst wird?

Gibt es eine Möglichkeit, dies angesichts meiner obigen Notizen wieder zu öffnen?

@crispen-smith hier ist mein grundlegender Versuch, das Problem als eigenständiges Skript zu reproduzieren:

var mongoose = require('mongoose');
mongoose.set('debug', true);
var util = require('util');
var assert = require('assert');

mongoose.connect('mongodb://localhost:27017/gh1204');

var Schema = mongoose.Schema;

var measurement = new Schema({
    name: {type: String, required: true},
    instruction: {type: String, required: true}
});

var size = new Schema({
    name: {type: String, required: true},
    lengths: [String]
});

var chartSchema = new Schema({
    measurements: [measurement],
    sizes: [size]
});

var Chart = mongoose.model('gh1204', chartSchema);

Chart.create({}, function(error, chart) {
  assert.ifError(error);
  chart.sizes.push({ name: 'bacon', lengths: ['25'] });
  chart.save(function(error, chart) {
    assert.ifError(error);
    assert.equal(chart.sizes[0].lengths.length, 1);
    assert.equal(chart.sizes[0].lengths[0], '25');
    console.log('done');
    process.exit(0);
  });
});

Bisher keine Würfel, es läuft wie erwartet. Können Sie das obige Beispiel ändern, um das Problem zu demonstrieren, das Sie sehen? Ich war nicht in der Lage, Ihre Prosabeschreibungen in Code zu übersetzen.

Klar, ich schaue mir das heute Abend etwas später an.

Probieren Sie es aus?

/*jslint node:true */
"use strict";

var mongoose = require('mongoose');
mongoose.set('debug', true);
var util = require('util');
var assert = require('assert');

mongoose.connect('mongodb://localhost:27017/gh1204');

var Schema = mongoose.Schema;

var measurement = new Schema({
  name: {type: String, required: true},
  instruction: {type: String, required: true}
  });

var size = new Schema({
  name: {type: String, required: true},
  lengths: [String]
    });

var chartSchema = new Schema({
  measurements: [measurement],
  sizes: [size]
    });

var Chart = mongoose.model('gh1204', chartSchema);

Chart.create({}, function (error, chart) {
    assert.ifError(error);
    chart.sizes.push({ name: 'bacon', lengths: ['25'] });
    chart.save(function (error, chart) {
      assert.ifError(error);
     assert.equal(chart.sizes[0].lengths.length, 1);
     assert.equal(chart.sizes[0].lengths[0], '25');
     console.log('Created Index');

    chart.sizes[0].lengths[0] = "20";

  chart.save(function (error, chart) {
    assert.ifError(error);
    assert.equal(chart.sizes[0].lengths.length, 1);
    assert.equal(chart.sizes[0].lengths[0], '25');
    console.log('Created Index');
    process.exit(0);

    });
  });
});

Hier ist meine Konsolenausgabe in meiner Knotenkonsole:

Crispens-MacBook-Pro:mongooseTest crispensmith$ node index.js
Mongoose: gh1204.insert({ _id: ObjectId("5580d0c44d32b07971dfd281"), sizes: [], measurements: [], __v: 0 })   
Mongoose: gh1204.update({ __v: 0, _id: ObjectId("5580d0c44d32b07971dfd281") }) { '$set': { measurements: [] }, '$pushAll': { sizes: [ { name: 'bacon', _id: ObjectId("5580d0c44d32b07971dfd282"), lengths: [ '25' ] } ] }, '$inc': { __v: 1 } }  
Created Index
Mongoose: gh1204.update({ __v: 1, _id: ObjectId("5580d0c44d32b07971dfd281") }) { '$set': { measurements: [] }, '$inc': { __v: 1 } }  

/Users/crispensmith/Documents/mongooseTest/node_modules/mongoose/node_modules/mpromise/lib/promise.js:108
if (this.ended && !this.hasRejectListeners()) throwgrund;
^
AssertionError: "20" == "25"
bei EventEmitter.(/Users/crispensmith/Documents/mongooseTest/index.js:44:14)
bei EventEmitter.(/Users/crispensmith/Documents/mongooseTest/node_modules/mongoose/node_modules/mpromise/lib/promise.js:175:45)
bei EventEmitter.emit (events.js:98:17)
bei Promise.safeEmit (/Users/crispensmith/Documents/mongooseTest/node_modules/mongoose/node_modules/mpromise/lib/promise.js:81:21)
bei Promise.fulfill (/Users/crispensmith/Documents/mongooseTest/node_modules/mongoose/node_modules/mpromise/lib/promise.js:94:24)
bei Promise.resolve (/Users/crispensmith/Documents/mongooseTest/node_modules/mongoose/lib/promise.js:113:23)
am Modell.(/Users/crispensmith/Documents/mongooseTest/node_modules/mongoose/lib/document.js:1569:39)
at next_ (/Users/crispensmith/Documents/mongooseTest/node_modules/mongoose/node_modules/hooks-fixed/hooks.js:89:34)
bei EventEmitter.fnWrapper (/Users/crispensmith/Documents/mongooseTest/node_modules/mongoose/node_module/hooks-fixed/hooks.js:171:15)
bei EventEmitter.(/Users/crispensmith/Documents/mongooseTest/node_modules/mongoose/node_modules/mpromise/lib/promise.js:175:45)
bei EventEmitter.emit (events.js:98:17)
bei Promise.safeEmit (/Users/crispensmith/Documents/mongooseTest/node_modules/mongoose/node_modules/mpromise/lib/promise.js:81:21)
bei Promise.fulfill (/Users/crispensmith/Documents/mongooseTest/node_modules/mongoose/node_modules/mpromise/lib/promise.js:94:24)
at p1.then.then.self.isNew (/Users/crispensmith/Documents/mongooseTest/node_modules/mongoose/lib/model.js:254:27)
bei newTickHandler (/Users/crispensmith/Documents/mongooseTest/node_modules/mongoose/node_modules/mpromise/lib/promise.js:229:18)
at process._tickCallback (node.js:442:13)

(Entschuldigung, ich hatte eine Menge Zeit, dies in den richtigen Abschlag zu bringen, das ist das Beste, was ich tun konnte.)
Und hier ist die mongoDB-Ausgabe:

  > db.gh1204.find();
  { "_id" : ObjectId("5580d0c44d32b07971dfd281"), "sizes" : [ { "name" : "bacon", "_id" : ObjectId("5580d0c44d32b07971dfd282"), "lengths" : [ "25" ] } ], "measurements" : [ ], "__v" : 2 }

Aus CRUD-Perspektive besteht das Wesentliche darin, dass es kein Problem mit dem Erstellen oder Lesen gibt, wenn Updates fehlschlagen.

TLDR;
In meinem Anwendungsfall bedeutet dies, dass ein einmal definierter Längensatz für eine Messung nicht mehr bearbeitet werden kann. Ich arbeite an einem Modeeinzelhandelsprojekt mit zwei Anwendungsfällen, die Arrays innerhalb von Unterdokumenten erfordern. Der erste Anwendungsfall besteht darin, dass Vanilla-Benutzer ein eigenes Profil haben und in der Lage sein sollten, dieses zu pflegen. Der zweite Anwendungsfall besteht darin, dass die Admin-Rolle benutzerdefinierte Diagramme für die Auflistungen für verschiedene Produkttypen (Tops, Bottoms usw.) erstellen kann. Normalerweise würde ich nicht erwarten, dass das Admin-Profil diese sekundären Diagramme bearbeiten muss, aber es wäre trotzdem schön, es für diesen Anwendungsfall zu haben.

Theoretisch stellt die aktuelle Funktionalität tatsächlich ein nettes (zufälliges) unveränderliches Objekt dar, aber der Arbeitsaufwand, um es als unveränderliches Objekt zu verwenden, anstatt nur zu bearbeiten, ist nicht trivial.

Ich sehe, worum es geht - klassischer Fall der ersten Frage auf der Mungo-FAQ . Mongoose kann keine Änderungen verfolgen, wenn Sie einen Array-Index direkt ohne etwas wie ES6-Proxys oder ES7 Object.observe() festlegen. Verwenden

chart.sizes[0].lengths.set(0, "20");

oder

chart.sizes[0].lengths[0] = '20';
chart.markModified('sizes.0.lengths.0');

Okay, das macht Sinn. Seltsamerweise (und dies kann auf meine Suchstrategien zurückzuführen sein) konnte ich nur Funktionen finden, die 1x pro Speichervorgang ausgeführt wurden, und keine davon hätte für den Anwendungsfall gepasst.

Ich sehe, was das Problem ist - klassischer Fall der ersten Frage auf der Mungo-FAQ.

Danke, in meinem Fall war es dieses Problem! :)

model.myArray[index] = anyValue

wird

model.myArray.set(index, anyValue)

Das war auch für mich ein Thema, ich wünschte, ich hätte diesen Thread früher gesehen!

Wenn Sie sich nicht sicher sind, ob Mungo über Änderungen benachrichtigt wird, können Sie

doc.markModified('propChanged')
doc.save() // works

Ich habe mein Problem auch mit Array.set() gelöst.
:+1:

Habe den gleichen Fehler, das ist mein Code-Snippet
image
Und das ist der Mungo-Log
image
Das bekomme ich vom Postboten
image

Stimmt was nicht?

++ BEARBEITEN ++

  1. Versucht, Array.set zu verwenden, bekomme das gleiche Ergebnis

Ich denke, die bessere Frage ist, ob etwas richtig ist. Sie führen eine asynchrone Speicherung im if (req.body.invite != []) Block durch und ändern dann das invited Array im Resolve-Callback. Der Aufruf von nRoom.invited.push() erfolgt immer nach dem 2. Aufruf von save(). Außerdem ist req.body.invite != [] immer wahr, da [] == [] in JS falsch ist.

@vkarpov15 Haha Ok, danke für die Antwort! 👍 Habe viel zu lernen :)

@peterkrieg Danke!

Hallo, ich habe dieses Problem beim Aktualisieren von tief eingebetteten Dokumenten. Hat jemand einen Rat? Mein Code ist unter:

https://stackoverflow.com/questions/51426326/updating-deeply-embedded-documents-with-mongodb-2-6-12

@thehme welche Version von Mungo verwendest du?

in dieser Zeile, {$set: {["dT." + index +".ts." + i + ".th"]: newValue}} , fühlen sich die eckigen Klammern für mich fehl am Platz an. macht es einen Unterschied, stattdessen {$set: { "dT." + index +".ts." + i + ".th": newValue } } verwenden?

$set docs zeigt nur einen String

Besuchen Sie uns auf Slack , um in Echtzeit darüber zu sprechen 👍

Ich verwende 5.5.11 und habe immer noch dieses Problem. Ich habe alle in diesem Thread vorgeschlagenen Lösungen ohne Erfolg ausprobiert. Mein Dokumentenlayout sieht so aus:

{
  myObj: {
    myArr: [{ key: "value" }]
  }
}

myDoc.myObj.myArr.push({ key: "value2" });
// debugging myDoc shows new embedded doc
await myDoc.save();
// console shows new embedded doc is gone

Bearbeiten: Auf die neueste Version (5.7.6) aktualisiert und das Problem besteht immer noch.

Das folgende Skript funktioniert auf Mongoose 5.7.6 einwandfrei. Bitte eröffnen Sie eine neue Ausgabe und folgen Sie der Ausgabevorlage.

const mongoose = require('mongoose');

run().catch(err => console.log(err));

async function run() {
  await mongoose.connect('mongodb://localhost:27017/test', {
    useNewUrlParser: true,
    useUnifiedTopology: true
  });
  await mongoose.connection.dropDatabase();

  const schema = mongoose.Schema({ 
    myObj: { myArr: [{ key: String }] }
  });
  const Model = mongoose.model('Test', schema);


  await Model.create({ myObj: { myArr: [{ key: 'value' }] } });
  const myDoc = await Model.findOne();

  myDoc.myObj.myArr.push({ key: "value2" });
  await myDoc.save();

  console.log(myDoc.myObj.myArr);

  const saved = await Model.findOne();
  console.log(myDoc.myObj.myArr);
}
War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

ghost picture ghost  ·  3Kommentare

Igorpollo picture Igorpollo  ·  3Kommentare

gustavomanolo picture gustavomanolo  ·  3Kommentare

p3x-robot picture p3x-robot  ·  3Kommentare

lukasz-zak picture lukasz-zak  ·  3Kommentare