Mongoose: Leeres Array wird gespeichert, wenn eine Eigenschaft auf ein Schema verweist

Erstellt am 6. Feb. 2013  ·  39Kommentare  ·  Quelle: Automattic/mongoose

var FooSchema = new Schema({});
var Foo = mongoose.model("Foo", FooSchema);
var BarSchema = new Schema({
    foos: [Foo.schema]
});
var Bar = mongoose.model("Bar", BarSchema);

var b = new Bar();
b.save();

Dadurch wird ein leeres Array von b.foos erstellt, anstatt die Eigenschaft einfach undefiniert zu lassen.

enhancement

Hilfreichster Kommentar

@antonioaltamura Ich habe dieses Problem vergessen, das Folgende funktioniert in Mungo 4.6:

const CollectionSchema = new Schema({
 field1: { type: [String], default: void 0 }, // <-- override the array default to be undefined
});

const Collection = mongoose.model('test', CollectionSchema);

Collection.create({}).
  then(doc => { console.log(doc); return doc; }).
  then(() => { process.exit(0); });

Alle 39 Kommentare

Dies ist beabsichtigt. Alle Standardwerte werden gespeichert, sodass die Mungo-Anwendungsansicht des Dokuments mit einer Nicht-Mungo-Anwendungsansicht identisch ist.

Ok, wie mache ich den Standard null? ;-) Der Anwendungsfall hier ist, dass es in Mongo teuer ist, alle Bars mit leeren Foos anzufordern. Ich muss eine weitere Eigenschaft hinzufügen, um diesen Zustand zu verfolgen, was meinen Code nur noch komplizierter macht.

Ich verstehe. Dadurch können Sie das Speichern leerer Foos in neuen Dokumenten überspringen:

BarSchema.pre('save', function (next) {
  if (this.isNew && 0 === this.foos.length) {
    this.foos = undefined;                                                                                                                                   
  }
  next();
})

Süss. Das ist für mich ein ausreichender Workaround.

Soll dieser Workaround noch funktionieren? (Mungo 3.8.3 hier)

Mathieumg: bei mir funktioniert es nicht (3.8.15)

Ja, ich denke, dieses Thema verdient etwas Aufmerksamkeit. (Es ist ziemlich unbedeutend, aber
Ich bin daran interessiert, Pre für andere Dinge zu verwenden :))

Am Mo, 25.08.2014 um 16:49 Uhr, Mathieu M-Gosselin <
[email protected]> schrieb:

Vielleicht sollte ich ein neues Thema eröffnen, damit dies bemerkt wird?


Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an
https://github.com/LearnBoost/mongoose/issues/1335#issuecomment -53328195
.

+1
Pre-Workaround funktioniert nicht für 3.8.15
Die einzige Alternative, die ich denke, ist null oder false zu haben, um die Abfrage zu vereinfachen, aber das fügt auch einige Bytes hinzu und hat erhebliche Auswirkungen auf große Datenbanken.

        this.foos = null;

@gaurang171 @Nepoxx können Sie mir einen Code zur Verfügung stellen, der dies reproduziert? Der Workaround funktioniert in unseren Testfällen einwandfrei (siehe Testfall )

Dies ist immer noch ein Problem für mich mit 4.0.1.

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

var barSchema = new mongoose.Schema({
  baz: String
});

var fooSchema = new mongoose.Schema({
  bars: [barSchema]
});

var Foo = mongoose.model('Foo', fooSchema);

var foo = new Foo();
console.log(foo); // { _id: 55256e20e3c38434687034fb, bars: [] }

foo.save(function(err, foo2) {
  console.log(foo2); // { __v: 0, _id: 55256e20e3c38434687034fb, bars: [] }

  foo2.bars = undefined;
  foo2.save(function(err, foo3) {
    console.log(foo3); // { __v: 0, _id: 55256e20e3c38434687034fb, bars: undefined }

    Foo.findOne({ _id: foo3._id }, function(err, foo4) {
      console.log(foo4); // { _id: 55256e20e3c38434687034fb, __v: 0, bars: [] }

      mongoose.disconnect();
    });
  });
});

@viking Es wurde nie "fixiert". Es wurde ein Workaround bereitgestellt . Sie sollten testen, ob diese Problemumgehung noch funktioniert, da diesbezüglich widersprüchliche Informationen vorliegen. =)

Einen Workaround habe ich schon parat. Mich würde interessieren, ob sich dieses Verhalten ändert oder nicht.

Entschuldigung, Sie haben nicht gesagt, dass Sie wissen möchten, ob sich das Verhalten ändert oder nicht.

Ich bin mir eigentlich nicht sicher, warum dieses Problem von @vkarpov15 erneut geöffnet wurde, was die Frage

