Mongoose: A matriz vazia é salva quando uma propriedade faz referência a um esquema

Criado em 6 fev. 2013  ·  39Comentários  ·  Fonte: 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();

Isso criará um array vazio de b.foos em vez de apenas deixar a propriedade indefinida.

enhancement

Comentários muito úteis

@antonioaltamura Esqueci-me deste problema, o seguinte funciona no mangusto 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 comentários

Isso ocorre por design. Todos os valores padrão são salvos para que a visualização dos aplicativos do mangusto do documento seja idêntica a uma visualização dos aplicativos não mangusto.

Ok, como eu torno o padrão nulo? ;-) O caso de uso aqui é que é caro no mongo solicitar todos os Bar's que têm foos vazios. Tenho que adicionar outra propriedade para rastrear esse estado, o que só complica ainda mais meu código.

Entendo. Isso permitirá que você ignore o salvamento de foos vazios em novos documentos:

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

Doce. É uma solução alternativa boa o suficiente para mim.

Esta solução alternativa ainda deveria funcionar? (mangusto 3.8.3 aqui)

Mathieumg: não funciona para mim (3.8.15)

Sim, acho que esse assunto merece atenção. (É bem menor, mas
Estou interessado em usar o pre para outras coisas :))

Na segunda-feira, 25 de agosto de 2014 às 16:49, Mathieu M-Gosselin <
notificaçõ[email protected]> escreveu:

Talvez eu deva abrir uma nova edição para que isso seja notado?

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/LearnBoost/mongoose/issues/1335#issuecomment -53328195
.

+1
A pré-solução não está funcionando para 3.8.15
a única alternativa que eu acho é ter null ou false para simplificar a consulta, mas que também adiciona alguns bytes e tem um efeito significativo no banco de dados grande.

        this.foos = null;

@ gaurang171 @Nepoxx vocês podem me fornecer algum código que reproduza isso? A solução alternativa funciona bem em nossos casos de teste (consulte o caso de teste )

Isso ainda é um problema para mim, usando o 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 foi 'consertado'. Uma solução alternativa foi fornecida . Você deve testar para ver se a solução alternativa ainda funciona, pois há informações conflitantes sobre isso. =)

Já tenho uma solução alternativa em vigor. Estou interessado em saber se esse comportamento vai mudar ou não.

Desculpe, você não disse que queria saber se o comportamento mudará ou não.

Na verdade, não sei por que esse problema foi reaberto por @ vkarpov15 , o que

Sim, este comportamento não mudará no futuro próximo. Eu reabri este problema para que eu soubesse investigá-lo quando pudesse, porque eu me certifico de entender o que está acontecendo com cada comentário que chega ao github do mangusto e este foi um para o qual eu não tive uma boa resposta para fora do topo da minha cabeça. Olhando mais de perto, não podemos realmente mudar isso em um futuro próximo por causa do semver, mas algo para investigar se é um ponto problemático para muitos usuários.

Eu usei esta solução alternativa com uma torção porque às vezes você DESEJA escrever um array vazio e às vezes não.

Minha pré-gravação é assim:

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

E então, para escrever crianças vazias, eu uso isto:

idea.children = [null];

Considerando que isso é despojado:

idea.children = [];

Isso pode não funcionar para você, mas você deve ser capaz de criar algum outro tipo de esquema.

Este é um grande problema quando você tem arrays dentro de objetos aninhados.

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

Aqui, espero que foo esteja faltando e, em vez disso, tenho este objeto aninhado com uma matriz vazia.

Mas um pré-salvamento não pode ajudá-lo aqui? em vez de ter {foo: undefined}, você poderia usar um tipo único de foo para significar um foo nulo e, em seguida, remover o foo no pré-salvamento.

Embora feio.

Acabei de ter o mesmo problema que o @viking . você encontrou alguma solução alternativa utilizável?

