【问题标题】:Mongoose schema inheritance and model populateMongoose 模式继承和模型填充
【发布时间】:2013-12-24 04:37:56
【问题描述】:

我一直在尝试使用 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);

所以当找到记录时没有填充:

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

感谢到目前为止的回复!

【问题讨论】:

  • 这些答案是否解决了您的问题?您找到其他解决方案了吗?
  • 不幸的是他们没有解决这个问题,这似乎仍然是猫鼬的一个缺点——也许它会在未来的版本中得到解决。我还没有找到其他解决方案,但从那以后就没有真正研究过。

标签: node.js inheritance mongoose populate


【解决方案1】:

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

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

【讨论】:

  • 我实际上是单独运行它们,所以我首先将文档保存/添加到集合中。然后,注释掉保存并取消注释查找查询。我尝试了您的建议以防万一,但结果仍然为空。我在这里发现了另一个类似的问题,其中 OP 正在使用 schema-extend 插件,但不确定如何将其应用于此示例 - stackoverflow.com/questions/18218080/…
【解决方案2】:

看起来像一个错误。调试激活后,人口查询显示如下:

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,您可以明确声明需要搜索填充的模型:

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

原因是文档已经在那里转换为正确的模型,当您将populatefind 链接时显然不会发生这种情况。

【讨论】:

  • 他需要找一个老板,而不是一个人-
  • @regretoverflow 嗯,你是对的!我将编辑我的答案并保留其中的一些部分,也许这对于通过Person 模型找到老板很有用:)
  • 把上面的一个更新的例子放在一起,不知道我是不是误解了。。谢谢你到目前为止的帮助
  • 谢谢@robertklep,我明白你的意思了。我曾尝试添加一个“DeviceAttributes”集合,该集合包含设备之间的差异并保持“设备”模型一致,例如driver = new Driver({ name: 'ftdi' }); localDeviceAttributes = new DeviceAttributes({attributes: driver._id}); localDevice = new LocalDevice({ name: 'myLocalDevice', deviceAttributes: localDeviceAttributes._id });我刚刚把它写在了我的脑海中,所以希望它是有意义的。
  • deviceAttributes 将在 BaseSchema 中声明,因此理论上应该根据我对您的更新的理解工作 - 这让我觉得这是一种猫鼬错误,但也许只是在这种情况下,填充有点限制..
猜你喜欢
  • 2016-05-23
  • 2021-11-15
  • 2012-04-20
  • 2014-11-27
  • 2018-06-08
  • 1970-01-01
  • 2013-08-15
  • 2014-10-04
  • 2016-01-09
相关资源
最近更新 更多