Mongoose: Пустой массив сохраняется, когда свойство ссылается на схему.

Созданный на 6 февр. 2013  ·  39Комментарии  ·  Источник: 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();

Это создаст пустой массив b.foos вместо того, чтобы просто оставить свойство неопределенным.

enhancement

Самый полезный комментарий

@antonioaltamura Я забыл об этой проблеме, ниже работает в мангусте 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); });

Все 39 Комментарий

Это сделано намеренно. Все значения по умолчанию сохраняются, поэтому представление документа в приложениях мангуста идентично представлению приложений без мангуста.

Хорошо, как мне сделать значение по умолчанию нулевым? ;-) Суть в том, что в mongo дорого запрашивать все панели с пустыми foos. Мне нужно добавить еще одно свойство для отслеживания этого состояния, что еще больше усложняет мой код.

Понятно. Это позволит вам пропустить сохранение пустых файлов foo в новых документах:

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

Милая. Для меня это достаточно хорошая работа.

Предполагается, что этот обходной путь все еще работает? (здесь мангуст 3.8.3)

Матье: у меня не работает (3.8.15)

Да, я считаю, что этот вопрос заслуживает внимания. (Это довольно незначительно, но
Мне интересно использовать pre для других вещей :))

В пн, 25 августа 2014 г., 16:49, Матьё М.-Госслен <
[email protected]> написал:

Может быть, мне стоит открыть новый выпуск, чтобы меня заметили?

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/LearnBoost/mongoose/issues/1335#issuecomment -53328195
.

+1
Предварительная работа не работает для 3.8.15
единственная альтернатива, которую я думаю, - иметь null или false для упрощения запроса, но это также добавляет несколько байтов и оказывает значительное влияние на большую базу данных.

        this.foos = null;

@ gaurang171 @Nepoxx вы можете предоставить мне какой-нибудь код, который воспроизводит это? Обходной путь отлично работает в наших тестовых случаях (см. Тестовый пример )

Это все еще проблема для меня, использующего 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 Это никогда не было «исправлено». Был предоставлен обходной путь . Вы должны проверить, работает ли этот обходной путь, поскольку по этому поводу имеется противоречивая информация. знак равно

У меня уже есть обходной путь. Мне интересно знать, изменится ли это поведение или нет.

Извините, вы не сказали, что хотите знать, изменится ли поведение или нет.

На самом деле я не уверен, почему эта проблема была повторно открыта @ vkarpov15 , что вызывает вопрос, изменится ли это поведение или нет. Думаю, проблема в обратной совместимости. Вероятно, это должно было быть решено до выхода 4.x.

Да, это поведение не изменится в ближайшем будущем. Я повторно открыл эту проблему, поэтому я знал, что расследовать ее, когда у меня будет возможность, потому что я обязательно понимаю, что происходит, с каждым комментарием, который приходит в github мангуста, и это был тот, на который у меня не было хорошего ответа чтобы слететь с моей головы. Если присмотреться, мы не сможем изменить это в ближайшем будущем из-за semver, но есть кое-что, чтобы исследовать, является ли это болевой точкой для многих пользователей.

Я использовал этот обходной путь с изюминкой, потому что иногда вы ХОТИТЕ записать пустой массив, а иногда нет.

Итак, мое предварительное сохранение выглядит так:

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

А затем, чтобы написать пустых детей, я использую это:

idea.children = [null];

Принимая во внимание, что это лишено:

idea.children = [];

Это может не сработать для вас, но вы сможете придумать какую-то другую схему.

Это довольно большая проблема, когда у вас есть массивы внутри вложенных объектов.

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

Здесь я ожидаю, что foo будет отсутствовать, и вместо этого у меня есть этот вложенный объект с пустым массивом.

Но разве здесь вам не поможет предварительное сохранение? вместо {foo: undefined} вы можете использовать уникальный тип foo для обозначения null foo, а затем вырезать foo в предварительном сохранении.

Хотя некрасиво.

Я столкнулся с той же проблемой, что и @viking . вы нашли какое-либо подходящее решение?

