【问题标题】:Multiple schema references in single schema array - mongoose单个模式数组中的多个模式引用 - mongoose
【发布时间】:2016-05-29 20:59:28
【问题描述】:

您能否在 mongoose 架构中填充一个数组,并引用几个不同的架构选项?

为了澄清这个问题,假设我有以下架构:

var scenarioSchema = Schema({
  _id     : Number,
  name    : String,
  guns : []
});

var ak47 = Schema({
  _id     : Number
  //Bunch of AK specific parameters
});

var m16 = Schema({
  _id     : Number
  //Bunch of M16 specific parameters
});

我可以用一堆 ak47 OR m16 填充枪阵列吗?我可以将 BOTH 放在同一个枪支阵列中吗?或者它是否需要像这样在 assets 数组中填充 ref,从而将其限制为单个特定类型?

guns: [{ type: Schema.Types.ObjectId, ref: 'm16' }]

我知道我可以为不同的枪支类型设置单独的数组,但是随着项目的扩展,这会在架构中创建大量额外的字段,其中大部分会根据加载的场景留空。

var scenarioSchema = Schema({
  _id     : Number,
  name    : String,
  ak47s : [{ type: Schema.Types.ObjectId, ref: 'ak47' }],
  m16s: [{ type: Schema.Types.ObjectId, ref: 'm16' }]
});

回到这个问题,我可以在一个数组中粘贴多个架构引用吗?

【问题讨论】:

    标签: javascript mongodb mongoose mongoose-populate


    【解决方案1】:

    您在这里寻找的是猫鼬.discriminator() 方法。这基本上允许您将不同类型的对象存储在同一个集合中,但将它们作为可区分的第一类对象。

    请注意,这里的“相同集合”原则对于.populate() 的工作方式以及包含模型中引用的定义很重要。因为无论如何你真的只能指向“一个”模型作为参考,但是还有一些其他的魔法可以让一个模型出现很多。

    示例列表:

    var util = require('util'),
        async = require('async'),
        mongoose = require('mongoose'),
        Schema = mongoose.Schema;
    
    mongoose.connect('mongodb://localhost/gunshow');
    
    //mongoose.set("debug",true);
    
    var scenarioSchema = new Schema({
      "name": String,
      "guns": [{ "type": Schema.Types.ObjectId, "ref": "Gun" }]
    });
    
    function BaseSchema() {
      Schema.apply(this, arguments);
    
      // Common Gun stuff
      this.add({
        "createdAt": { "type": Date, "default": Date.now }
      });
    }
    
    util.inherits(BaseSchema, Schema);
    
    var gunSchema = new BaseSchema();
    
    var ak47Schema = new BaseSchema({
      // Ak74 stuff
    });
    
    ak47Schema.methods.shoot = function() {
      return "Crack!Crack";
    };
    
    var m16Schema = new BaseSchema({
      // M16 Stuff
    });
    
    m16Schema.methods.shoot = function() {
      return "Blam!!"
    };
    
    
    var Scenario = mongoose.model("Scenario", scenarioSchema);
    
    var Gun = mongoose.model("Gun", gunSchema );
    var Ak47 = Gun.discriminator("Ak47", ak47Schema );
    var M16 = Gun.discriminator("M16", m16Schema );
    
    
    async.series(
      [
        // Cleanup
        function(callback) {
          async.each([Scenario,Gun],function(model,callback) {
            model.remove({},callback);
          },callback);
        },
    
        // Add some guns and add to scenario
        function(callback) {
          async.waterfall(
            [
              function(callback) {
                async.map([Ak47,M16],function(gun,callback) {
                  gun.create({},callback);
                },callback);
              },
              function(guns,callback) {
                Scenario.create({
                  "name": "Test",
                  "guns": guns
                },callback);
              }
            ],
            callback
          );
        },
    
        // Get populated scenario
        function(callback) {
          Scenario.findOne().populate("guns").exec(function(err,data) {
    
            console.log("Populated:\n%s",JSON.stringify(data,undefined,2));
    
            // Shoot each gun for fun!
            data.guns.forEach(function(gun) {
              console.log("%s says %s",gun.__t,gun.shoot());
            });
    
            callback(err);
          });
        },
    
        // Show the Guns collection
        function(callback) {
          Gun.find().exec(function(err,guns) {
            console.log("Guns:\n%s", JSON.stringify(guns,undefined,2));
            callback(err);
          });
        },
    
        // Show magic filtering
        function(callback) {
          Ak47.find().exec(function(err,ak47) {
            console.log("Magic!:\n%s", JSON.stringify(ak47,undefined,2));
            callback(err);
          });
        }
      ],
      function(err) {
        if (err) throw err;
        mongoose.disconnect();
      }
    );
    

    然后输出

    Populated:
    {
      "_id": "56c508069d16fab84ead921d",
      "name": "Test",
      "__v": 0,
      "guns": [
        {
          "_id": "56c508069d16fab84ead921b",
          "__v": 0,
          "__t": "Ak47",
          "createdAt": "2016-02-17T23:53:42.853Z"
        },
        {
          "_id": "56c508069d16fab84ead921c",
          "__v": 0,
          "__t": "M16",
          "createdAt": "2016-02-17T23:53:42.862Z"
        }
      ]
    }
    Ak47 says Crack!Crack
    M16 says Blam!!
    Guns:
    [
      {
        "_id": "56c508069d16fab84ead921b",
        "__v": 0,
        "__t": "Ak47",
        "createdAt": "2016-02-17T23:53:42.853Z"
      },
      {
        "_id": "56c508069d16fab84ead921c",
        "__v": 0,
        "__t": "M16",
        "createdAt": "2016-02-17T23:53:42.862Z"
      }
    ]
    Magic!:
    [
      {
        "_id": "56c508069d16fab84ead921b",
        "__v": 0,
        "__t": "Ak47",
        "createdAt": "2016-02-17T23:53:42.853Z"
      }
    ]
    

    您还可以取消注释列表中的 mongoose.set("debug",true) 行,以查看 mongoose 是如何实际构建调用的。

    因此,这表明您可以将不同的模式应用于不同的第一类对象,甚至可以像真实对象一样附加不同的方法。 Mongoose 将这些全部存储在附加模型的“枪”集合中,它将包含鉴别器引用的所有“类型”:

    var Gun = mongoose.model("Gun", gunSchema );
    var Ak47 = Gun.discriminator("Ak47", ak47Schema );
    var M16 = Gun.discriminator("M16", m16Schema );
    

    但每个不同的“类型”也以一种特殊的方式被它自己的模型引用。所以你会看到,当 mongoose 存储和读取对象时,有一个特殊的 __t 字段告诉它要应用哪个“模型”,并因此附加了架构。

    作为一个例子,我们调用.shoot() 方法,它为每个模型/模式定义不同。而且您仍然可以将每个模型本身用作查询或其他操作的模型,因为Ak47 将在所有查询/更新中自动应用__t 值。

    因此,尽管存储在一个集合中,但它看起来可能是多个集合,但也有利于将它们保持在一起以进行其他有用的操作。这就是您可以应用您正在寻找的“多态性”的方式。

    【讨论】:

    • 嗨,我知道这有点晚了,但是可以将 Gun 模型分成他们自己的集合吗?所以,Ak47有自己的收藏,M116也有自己的。
    猜你喜欢
    • 2012-11-20
    • 2013-04-07
    • 2013-08-02
    • 2015-05-18
    • 2021-10-11
    • 2018-10-22
    • 2015-04-30
    • 2016-07-02
    相关资源
    最近更新 更多