首页 文章

Mongoose模式继承和模型填充

提问于
浏览
6

我一直在尝试使用mongoose的内置继承功能(而不是扩展插件)但到目前为止还没有太多运气 . 这是我尝试使用的代码的简化示例,它表现出相同的问题 . 这是基于使用鉴别器的模式继承的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);

所以当找到记录 without 时填充:

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

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或其他东西来创建实例 . 我的想法是应该可以通过'name'在设备表中搜索设备并填充集合引用(如果这是正确的术语?)而不必指定要搜索的集合 - 这是我对点的理解架构继承 - 还是我完全被误解了?

感谢您的回复!

2 回答

  • 1

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

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

    看起来像个bug . 在调试处于活动状态时,这是针对填充查询显示的内容:
    猫鼬:people.findOne({name:'Billy'}){fields:undefined}
    Mongoose:people.find({_ id:{'$ in':[ObjectId(“52a221ee639cc03d71000001”)]}}} {fields:undefined}
    (显示的ObjectId是存储在bossStatus中的ObjectId)
    所以Mongoose正在查询错误的集合(人而不是bossstatuses) .

    正如@regretoverflow指出的那样,如果你正在寻找老板,请使用 Boss 模型而不是 Person 模型 .

    如果您确实希望通过 Person 模型填充 bossStatus ,则可以显式声明需要搜索填充的模型:

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

    EDIT: (与您的 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;
      }
    };
    

    原因是该文档已经被转换为正确的模型,当您使用 find 链接 populate 时,显然不会发生这种情况 .

相关问题