如何使用Mongoose处理从数据库读取的字段和引用?

时间:2019-01-17 01:44:56

标签: javascript mongodb mongoose

我的应用程序中有一个看起来像这样的模型:

const EmployeeSchema = new Schema({
  firstName: String,
  lastName: String,
  address: {
    street: String,
    zipCode: String,
    city: {
      type: Schema.Types.ObjectId,
      ref: 'City',
      autopopulate: { select: 'cityName state' }
    }
  },
  ...
  // and a bunch of other fields
});

module.exports = mongoose.model('Employee', EmployeeSchema, 'employees');

这是我正在使用的主要模型。但是,如果客户端希望从数据库中绘制所有雇员的完整列表,则服务器应返回一个包含所有雇员的大型数组。为了使数据更小,我有另一个模型,该模型仅显示所需信息的摘要,如下所示:

const EmployeeSnippetSchema = new Schema({
  firstName: String,
  lastName: String,
  residenceCity: String,
  residenceState: String
});

module.exports = mongoose.model('EmployeeSnippet', EmployeeSnippetSchema, 'employees');

现在,如果客户想要一个包含数据库中所有雇员的完整列表的数组,我只需进行以下调用:

EmployeeSnippet.find()...

在主模式中,“地址”的“城市”字段只是对数据库中其他集合(“城市”)的引用。如何使用EmployeeSnippet进行调用,该调用将从'city'集合中提取'cityName'和'state'值并将其分别填充在'residenceCity'和'residenceState'中? (考虑到这是在服务器从数据库中提取数百(可能是数千)记录的操作期间。)

2 个答案:

答案 0 :(得分:0)

好。我找到了一个可行的解决方案,尽管我不知道对于从数据库中提取大量模型文档的情况而言,它是否是最佳选择。但是,无论如何,都是这样。

我需要在代码段的架构上使用post挂钩进行初始化。换句话说,EmployeeSnippet应该如下所示:

const EmployeeSnippetSchema = new Schema({
  firstName: String,
  lastName: String,
  address: {
    city: {
      type: Schema.Types.ObjectId,
      ref: 'City',
      autopopulate: { select: 'cityName state' }
    }
  },
  residenceCity: String,
  residenceState: String
});

我只添加了以下钩子:

EmployeeSnippetSchema.post('init', function(doc) {
  if (doc.address && doc.address.city) {
    doc.residenceCity = doc.address.city.cityName;
    doc.residenceState = doc.address.state;
  }
  
  delete doc.address;
});

这是可行的,尽管正如我之前所说,当我们收到大量文档时,我不知道这是否是一种优化的解决方案,但这是我能想到的最好的方法。

P.S。我只是意识到我使用了“自动填充”功能,而这并不是猫鼬开箱即用的功能。这实际上是猫鼬团队的插件:http://plugins.mongoosejs.io/plugins/autopopulate

答案 1 :(得分:0)

我实际上找到了另一种方法,我认为它是更优化的,因为它将所有这些工作委托给了实际的数据库引擎。这是使用聚合。有了它,我什至不需要为代码段创建一个全新的模型—我可以从主模型中获取它。这是我的方法:

Employee.aggregate()
  .lookup({
    from: 'cities',
    localField: 'residenceAddress.city',
    foreignField: '_id',
    as: 'residenceAddress'
  })
  .addFields({
    residenceCity: { $arrayElemAt: ['$residenceAddress', 0] },
    residenceState: { $arrayElemAt: ['$residenceAddress', 0] }
  })
  .project({
    firstName: 1,
    lastName: 1,
    residenceCity: '$residenceCity.cityName',
    residenceState: '$residenceState.state',
  })
  .then((result) => {
    // do whatever stuff I need with the snippet resulting from this process
  });