Ja, dieses Verhalten wird sich in naher Zukunft nicht ändern. Ich habe dieses Problem erneut geöffnet, damit ich es bei Gelegenheit untersuchen sollte, weil ich bei jedem Kommentar, der im Mungo-Github eingeht, verstehe, was vor sich geht, und auf diesen hatte ich keine gute Antwort zu meinem Kopf. Bei genauerer Betrachtung können wir dies in naher Zukunft aufgrund von Semver nicht wirklich ändern, aber es ist etwas zu untersuchen, ob dies für viele Benutzer ein Problem ist.

Ich habe diese Problemumgehung mit einer Wendung verwendet, weil Sie manchmal ein leeres Array schreiben möchten und manchmal nicht.

Mein Presave sieht also so aus:

        var schema = mongoose.Schema({
                   ...
           "children": [ String ]
                   ...
            });
        // turn off the saving of empty children if there are no children in the schema.
        schema.pre('save', function (next) {
            if (this.isNew) {
                if (this.children.length == 0) {
                    this.children = undefined;       
                }
                else if (this.children.length == 1 && this.children[0] == null) {
                    this.children = [];
                }                                                                                                                    
            }
            next();
        });
        var model = mongoose.model('MyDocument', schema);

Und um dann leere Kinder zu schreiben, verwende ich Folgendes:

idea.children = [null];

Wobei dies abgestreift ist:

idea.children = [];

Dies funktioniert möglicherweise nicht für Sie, aber Sie sollten in der Lage sein, eine andere Art von Schema zu entwickeln.

Dies ist ein ziemlich großes Problem, wenn Sie Arrays in verschachtelten Objekten haben.

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

var quxSchema = new mongoose.Schema({ qux: String });
var fooSchema = new mongoose.Schema({ foo: { bar: { baz: [ quxSchema ] } } });
var Foo = mongoose.model('Foo', fooSchema);

var foo = new Foo({foo: undefined})
foo.save(function(err, foo) {
  console.log(JSON.stringify(foo)); // {"__v":0,"_id":"557ae56480f047fd4ff4ab26","foo":{"bar":{"baz":[]}}}
  Foo.find({ _id: foo._id }, function(err, foos) {
    console.log(JSON.stringify(foos[0])); // {"_id":"557ae56480f047fd4ff4ab26","__v":0,"foo":{"bar":{"baz":[]}}}
  });
});

Hier erwarte ich, dass foo fehlt, und stattdessen habe ich dieses verschachtelte Objekt mit einem leeren Array.

Aber kann dir ein Pre-Save hier nicht weiterhelfen? Anstatt {foo: undefined} zu haben, könnten Sie einen eindeutigen Typ von foo verwenden, um ein null-foo zu kennzeichnen, und dann das foo im Presave entfernen.

Hässlich aber.

Ich bin gerade auf das gleiche Problem wie @viking gestoßen . hast du einen brauchbaren workaround gefunden?

Ich habe es mit dem 'pre'-Speichern versucht.. aber es kommt immer noch manchmal vor, ich bin mir nicht sicher, wie viele verschiedene Anwendungsfälle ich abdecken muss, um dies zu erledigen. Mein größtes Problem dabei ist, dass der JSON eigentlich ein Objekt und kein Array liefern sollte. Dies ist eine fatale Ausnahme für meinen Json-Parser, der versucht, diese Dinge Java-Objekten zuzuordnen.

Beispiel:

var subA = mongoose.Schema({
     name: String,
     a: String
});

var subB = mongoose.Schema({
     name: String,
     b: String
});

var A = mongoose.Schema({
name: String,
filter: {
        lastupdate  : { type: Date, default: Date.now },
        subA: [{type: Schema.Types.ObjectId, ref: 'subA', unique: true}],
        subB: [{type: Schema.Types.ObjectId, ref: 'subB', unique: true}]
    }
});

var ModelA = mongoose.model('A', A);

var obj = new modelA();
obj.save();

... daraus ergibt sich:

{
 filter: []
}

aber es sollte entweder einfach nicht da sein oder zumindest ein Objekt und kein leeres Array sein:

{
 filter: {} 
}

jemand eine Idee, wie man das lösen kann?

Grüße

Ich denke, Sie können dies zum Laufen bringen, offensichtlich nicht mit dem Presave, das ich gepostet habe, da Kinder dabei ein Array und kein Objekt sind. Ich habe dies nicht versucht, aber wäre es nicht möglich, ein leeres "Enten-Typing"-Objekt zu verwenden, um ein TATSÄCHLICHES leeres Objekt anzuzeigen?

Ihr Presave könnte also sein:

if (this.isNew) {
    if (typeof(this.filter.empty) != "undefined") {
        this.filter = {};
    }
}

Um dann einen tatsächlichen leeren Filter zu erstellen, können Sie Folgendes verwenden:

var obj = new modelA();
obj.filter = { empty: true };
obj.save();

Beachten Sie, dass es eigentlich egal ist, wenn das Schema nicht "leer" enthält, es macht es nie zum Mungo.

