Mongoose: La matriz vacía se guarda cuando una propiedad hace referencia a un esquema

Creado en 6 feb. 2013  ·  39Comentarios  ·  Fuente: 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();

Eso creará una matriz vacía de b.foos en lugar de dejar la propiedad sin definir.

enhancement

Comentario más útil

@antonioaltamura Me olvidé de este problema, lo siguiente funciona en mongoose 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); });

Todos 39 comentarios

Esto es por diseño. Todos los valores predeterminados se guardan para que la vista de aplicaciones de mangosta del documento sea idéntica a una vista de aplicaciones sin mangosta.

Ok, ¿cómo hago para que el valor predeterminado sea nulo? ;-) El caso de uso aquí es que es caro en mongo solicitar todos los bares que tienen foos vacíos. Tengo que agregar otra propiedad para rastrear ese estado, lo que complica aún más mi código.

Veo. Esto le permitirá omitir guardar foos vacíos en documentos nuevos:

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

Dulce. Eso es un trabajo bastante bueno para mí.

¿Se supone que esta solución alternativa todavía funciona? (mangosta 3.8.3 aquí)

Mathieumg: no me funciona (3.8.15)

Sí, creo que este tema merece atención. (Es bastante menor, pero
Estoy interesado en usar pre para otras cosas :))

El lunes, 25 de agosto de 2014 a las 4:49 p.m., Mathieu M-Gosselin <
[email protected]> escribió:

¿Quizás debería abrir una nueva edición para que esto se haga notar?

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/LearnBoost/mongoose/issues/1335#issuecomment -53328195
.

+1
La solución previa no funciona para 3.8.15
Creo que la única alternativa es tener nulo o falso para simplificar la consulta, pero eso también agrega algunos bytes y tiene un efecto significativo en una base de datos grande.

        this.foos = null;

@ gaurang171 @Nepoxx ¿ Pueden proporcionarme algún código que reproduzca esto? La solución alternativa funciona bien en nuestros casos de prueba (consulte el caso de prueba )

Esto sigue siendo un problema para mí al usar 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 Nunca fue 'arreglado'. Se proporcionó una solución alternativa . Debe probar para ver si esa solución todavía funciona, ya que hay información contradictoria al respecto. =)

Ya tengo una solución alternativa. Me interesa saber si este comportamiento cambiará o no.

Lo siento, no dijiste que querías saber si el comportamiento cambiará o no.

En realidad, no estoy seguro de por qué @ vkarpov15 volvió a abrir este problema, lo que plantea la pregunta de si este comportamiento cambiará o no. Creo que el problema es la compatibilidad con versiones anteriores. Probablemente debería haberse decidido antes de que saliera 4.x.

Sí, este comportamiento no cambiará en un futuro próximo. Volví a abrir este problema, así que sabía investigarlo cuando tuviera la oportunidad, porque me aseguro de entender lo que sucede con cada comentario que llega al github de mangosta y este fue uno para el que no tuve una buena respuesta. a la parte superior de mi cabeza. Mirando más de cerca, realmente no podemos cambiar esto en un futuro cercano debido a semver, pero algo para investigar si es un punto débil para muchos usuarios.

He utilizado esta solución alternativa con un giro porque a veces QUIERES escribir una matriz vacía y otras no.

Entonces Mi presave se ve así:

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

Y luego para escribir niños vacíos uso esto:

idea.children = [null];

Considerando que esto está despojado:

idea.children = [];

Es posible que esto no funcione para usted, pero debería poder idear algún otro tipo de esquema.

Este es un problema bastante grande cuando tiene matrices dentro de objetos anidados.

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":[]}}}
  });
});

Aquí espero que falte foo , y en su lugar tengo este objeto anidado con una matriz vacía.

¿Pero no te puede ayudar una reserva previa? en lugar de tener {foo: undefined}, podría usar un tipo único de foo para significar un foo nulo, y luego eliminar el foo en el presave.

Aunque feo.

Me encontré con el mismo problema que @viking . ¿Encontraste alguna solución alternativa útil?