Eu tentei com o método 'pré' salvar .. mas ainda ocorre às vezes, não tenho certeza de quantos casos de uso diferentes eu tenho que cobrir para fazer isso. Meu maior problema com isso é que o JSON deve realmente entregar um objeto, e não um array. esta é uma exceção fatal para meu analisador json, que tenta mapear essas coisas para objetos java.

exemplo:

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

... isso resulta em:

{
 filter: []
}

mas deve simplesmente não estar lá, ou pelo menos ser um objeto e não uma matriz vazia:

{
 filter: {} 
}

alguém alguma ideia de como resolver isso?

Saudações

Acho que você pode fazer isso funcionar, obviamente não com o pré-salvamento que postei, porque isso envolvia crianças sendo um array, não um objeto. Eu não tentei isso, mas não seria possível usar um objeto vazio de "digitação de pato" para significar um objeto vazio REAL?

Portanto, sua pré-gravação pode ser:

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

Então, para fazer um filtro vazio real, você pode usar:

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

Observe que, na verdade, não importa se o esquema não contém "vazio", ele nunca chega ao mangusto.

De alguma forma, consegui superar o problema. Na verdade, fiz algo muito semelhante. Acabei de ver sua resposta e queria dizer obrigado :)

Saudações
Simon

Eu estava tendo muitos problemas com essa solução quando havia esquemas aninhados com campos Array opcionais. Resolvi isso criando um novo tipo:

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

e definir todos os meus campos como optional_array vez de Array .

@simllll Eu uso um marcador de posição null para objetos ausentes e, em seguida, removo o null antes de usar o objeto.

Desculpe, mas este é um design horrível. Você não deve decidir em meu nome se quero que seja um array vazio ou se não o quero. Forneça pelo menos uma opção para desativar esse recurso.

Concordo com @ifeltsweet

Depois de meses, existe uma solução adequada sem pré-gancho? Eu tenho dezenas de campos de array, devo lidar um por um em um pré-gancho ...

@antonioaltamura Esqueci-me deste problema, o seguinte funciona no mangusto 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 sua correção não funciona para mim no mongoose 4.7.0. Ainda está funcionando bem para você? Não inclui o valor na criação de um novo objeto, mas quando faço um Model.save, ele tenta adicioná-lo novamente.

@ cpup22 você pode fornecer um exemplo de código? O script referenciado funciona bem para mim em 4.7.1.

Acho que a solução alternativa de https://github.com/Automattic/mongoose/issues/2363#issuecomment -171988413 é muito mais limpa. Ele permite padronizar para null mesmo quando default: null falhar:

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

Não importa, não funciona ao passar um valor (por exemplo, new Collection({ field1: [] }) obtém field1 === null . Se ao menos houvesse um gancho de construção que acontecesse antes de os dados passados ​​serem aplicados e após o padrão array vazio é criado. Ou este bug foi corrigido para permitir default: null para arrays.

Oh, parece que isso funciona:

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

O tipo de array respeita as funções mais sinceramente do que os valores. Isso não tem problemas com a substituição de null , [] , ou valor de array não vazio por meio de Collection.create() ou new Collection() .

@ vkarpov15 olá, então mesmo se eu alterar o valor da chave do tipo array para nulo no meu esquema mongoose antes que o mongo db salve um documento e, quando o array estiver vazio, ele será salvo como chave = nulo.
você tem alguma solução

@vikramkalta forneça exemplos de código, conversões

Olá, estou usando o mangusto 4.6.5 e ainda estou para salvar um array vazio com essas soluções.

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

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

@ keyboard99 siga as instruções em https://github.com/Automattic/mongoose/issues/1335#issuecomment -252129243. seu código define um padrão para os elementos individuais da matriz, não para a própria matriz

Existe uma solução simples para esses problemas. Basta fornecer o valor padrão como indefinido. Como se segue:

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

Funciona para mim

Mas isso vai causar complicações com array.push
:(

@GeneralG alguma complicação além da óbvia de ter que fazer doc.array = doc.array || []; antes de chamar push() ?

Esta página foi útil?
0 / 5 - 0 avaliações