首页 文章

只有当字段包含ObjectId时,是否可以让mongoose填充混合字段?

提问于
浏览
1

设置

假设我们有 FooBar 的模式 . Foo 有一个字段 bar . 此字段可以包含引用文档 Bar 的ObjectId,也可以包含不是ObjectIds的其他任意对象 .

const fooSchema = new mongoose.Schema({
  bar: {
    type: mongoose.Schema.Types.Mixed,
    ref: 'Bar'
  }
});
const Foo = <any>mongoose.model<any>('Foo', fooSchema);

const barSchema = new mongoose.Schema({
  name: String
});
const Bar = <any>mongoose.model<any>('Bar', barSchema);

问题

现在假设我们有一堆 Foo 文件 .

我希望能够在 bar 字段上使用mongoose的populate来自动将 Bar 文档的引用替换为实际的 Bar 文档本身 . 我还想保留所有其他未引用 Bar 文档的对象 .

通常,我会使用这样的东西来获取所有 Foo 文档,然后填充 bar 字段:

Foo.find().populate('bar')

但是,当遇到 bar 字段中不是ObjectIds的对象时,此方法将抛出异常,而不是保持它们不变 .

UnhandledPromiseRejectionWarning:未处理的承诺拒绝(拒绝ID:1):CastError:强制转换为ObjectId,对于模型“Bar”的路径“_id”处的值“某些任意对象”失败

尝试寻找解决方案

我已经检查过使用 populate 上的 match 选项,要求 Bar 上的字段存在:

Foo.find().populate({
  path: 'bar',
  match: {
    name: {
      $exists: true
    }
  }
}

不幸的是,错误是一样的 .

问题

那么我的问题是,如果字段包含ObjectId,有没有办法让mongoose只有 populate 字段,否则不管它?

1 回答

  • 1

    据我所知,你不能用那种方式填充 . 选择属性在尝试获取填充值后仍然有效,并且在此之前无法对其进行过滤 .

    你必须手动完成 . 你可以手动完成 .

    let foos = await Foo.find({});
    foos = foos.map(function (f) {
      return new Promise(function (resolve) {
        if (condition()) {
            Foo.populate(f, {path: 'bar'}).then(function(populatedF){
                resolve(f);
            });
        } else {
          resolve(f);
        }
      });
    });
    
    await Promise.all(foos).then(function (fs) {
      res.status(200).json(fs);
    });
    

    优雅地将它包装在模型上的post hook或static方法中 .

    另一种选择是发送2个查询:

    const foosPopulated = Foo.find({ alma: { $type: 2 } }).populate('bar'); // type of string
    const foosNotPopulated = Foo.find({ alma: { $type: 3 } }); // type of object
    
    const foos = foosPopulated.concat(foosNotPopulated);
    

    这当然不是最理想的,因为有2个查询(以及所有人口查询),但这对您来说可能不是问题 . 可读性要好得多 . 当然,您可以更改查找查询以特定地匹配您的案例 .

相关问题