Я пробовал это с "предварительным" способом сохранения ... но это все еще иногда случается, я не уверен, сколько различных вариантов использования мне нужно охватить, чтобы это сделать. Моя самая большая проблема заключается в том, что JSON на самом деле должен доставлять объект, а не массив. это фатальное исключение для моего парсера json, который пытается сопоставить эти вещи с объектами java.

пример:

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

... это приводит к:

{
 filter: []
}

но его должно либо просто не быть, либо, по крайней мере, быть объектом, а не пустым массивом:

{
 filter: {} 
}

у кого-нибудь есть идеи, как это решить?

С уважением

Я думаю, что вы можете сделать эту работу, очевидно, не с предварительным сохранением, которое я опубликовал, потому что в этом случае дети были массивом, а не объектом. Я не пробовал этого, но разве нельзя было бы использовать пустой объект "утиный ввод" для обозначения НАСТОЯЩЕГО пустого объекта?

Итак, ваше предварительное сохранение может быть:

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

Затем, чтобы создать настоящий пустой фильтр, вы можете использовать:

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

Обратите внимание, что на самом деле не имеет значения, если в схеме нет «пустого», она никогда не превращается в мангуста.

Каким-то образом мне удалось решить эту проблему ... На самом деле я сделал нечто очень похожее. Просто увидел ваш ответ и хотел сказать вам спасибо :)

С уважением
Саймон

У меня было много проблем с этим решением, когда были вложенные схемы с необязательными полями Array . Я решил это, создав новый тип:

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

а затем установил для всех моих полей значение optional_array вместо Array .

@simllll Я использую заполнитель null для отсутствующих объектов, а затем удаляю null перед использованием объекта.

Извините, но это ужасный дизайн. Вы не должны решать от моего имени, хочу ли я, чтобы он был пустым или не заданным вовсе. По крайней мере, предоставьте возможность отключить эту функцию.

Согласен с @ifeltsweet

Спустя несколько месяцев есть правильное решение без предварительного крючка? У меня есть десятки полей массива, я должен обрабатывать одно за другим в предварительном хуке ...

@antonioaltamura Я забыл об этой проблеме, ниже работает в мангусте 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 ваше исправление не работает для меня на mongoose 4.7.0. Все еще работает нормально? Он не включает значение при создании нового объекта, но когда я делаю Model.save, он пытается добавить его снова.

@ cpup22 можете ли вы предоставить образец кода? Указанный скрипт у меня отлично работает на 4.7.1.

Я думаю, что обходной путь с https://github.com/Automattic/mongoose/issues/2363#issuecomment -171988413 намного чище. Это позволяет по умолчанию использовать null даже если default: null не работает:

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

Неважно, это не работает при передаче значения (например, new Collection({ field1: [] }) получает field1 === null . Если бы только был хук построения, который произошел до того, как переданные данные были применены, и после применения по умолчанию создается пустой массив Или эта ошибка была исправлена, чтобы разрешить default: null для массивов.

О, похоже, это работает:

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

Тип массива более искренне уважает функции, чем значения. У этого нет проблем с перезаписью переданного null , [] или непустого значения массива через Collection.create() или new Collection() .

@ vkarpov15 привет, поэтому даже если я изменю значение ключа для массива типов на null в моей схеме mongoose до того, как mongo db сохранит документ, а когда массив пуст, он сохраняется как key = null.
у вас есть любые решения

@vikramkalta, пожалуйста, предоставьте образцы кода, преобразование прозы в код подвержено ошибкам.

Привет, я использую mongoose 4.6.5 и все еще сохраняю пустой массив с этими решениями.

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

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

@ keyboard99, следуйте инструкциям на странице https://github.com/Automattic/mongoose/issues/1335#issuecomment -252129243. ваш код устанавливает значение по умолчанию для отдельных элементов массива, а не для самого массива

Есть простое решение этой проблемы. Просто укажите значение по умолчанию как undefined. Следующим образом:

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

Работает для меня

Но это вызовет сложности с array.push.
:(

@GeneralG какие-либо осложнения, кроме очевидной необходимости сделать doc.array = doc.array || []; перед вызовом push() ?

Была ли эта страница полезной?
0 / 5 - 0 рейтинги