Irgendwie habe ich es geschafft, das Problem zu lösen.. Ich habe tatsächlich etwas sehr ähnliches gemacht. Habe gerade deine Antwort gesehen und wollte mich bedanken :)

Grüße
Simon

Ich hatte viele Probleme mit dieser Lösung, als es verschachtelte Schemas mit optionalen Array Feldern gab. Ich habe dies gelöst, indem ich einen neuen Typ erstellt habe:

optional_array = 
  type: Mixed
  validate: 
    validator: (v) ->
      return v instanceof Array
    message: '{VALUE} needs to be an array.'

und dann alle meine Felder auf optional_array anstelle von Array .

@simllll Ich verwende einen Platzhalter null für fehlende Objekte und entferne dann das null bevor ich das Objekt verwende.

Entschuldigung, aber das ist ein schreckliches Design. Sie sollten nicht in meinem Namen entscheiden, ob ich möchte, dass es ein leeres Array ist oder überhaupt nicht gesetzt wird. Stellen Sie zumindest eine Option zum Deaktivieren dieser Funktion bereit.

Stimme @ifeltsweet zu

Gibt es nach Monaten eine richtige Lösung ohne Vorhaken? Ich habe Dutzende von Array-Feldern, ich sollte eins nach dem anderen in einem Pre-Hook behandeln ...

@antonioaltamura Ich habe dieses Problem vergessen, das Folgende funktioniert in Mungo 4.6:

const CollectionSchema = new Schema({
 field1: { type: [String], default: void 0 }, // <-- override the array default to be undefined
});

const Collection = mongoose.model('test', CollectionSchema);

Collection.create({}).
  then(doc => { console.log(doc); return doc; }).
  then(() => { process.exit(0); });

@vkarpov15 Ihr Fix funktioniert bei mir nicht auf Mungo 4.7.0. Funktioniert immer noch gut für dich? Es enthält nicht den Wert für die Erstellung eines neuen Objekts, aber wenn ich ein Model.save durchführe, versucht es, es erneut hinzuzufügen.

@cpup22 können Sie ein Codebeispiel bereitstellen? Das referenzierte Skript funktioniert bei mir auf 4.7.1 einwandfrei.

Ich denke, der Workaround von https://github.com/Automattic/mongoose/issues/2363#issuecomment -171988413 ist viel sauberer. Es ermöglicht die Standardeinstellung null auch wenn default: null fehlschlägt:

const CollectionSchema = new Schema({
  field1: { type: [String] },
});
CollectionSchema.methods.postconstructed = function () {
  this.field1 = null;
};
CollectionSchema.queue('postconstructed');

const Collection = mongoose.model('test', CollectionSchema);

Collection.create({}).then(doc => { console.log(doc); process.exit(0) });

Egal, es funktioniert nicht, wenn ein Wert übergeben wird (zB new Collection({ field1: [] }) bekommt field1 === null . Wenn nur ein Konstruktions-Hook passiert wäre, bevor die übergebenen Daten angewendet wurden und nach der Vorgabe Es wird ein leeres Array erstellt oder dieser Fehler wurde behoben, um default: null für Arrays zuzulassen.

Oh, sieht so aus, als ob das funktioniert:

const CollectionSchema = new Schema({
  field1: { type: [String], default: () => null },
});

Das Ding vom Array-Typ respektiert Funktionen aufrichtiger als Werte. Dies hat kein Problem mit dem Überschreiben eines übergebenen null , [] oder nicht leeren Arraywerts über Collection.create() oder new Collection() .

@ vkarpov15 hallo, also selbst wenn ich den Schlüsselwert für den Typ Array in meinem Mungo-Schema auf null ändere, bevor mongo db ein Dokument speichert, und wenn das Array leer ist, wird es als Schlüssel = null gespeichert.
du hast irgendwelche lösungen

@vikramkalta Bitte stellen Sie Codebeispiele zur Verfügung, Konvertierungen von Prosa in Code sind fehleranfällig.

Hallo, ich benutze den Mungo 4.6.5 und speichere noch ein leeres Array mit diesen Lösungen.

services: [{ type: Schema.Types.ObjectId, ref: 'Service', default: () => null }],

services: [{ type: Schema.Types.ObjectId, ref: 'Service', default: void 0 }],

@keyboard99 bitte folgen Sie den Anweisungen in https://github.com/Automattic/mongoose/issues/1335#issuecomment -252129243 . Ihr Code legt einen Standard für die einzelnen Array-Elemente fest, nicht für das Array selbst

Für diese Probleme gibt es eine einfache Lösung. Geben Sie einfach den Standardwert als undefiniert an. Wie folgt:

var BarSchema = new Schema({
    foos: {
        type: [FooSchema],
        default: undefined
    }
});

Funktioniert bei mir

Aber das führt zu Komplikationen mit array.push
:(

@GeneralG irgendwelche Komplikationen außer der offensichtlichen, dass man doc.array = doc.array || []; bevor man push() anruft?

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen