Mongoose模式inheritance和模型填充

我一直试图用内置的mongoose(而不是扩展插件)的inheritancefunction,但迄今没有多less运气。 这是我试图使用的代码的一个简化的例子,展示了同样的问题。 这是基于使用鉴别器的模式inheritance的mongoose文档的扩展版本 – 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); 

添加文档的示例代码:

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

所以当find没有填充的logging时:

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

结果如预期的那样,bossStatus指的是bossstatuses集合中的_id:

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

添加填充调用时:

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

Person结果的结果bossStatus属性为

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

编辑:

好吧,我只是放在一起什么可能是一个更好的例子,我试图实现,架构结构借给更多的关系数据库,但希望使问题更清楚。

 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和RemoteDevice模式可能(也可能会)包含其他差异。例如,交换机将使用DeviceFactory或其他东西来创build实例。 我的想法是,应该可以通过'名称'在设备表中search设备,并填充集合引用(如果这是正确的术语?),而不必指定要search的集合 – 这是我对这一点的理解模式inheritance – 还是我完全误解了?

感谢迄今为止的回复!

你正在寻找一个老板,而不是一个人:

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

看起来像一个错误。 在debugging活动的时候,这就是群体查询所显示的内容:

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

(显示的ObjectId是存储在bossStatus

所以mongoose是查询错误的集合( people而不是bossstatuses )。

正如@regretoverflow所指出的那样,如果你正在寻找一个老板,那么使用Boss模型而不是Person模型。

如果你想通过Person模型来填充bossStatus ,你可以明确地声明一个需要被search的模型:

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

编辑:(与您的Device示例)

driverLocalDeviceSchema一部分,但是您正在查询Device模型,它没有driver概念,并且在Device实例的上下文中填充driver对于Mongoose没有意义。

填充每个实例的另一种可能性是在检索文档后执行。 你已经有deviceCallback函数,这可能会工作:

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

原因是这个文档已经被转换成了正确的模型,当你用链接populate find时显然不会发生。