Quello che stai cercando qui è la mangusta .discriminator()
metodo. Ciò ti consente sostanzialmente di archiviare oggetti di tipi diversi nella stessa raccolta, ma di averli come oggetti di prima classe distinguibili.
Nota che il principio della "stessa raccolta" qui è importante per come .populate()
opere e la definizione del riferimento nel modello contenitore. Dal momento che puoi comunque puntare solo a "un modello" come riferimento, ma c'è qualche altra magia che può far apparire un modello come tanti.
Elenco di esempio:
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();
}
);
E uscita
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"
}
]
Puoi anche rimuovere il commento da mongoose.set("debug",true)
riga nell'elenco per vedere come la mangusta sta effettivamente costruendo le chiamate.
Quindi ciò che questo dimostra è che puoi applicare schemi diversi a diversi oggetti di prima classe e anche con metodi diversi ad essi collegati proprio come oggetti reali. Mongoose li memorizza tutti in una raccolta di "pistole" con il modello allegato e conterrà tutti i "tipi" a cui fa riferimento il discriminatore:
var Gun = mongoose.model("Gun", gunSchema );
var Ak47 = Gun.discriminator("Ak47", ak47Schema );
var M16 = Gun.discriminator("M16", m16Schema );
Ma anche ogni diverso "tipo" è referenziato con il proprio modello in un modo speciale. Quindi vedi che quando Mongoose memorizza e legge l'oggetto, c'è uno speciale __t
campo che indica quale "modello" applicare, e quindi schema allegato.
Come esempio chiamiamo .shoot()
metodo, che è definito in modo diverso per ogni modello/schema. Inoltre puoi ancora usarli come modello da solo per query o altre operazioni, poiché Ak47
applicherà automaticamente il __t
valore in tutte le query/aggiornamenti.
Quindi, sebbene lo spazio di archiviazione sia in una raccolta, può sembrare che siano molte raccolte, ma ha anche il vantaggio di tenerle insieme per altre operazioni utili. È così che puoi applicare il tipo di "polimorfismo" che stai cercando.