假设我们有Foo
和Bar
的模式。 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:对于模型“Bar”的路径“_id”的值“某个任意对象”,Cast to ObjectId失败
我已使用match
上的populate
选项进行了检查,要求Bar
上的字段存在:
Foo.find().populate({
path: 'bar',
match: {
name: {
$exists: true
}
}
}
不幸的是,错误是一样的。
那么我的问题是,如果字段包含ObjectId,有没有办法让mongoose只有populate
字段,否则不管它?
答案 0 :(得分: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次查询(以及所有人口查询),这当然不是最理想的,但这对您来说可能不是问题。可读性要好得多。当然,您可以更改查找查询以特定地匹配您的案例。