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,对吗?如果您不想更改模式,您也可以使用聚合,您的代码将继续运行良好,直到您在数据库中获得一定数量的数据,这样您就可以继续使用您的方法。