【问题标题】:Mongoose Mixed type field with datesMongoose 带日期的混合类型字段
【发布时间】:2016-07-27 14:07:37
【问题描述】:

我正在创建一个应用程序,用户可以在其中创建各种类型的自定义字段,并且我想将这些字段存储在 mongo.xml 中。这些类型将包括字符串、数字和日期。我的架构如下所示:

const MetaSchema = new mongoose.Schema({
  key: String,
  value: {type: mongoose.Schema.Types.Mixed},
  type: String,
  created_at: {type: Date, default: Date.now}
});

这很好用,我可以按预期存储我的数据。问题是,例如,当我想存储一个日期时,它以 ISO 格式发送到服务器,有效负载可能如下所示:

{
  "key": "Contract Signed",
  "value": "2016-04-06T22:35:11.540Z",
  "type": "date"
}

我有什么方法可以让 mongo / mongoose 将其视为日期而不是字符串来处理和存储?如果我将其设置为键入日期,那么我认为它可以解决问题,但我必须保存他们可以为自定义字段提供的任何内容。非常感谢!

TLDR:mongoose / mongo 中的混合数据类型能否根据插入的数据类型(IE 日期与字符串)区别对待。

【问题讨论】:

  • 处理这个问题的更好方法是使用discriminators,它将实现它自己的“类型”(默认为__t),这实际上使每个对象都绑定到它自己的伪模型和它自己的模式。然后,您可以对每种“类型”进行“严格”处理。这比尝试在您使用 Mixed 的地方手动滚动任何验证逻辑要好得多。
  • 谢谢@NeilLunn!这看起来就像我需要的一样。想提交我可以接受的回复,以便您获得更多互联网积分吗? :) 谢谢!

标签: javascript node.js mongodb mongoose


【解决方案1】:

使用猫鼬discriminators 可能是这里的方法。它们实际上在存储的文档中使用自己的“类型”(默认__t,但可以被覆盖)属性,这允许猫鼬将一种“模型”实际应用到每个具有自己附加模式的对象。

举个简单的例子:

