MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Plusieurs références de schéma dans un seul tableau de schéma - mongoose

Ce que vous cherchez ici est la mangouste .discriminator() méthode. Cela vous permet essentiellement de stocker des objets de différents types dans la même collection, mais de les avoir comme des objets de première classe distincts.

Notez que le principe de "même collection" ici est important pour la façon dont .populate() travaux et la définition de la référence dans le modèle contenant. Puisque vous ne pouvez vraiment pointer que vers "un" modèle pour une référence de toute façon, mais il y a une autre magie qui peut faire apparaître un modèle comme plusieurs.

Exemple de liste :

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

Et sortie

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

Vous pouvez également décommenter le mongoose.set("debug",true) ligne dans la liste pour voir comment la mangouste construit réellement les appels.

Donc, ce que cela démontre, c'est que vous pouvez appliquer différents schémas à différents objets de première classe, et même avec différentes méthodes qui leur sont attachées, tout comme de vrais objets. Mongoose les stocke tous dans une collection "guns" avec le modèle attaché, et elle contiendra tous les "types" référencés par le discriminateur :

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

Mais aussi chaque "type" différent est référencé avec son propre modèle d'une manière spéciale. Vous voyez donc que lorsque la mangouste stocke et lit l'objet, il existe un __t spécial champ qui lui indique quel "modèle" appliquer, et donc le schéma attaché.

Comme exemple, nous appelons le .shoot() méthode, qui est définie différemment pour chaque modèle/schéma. Et aussi vous pouvez toujours utiliser chacun comme un modèle par lui-même pour les requêtes ou d'autres opérations, puisque Ak47 appliquera automatiquement le __t valeur dans toutes les requêtes/mises à jour.

Ainsi, bien que le stockage se trouve dans une collection, il peut sembler y avoir plusieurs collections, mais a également l'avantage de les conserver ensemble pour d'autres opérations utiles. C'est ainsi que vous pouvez appliquer le type de "polymorphisme" que vous recherchez.