Multiple schema references in single schema array - mongoose

前端 未结 1 804
走了就别回头了
走了就别回头了 2020-12-05 03:14

Can you populate an array in a mongoose schema with references to a few different schema options?

To clarify the question a bit, say I have the following schemas:

相关标签:
1条回答
  • 2020-12-05 04:00

    What you are looking for here is the mongoose .discriminator() method. This basically allows you to store objects of different types in the same collection, but have them as distinquishable first class objects.

    Note that the "same collection" principle here is important to how .populate() works and the definition of the reference in the containing model. Since you really can only point to "one" model for a reference anyway, but there is some other magic that can make one model appear as many.

    Example listing:

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

    And output

    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"
      }
    ]
    

    You can also uncomment the mongoose.set("debug",true) line in the listing to see how mongoose is actually constructing the calls.

    So what this demonstrates is that you can apply different schemas to different first class objects, and even with different methods attached to them just like real objects. Mongoose is storing these all in a "guns" collection with the attached model, and it will contain all "types" refernced by the discriminator:

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

    But also each different "type" is referenced with it's own model in a special way. So you see that when mongoose stores and reads the object, there is a special __t field which tells it which "model" to apply, and hence attached schema.

    As one example we call the .shoot() method, which is defined differently for each model/schema. And also you can still use each as a model by itself for queries or other operations, since Ak47 will automatically apply the __t value in all query/upates.

    So though the storage is in one collection it can appear to be many collections, but also has the benefit of keeping them together for other useful operations. This is how you can apply the kind of "polymorphism" you are looking for.

    0 讨论(0)
提交回复
热议问题