Node.js Node JS-Mongoose-如何通过ID正确连接两个集合的结果
我有以下用于跟踪习惯和用户进度的模式 习惯图式Node.js Node JS-Mongoose-如何通过ID正确连接两个集合的结果,node.js,mongodb,mongoose,mongoose-schema,Node.js,Mongodb,Mongoose,Mongoose Schema,我有以下用于跟踪习惯和用户进度的模式 习惯图式 const HabitSchema = new mongoose.Schema({ user: { type: mongoose.Schema.Types.ObjectId, ref: "User", }, title: { type: String, required: true, }, type: { type: String, enum: ["G
const HabitSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
title: {
type: String,
required: true,
},
type: {
type: String,
enum: ["Goal", "Limit"],
default: "Goal",
required: true,
},
dateStarted: {
type: Date,
default: Date.now,
required: true,
},
});
进度模式:每次用户点击他们在给定的时间段(天、周、月等)内完成一个习惯时,都会为该时间段创建一个文档,以表明已经完成了
const ProgressSchema = new mongoose.Schema({
habit: {
type: mongoose.Schema.Types.ObjectId,
ref: "Habit",
},
period: {
type: Date,
default: Date.now,
required: true,
},
value: {
type: Number,
default: 1,
required: true,
},
});
当我获得一个用户的所有习惯时,我还希望获得每个习惯的所有进度文档。我想在返回之前加入这些对象
以下是我的尝试:
let habits = await Habit.find({ user: req.user.id }).sort({
dateStarted: -1,
});
habits = await Promise.all(
habits.map(async (h, i) => {
try {
const progress = await Progress.find({ habit: h._id });
return { ...h, progress };
} catch (e) {
res.status(500).send("Server Error");
}
})
);
res.json({ habits });
这会产生错误:CastError:model“habity”的路径“\u id”处的值“undefined”的转换到ObjectId失败。
实现这一点的正确方法是什么?我仍然不明白为什么会出现错误,因为我使用了wait/async,所以在检索到id之前,该行不应该运行(错误是说。\ id没有定义,但显然没有定义) 在尝试了很多东西之后,以下是最终奏效的方法:
try {
await Promise.all(
habits.map(async (h, i) => {
let progress = await Progress.find({ habit: h._id });
let newHabit = h;
newHabit._doc.progresses = progress;
return newHabit;
})
);
} catch (error) {
console.log(error);
res.status(500).send("Server Error");
}
一旦我最终得到了
等待进度代码>为了工作,我还发现我不能仅仅用扩展操作符添加到猫鼬检索到的对象。该对象有许多属性,数据库中的字段实际上包含在对象的。_doc属性中:newhabity。_doc.progress=progress
(但这与我得到的错误无关)。这让我觉得有一种更好的方法可以将字段添加到mongoose.find()
找到的文档中,因为您没有运行任何类似.save的函数,所以不需要find查询返回的附加项
let habits = await Habit.find({ user: req.user.id }).sort({
dateStarted: -1,
}).lean();
habits = await Promise.all(
habits.map(async (h, i) => {
try {
const progress = await Progress.find({ habit: h._id }).lean();
return { ...h, progress };
} catch (e) {
res.status(500).send("Server Error");
}
})
);
res.json({ habits });
更好的解决方案是使用我没有使用Mongoose,但是,在vanilla MongoDB中,您可以使用聚合框架这样做:
db.Habbit.aggregate([
{$lookup:{
来自:“进步”,
localField:“\u id”,
外域:“哈比特”,
as:“进步”
} }
])
此外,您还可以添加一个步骤,放弃除最新进度之外的所有进度,而不是全部返回。但是,我将把这个问题留给另一个问题。那里有什么I
。而且,req.user.id
不是ObjectId类型,因此出现错误。因此您需要将req.user.id转换为ObjectId类型,对吗?i
只是habits
中的项的索引,由.map()
提供给回调函数。我应该澄清一下,req.user.id没有导致ObjectId相关的错误,它工作正常。Progress.find({habity:h.\u id})
是导致错误的原因。该错误似乎与代码不符。这听起来像是试图将传递给习惯
模型构造函数的对象的\u id
字段转换为ObjectId
。我在提供的代码片段中没有看到这种情况。您确定它发生在您怀疑的线路上(异步代码破坏堆栈跟踪可能会有点混淆)?为什么不使用$lookup
在单个查询中获取习惯和进度?感谢您提供有关lean()
,编辑我的代码的提示。对于“填充”,我需要在习惯文档中有进度文档的id,而不是每个进度对象都有习惯id,对吗?如果您不想更改模式,您也可以使用聚合,您的代码将继续运行良好,直到您在数据库中获得一定数量的数据,这样您就可以继续使用您的方法。