Lo intenté con la forma de guardar 'pre' ... pero todavía ocurre a veces, no estoy seguro de cuántos casos de uso diferentes tengo que cubrir para hacer esto. Mi mayor problema de esto es que el JSON realmente debería entregar un objeto, y no una matriz. esta es una excepción fatal para mi analizador json que intenta mapear estas cosas a objetos java.

ejemplo:

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

... esto resulta en:

{
 filter: []
}

pero debería simplemente no estar allí, o al menos ser un objeto y no una matriz vacía:

{
 filter: {} 
}

alguien alguna idea de cómo resolver esto?

Saludos

Creo que puede hacer que esto funcione, obviamente no con el presave que publiqué, porque eso implicaba que los niños fueran una matriz, no un objeto. No he intentado esto, pero ¿no sería posible utilizar un objeto vacío "escribiendo pato" para indicar uno vacío REAL?

Entonces su presave podría ser:

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

Luego, para hacer un filtro vacío real, puede usar:

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

Tenga en cuenta que en realidad no importa si el esquema no tiene "vacío", nunca llega a ser mangosta.

De alguna manera logré solucionar el problema. De hecho, hice algo muy similar. Sin embargo, solo vi tu respuesta y quería darte las gracias :)

Saludos
Simón

Tenía muchos problemas con esta solución cuando había esquemas anidados con campos Array opcionales. Resolví esto creando un nuevo tipo:

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

y luego estableciendo todos mis campos en optional_array lugar de Array .

@simllll Utilizo un marcador de posición null para los objetos que faltan y luego elimino null antes de usar el objeto.

Lo siento, pero este es un diseño horrible. No debería decidir en mi nombre si quiero que sea una matriz vacía o no esté configurada en absoluto. Al menos proporcione una opción para deshabilitar esta función.

De acuerdo con @ifeltsweet

Después de meses, ¿hay una solución adecuada sin pre-gancho? Tengo docenas de campos de matriz, debería manejar uno por uno en un gancho previo ...

@antonioaltamura Me olvidé de este problema, lo siguiente funciona en mongoose 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 tu solución no me funciona en mongoose 4.7.0. ¿Sigues funcionando bien para ti? No incluye el valor en la creación de un nuevo objeto, pero cuando hago un Model.save, intenta agregarlo nuevamente.

@ cpup22 ¿ puede proporcionar una muestra de código? El script al que se hace referencia funciona bien para mí en 4.7.1.

Creo que la solución alternativa de https://github.com/Automattic/mongoose/issues/2363#issuecomment -171988413 es mucho más limpia. Permite el valor predeterminado de null incluso cuando default: null falla:

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

No importa, no funciona cuando se pasa un valor (por ejemplo, new Collection({ field1: [] }) obtiene field1 === null . Si solo hubiera un gancho de construcción que sucediera antes de que se aplicaran los datos pasados ​​y después de los valores predeterminados Se crea una matriz vacía O este error se corrigió para permitir default: null para matrices.

Oh, parece que esto funciona:

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

El tipo de matriz respeta las funciones más sinceramente que los valores. Esto no tiene ningún problema con sobrescribir un valor de matriz null , [] pasado o no vacío a través de Collection.create() o new Collection() .

@ vkarpov15 hola, así que incluso si cambio el valor de la clave para el tipo de matriz a nulo en mi esquema de mangosta antes de que mongo db guarde un documento, y cuando la matriz está vacía, lo guarda como clave = nulo.
tienes alguna solución

@vikramkalta , proporcione ejemplos de código, las conversiones de prosa a código son propensas a errores.

Hola, estoy usando la mangosta 4.6.5 y todavía tengo que guardar una matriz vacía con estas soluciones.

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

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

@ keyboard99 , siga las instrucciones en https://github.com/Automattic/mongoose/issues/1335#issuecomment -252129243. su código establece un valor predeterminado para los elementos individuales de la matriz, no para la matriz en sí

Hay una solución sencilla para esos problemas. Simplemente proporcione el valor predeterminado como indefinido. De la siguiente manera:

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

Funciona para mi

Pero eso causará complicaciones con array.push
:(

@GeneralG alguna complicación que no sea la obvia de tener que hacer doc.array = doc.array || []; antes de llamar a push() ?

¿Fue útil esta página
0 / 5 - 0 calificaciones