var async = require('async'),
    util  = require('util'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/things');
mongoose.set("debug",true);

function BaseSchema() {

  Schema.apply(this,arguments);

  this.add({
    key: String,
    created_at: { type: Date, default: Date.now }
  });

}

util.inherits(BaseSchema,Schema);

var metaSchema = new BaseSchema();

var stringSchema = new BaseSchema({
  value: String
});

var numberSchema = new BaseSchema({
  value: Number
});

var dateSchema = new BaseSchema({
  value: Date
});

var MetaModel = mongoose.model('MetaModel',metaSchema),
    StringModel = MetaModel.discriminator('StringModel', stringSchema),
    NumberModel = MetaModel.discriminator('NumberModel', numberSchema),
    DateModel = MetaModel.discriminator('DateModel', dateSchema);

async.series(
  [
    function(callback) {
      MetaModel.remove({},callback);
    },
    function(callback) {
      async.each(
        [
          { "model": "StringModel", "value": "Hello" },
          { "model": "NumberModel", "value": 12 },
          { "model": "DateModel", "value": new Date() }
        ],
        function(item,callback) {
          mongoose.model(item.model).create(item,callback)
        },
        callback
      );
    },
    function(callback) {
      MetaModel.find().exec(function(err,docs) {
        console.log(docs);
        callback(err);
      });
    },
    function(callback) {
      DateModel.findOne().exec(function(err,doc) {
        console.log(doc);
        callback(err);
      });
    }
  ],
  function(err) {
    if (err) throw err;
    mongoose.disconnect();
  }
)

因此,由于这些基本上是“相关的”,因此我正在定义一个带有公共元素的“基本”模式。当然,每种“类型”都有单独的模式。对核心“模型”的实际分配发生在以下几行中:

var MetaModel = mongoose.model('MetaModel',metaSchema),
    StringModel = MetaModel.discriminator('StringModel', stringSchema),
    NumberModel = MetaModel.discriminator('NumberModel', numberSchema),
    DateModel = MetaModel.discriminator('DateModel', dateSchema);

这意味着MetaModel 实际上定义了集合和“默认”模式分配。以下几行使用该模型中的.discriminator() 来定义将存储在同一集合中的其他文档“类型”。

打开调试输出以显示正在发生的事情,清单产生如下内容:

Mongoose: metamodels.remove({}) {}
Mongoose: metamodels.insert({ value: 'Hello', __t: 'StringModel', created_at: new Date("Thu, 07 Apr 2016 00:24:08 GMT"), _id: ObjectId("5705a8a8443c0f74491bdec0"), __v: 0 })
Mongoose: metamodels.insert({ value: 12, __t: 'NumberModel', created_at: new Date("Thu, 07 Apr 2016 00:24:08 GMT"), _id: ObjectId("5705a8a8443c0f74491bdec1"), __v: 0 })
Mongoose: metamodels.insert({ value: new Date("Thu, 07 Apr 2016 00:24:08 GMT"), __t: 'DateModel', created_at: new Date("Thu, 07 Apr 2016 00:24:08 GMT"), _id: ObjectId("5705a8a8443c0f74491bdec2"), __v: 0 })
Mongoose: metamodels.find({}) { fields: undefined }
[ { created_at: Thu Apr 07 2016 10:24:08 GMT+1000 (AEST),
    __t: 'StringModel',
    __v: 0,
    value: 'Hello',
    _id: 5705a8a8443c0f74491bdec0 },
  { created_at: Thu Apr 07 2016 10:24:08 GMT+1000 (AEST),
    __t: 'NumberModel',
    __v: 0,
    value: 12,
    _id: 5705a8a8443c0f74491bdec1 },
  { created_at: Thu Apr 07 2016 10:24:08 GMT+1000 (AEST),
    __t: 'DateModel',
    __v: 0,
    value: Thu Apr 07 2016 10:24:08 GMT+1000 (AEST),
    _id: 5705a8a8443c0f74491bdec2 } ]
Mongoose: metamodels.findOne({ __t: 'DateModel' }) { fields: undefined }
{ created_at: Thu Apr 07 2016 10:24:08 GMT+1000 (AEST),
  __t: 'DateModel',
  __v: 0,
  value: Thu Apr 07 2016 10:24:08 GMT+1000 (AEST),
  _id: 5705a8a8443c0f74491bdec2 }

您可以看到所有内容都在分配给主模型的metamodels 集合中创建,但是在引用每个“鉴别器模型”时,会自动创建一个包含模型名称的__t 字段。这将在稍后读回数据时使用,因此 mongoose 知道在投射对象时要应用哪个模型和附加模式。

当然,由于这些都有自己的架构,因此适用标准验证规则。此外,附加到每种类型的架构的任何“实例方法”也适用于任何单独的模型。

最后,__t 字段也适用于使用“鉴别器模型”之一进行任何其他操作,例如查询或更新。如上一条执行的语句所示:

      DateModel.findOne().exec(function(err,doc) {
        console.log(doc);
        callback(err);
      });

以及实际调用:

Mongoose: metamodels.findOne({ __t: 'DateModel' }) { fields: undefined }

自动包含该属性值以指示“类型”并提供集合数据的“虚拟视图”,就好像它只包含该特定类型一样。

真正的力量实际上在于所有对象都在同一个集合中,并且猫鼬能够在检索数据时自动分配“类类型”。

【讨论】:

  • 老兄,我在 stackoverflow 上得到的最全面的答案之一。感谢您抽出宝贵时间!非常感谢!
  • 很好的答案!我用你的答案来建立另一个用例。但是遇到了问题。也许你也可以在这里帮忙?谢谢 :) stackoverflow.com/questions/65990659/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-17
  • 1970-01-01
  • 1970-01-01
  • 2019-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多