Mongoose: 当属性引用架构时保存空数组

创建于 2013-02-06  ·  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条评论

这是设计使然。 保存所有默认值,因此文档的 mongoose 应用程序视图与非 mongoose 应用程序视图相同。

好的,我如何使默认为空? ;-) 这里的用例是在 mongo 中请求所有具有空 foos 的 Bar 是昂贵的。 我必须添加另一个属性来跟踪该状态,这只会使我的代码进一步复杂化。

我知道了。 这将让您跳过在新文档中保存空的 foos:

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

甜的。 这对我来说已经足够好了。

这种变通办法仍然有效吗? (这里是猫鼬 3.8.3)

Mathieumg:它对我不起作用(3.8.15)

是的,我认为这个问题值得关注。 (虽然很小,但是
我有兴趣将 pre 用于其他事情:))

2014 年 8 月 25 日星期一下午 4:49,Mathieu M-Gosselin <
[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重新打开了这个问题,这确实

是的,这种行为在不久的将来不会改变。 我重新打开了这个问题,所以当我有机会时,我知道要调查它,因为我确保了解进入 mongoose 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 来表示空 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: {} 
}

有人知道如何解决这个问题吗?

问候

我认为您可以完成这项工作,显然不能使用我发布的 presave,因为这涉及到 children 是一个数组而不是一个对象。 我还没有尝试过这个,但实际上不可能使用“鸭子打字” Empty 对象来表示 ACTUAL 空对象吗?

所以你的预存可能是:

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

几个月后,有没有预钩的适当解决方案? 我有几十个数组字段,我应该在一个pre hook中一一处理...

@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您的修复在猫鼬 4.7.0 上对我不起作用。 还在为你工作好吗? 它不包括新对象创建的值,但是当我执行 Model.save 时,它​​会尝试再次添加它。

@cpup22你能提供一个代码示例吗? 引用的脚本在 4.7.1 上对我来说很好用。

我认为来自https://github.com/Automattic/mongoose/issues/2363#issuecomment -171988413 的解决方法要干净得多。 即使default: null失败,它也允许默认为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 },
});

数组类型的东西比值更尊重函数。 这对于通过Collection.create()new Collection()覆盖传入的null[]或非空数组值没有问题。

@vkarpov15你好,所以即使我在 mongo db 保存文档之前在我的 mongoose 模式中将类型数组的键值更改为 null,并且当数组为空时,它会保存为 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 中的说明进行操作。 您的代码为单个数组元素设置默认值,而不是为数组本身设置

这个问题有一个简单的解决方案。 只需提供未定义的默认值。 如下:

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

为我工作

但这会导致 array.push 的复杂化
:(

@GeneralG除了在调用push()之前必须执行doc.array = doc.array || [];的明显问题之外,还有其他任何并发症吗?

此页面是否有帮助?
0 / 5 - 0 等级