Mongoose schema inheritance and model populate

I tried this with the built-in mongoose inheritance functions (and not with the plugin extension), but still haven't had much luck. This is a simplified code example that I am trying to use that shows the same problem. This is based on the extended mongoose documentation for schema inheritance using discriminators - http://mongoosejs.com/docs/api.html#model_Model.discriminator

var util = require('util'); var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/problem'); var Schema = mongoose.Schema; var ObjectId = Schema.ObjectId; function BaseSchema() { Schema.apply(this, arguments); this.add({ name: String, createdAt: Date }); } util.inherits(BaseSchema, Schema); var BossStatusSchema = new Schema({ status: String }); var BossStatus = mongoose.model('BossStatus', BossStatusSchema); var PersonSchema = new BaseSchema(); var Person = mongoose.model('Person', PersonSchema); var BossSchema = new BaseSchema({ department: String, bossStatus: { type: ObjectId, ref: 'BossStatus' } }); var Boss = Person.discriminator('Boss', BossSchema); 

Example code for adding documents:

 var superBoss = new BossStatus({ status: 'super' }); var normalBoss = new BossStatus({ status: 'normal' }); var andy = new Person({ name: 'Andy' }); var billy = new Boss({ name: 'Billy', bossStatus: superBoss._id }); var callback = function(err, result) { console.dir(err); console.dir(result); }; superBoss.save(callback); normalBoss.save(callback); andy.save(callback); billy.save(callback); 

So, if you find a record without :

 Person .findOne({ name: 'Billy' }) .exec(callback); 

The result will be as expected, bossStatus refers to the _id from the bossstatuses collection:

 null { name: 'Billy', bossStatus: 52a20ab0185a7f4530000001, _id: 52a20ab0185a7f4530000004, __v: 0, __t: 'Boss' } 

When adding a call:

 Person .findOne({ name: 'Billy' }) .populate('bossStatus') .exec(callback); 

The resulting bossStatus property of Person null :

 null { name: 'Billy', bossStatus: null, _id: 52a20ab0185a7f4530000004, __v: 0, __t: 'Boss' } 

EDIT:

Well, I just compiled what is probably the best example of what I'm trying to achieve, the schema structure is more suitable for a relational database, but hopefully makes the problem more clear.

 var util = require('util'); var mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/problem'); var Schema = mongoose.Schema; var ObjectId = Schema.ObjectId; function BaseSchema() { Schema.apply(this, arguments); this.add({ name: { type: String, unique: true, required: true } }); } util.inherits(BaseSchema, Schema); var DeviceSchema = new BaseSchema(); var LocalDeviceSchema = new BaseSchema({ driver: { type: ObjectId, ref: 'Driver' } }); var RemoteDeviceSchema = new BaseSchema({ networkAddress: { type: ObjectId, ref: 'NetworkAddress' } }); var DriverSchema = new Schema({ name: { type: String, unique: true, required: true } }); var NetworkHostSchema = new Schema({ host: { type: String, unique: true, required: true } }); var NetworkAddressSchema = new Schema({ networkHost: { type: ObjectId, ref: 'NetworkHost' }, port: { type: Number, min: 1, max: 65535 } }); var Driver = mongoose.model('Driver', DriverSchema); var NetworkHost = mongoose.model('NetworkHost', NetworkHostSchema); var NetworkAddress = mongoose.model('NetworkAddress', NetworkAddressSchema); var Device = mongoose.model('Device', DeviceSchema); var LocalDevice = Device.discriminator('LocalDevice', LocalDeviceSchema); var RemoteDevice = Device.discriminator('RemoteDevice', RemoteDeviceSchema); var networkHost = new NetworkHost({ host: '192.168.2.1' }); var networkAddress = new NetworkAddress({ networkHost: networkHost._id, port: 3000 }); var remoteDevice = new RemoteDevice({ name: 'myRemoteDevice', networkAddress: networkAddress._id }); var driver = new Driver({ name: 'ftdi' }); var localDevice = new LocalDevice({ name: 'myLocalDevice', driver: driver._id }); var callback = function(err, result) { if(err) { console.log(err); } console.dir(result); }; /* // Uncomment to save documents networkHost.save(function() { networkAddress.save(function() { remoteDevice.save(callback); }); }); driver.save(function() { localDevice.save(callback); }); */ var deviceCallback = function(err, device) { if(err) { console.log(err); } switch(device.__t) { case 'LocalDevice': console.log('Would create a local device instance passing populated result'); break; case 'RemoteDevice': console.log('Would create a remote device instance passing populated result'); break; } }; Device .findOne({name: 'myLocalDevice'}) .populate('driver') .exec(deviceCallback); 

LocalDevice and RemoteDevice schemes can (and probably will) include other differences. A switch, for example, would use DeviceFactory or something to create instances. My thinking was that I would have to look for the device table for the device by name and fill out the collection links (if this is the correct terminology?) Without having to specify the collection to search for - was this my understanding of the circuit's inheritance point - or did I completely not understand?

Thanks for the answers so far!

+6
source share
2 answers

You are looking for a boss, not a person:

 Boss .findOne({ name: 'Billy' }) .populate('bossStatus') .exec(callback); 
+1
source

Looks like a mistake. With active debugging, this is what is shown for the fetch request:

 Mongoose: people.findOne({ name: 'Billy' }) { fields: undefined } Mongoose: people.find({ _id: { '$in': [ ObjectId("52a221ee639cc03d71000001") ] } }) { fields: undefined } 

(shown is the ObjectId that is stored in bossStatus )

So Mongoose is requesting the wrong collection ( people instead of bossstatuses ).

As @regretoverflow pointed out, if you are looking for a boss, use the Boss model, not the Person model.

If you want to populate bossStatus with the Person model, you can explicitly specify the model to look for for the population:

 .populate({ path : 'bossStatus', model : 'BossStatus' }) // or shorter but less clear: // .populate('bossStatus', {}, 'BossStatus') 

EDIT: (with Device examples)

driver is part of LocalDeviceSchema , but you are asking for a Device model that has no idea what driver is and populates driver in the context of a Device instance doesn’t make sense for Mongoose.

Another option to populate each instance is to do it after you have extracted the document. You already have a deviceCallback function, and this will probably work:

 var deviceCallback = function(err, device) { if(err) { console.log(err); } switch(device.__t) { // or `device.constructor.modelName` case 'LocalDevice': device.populate('driver', ...); break; case 'RemoteDevice': device.populate('networkAddress', ...); break; } }; 

The reason is that the document is already entered into the correct model there, which apparently does not happen when you associate populate with find .

0
source

Source: https://habr.com/ru/post/959494/


All Articles