【问题标题】:Aggregate data from different collections聚合来自不同集合的数据
【发布时间】:2023-03-23 06:11:01
【问题描述】:

我目前正在使用猫鼬进行具有以下架构的项目。

用户架构

const userSchema = {
name: string
email: string
medicalVisits: [{type: Schema.ObjectId, ref: "records"}]
createdAt: Date
}

记录架构

const recordSchema = {
medication: [String],
rating: Number
user: [{type: Schema.ObjectId, ref: "user"}]
tests: [{type: Schema.ObjectId, ref: "tests"}]
createdAt: Date
}

测试架构

testScore: Number
answers: Object
user: [{type: Schema.ObjectId, ref: "user"}] 
createdAt: Date

从上面的小架构中,我有一个设置,患者可以多次进行测试,并且他们各自的测试保存在 Tests collection 中。此外,date 会记录他们参加的所有测试。医生可以请求查看患者的记录,在这种情况下,患者只有一个 record document,其中嵌入了他们的 tests records。目前,我面临的问题是获取患者最新和最旧的测试分数以及他们的初始详细信息。

我可以做一个猫鼬填充来获取有关用户的所有信息,例如

await User.findById(userId).populate({
  path: "medicalVisits"
  model: "records"
  populate: {
    path: "tests"
    model: "test"
 }
})

该操作会返回患者的记录以及他们自注册以来所做的所有测试。但是当我对数据库进行这样的调用时,我只想检索患者的最新和最旧的分数。换句话说,我想获得患者Initial test score,以及他们最近的测试成绩。我是Mongoose aggregation 的新手,我尝试使用Mongoose aggregate 函数,但它返回一个空数组,我想我错过了一些东西。

目前,这是我的聚合管道的样子。

const user = await Doctor.aggregate([
      { $match: { _id: docId } },
      {
        $lookup: {
          from: "users",
          localField: "patients",
          foreignField: "_id",
          as: "patients",
        },
      },
      { $unwind: "$patients" },
      { $unwind: "$patients.medicalVisits" },
      {
        $lookup: {
          from: "records",
          localField: "patients.user",
          foreignField: "_id",
          as: "patientRecord",
        },
      },
      { $unwind: "$patientRecord" },
      // { $sort: { createdAt: 1 } },
      {
        $group: {
          _id: docId,
          user: { $last: "$patients" },
          record: { $last: "$patientRecord"}
        },
      },
    ]);
    return user[0];

从上面的sn-p,我的意图是: 给定doctor Id,他们可以看到他们的患者列表,还可以看到他们最新和最旧的测试成绩。

预期输出


const output = {
userId: 6e12euido....
name: "John doe"
email: "john@john.com"
rating: 2
initialTestScore: 10
recentTestScore: 30
}

我该怎么做?或者有什么更好的选择?非常感谢。

【问题讨论】:

  • 在你的第二个$lookup阶段,应该是localField: "patients.medicalVisits"

标签: node.js mongodb mongoose aggregation-framework


【解决方案1】:

我建议您在获得 userId/patientId 后执行以下操作:-

  1. 按排序顺序从数据库中获取他们的测试(全部)。
  2. 根据您应用的排序顺序(升序或降序)获取数组的第一个和最后一个元素作为初始和最终测试报告

如果您可以只检索用户详细信息和所有测试而不进行任何排序,那么您可以按照以下方式进行:-

  1. 循环遍历所有测试并根据测试日期对测试进行排序。
  2. 根据您应用的排序顺序(升序或降序)获取数组的第一个和最后一个元素作为初始和最终测试报告

您将不会在 DB 端执行操作,因此可能存在轻微的速度问题,但除非用户进行十亿次测试,否则差异仍会以毫秒为单位。

如果有帮助,请告诉我,如果没有帮助,请告诉我

【讨论】:

    【解决方案2】:

    基于这些集合(我从您的问题中了解到):

    // doctor collection:
    { _id: "doc1", patients: ["user1"] }
    // user collection:
    {
      _id: "user1", name: "John", email: "john@gmail.com",
      medicalVisits: ["record1", "record2"]
    }
    // record collection:
    { _id: "record1", rating: 2, tests: ["test1", "test2"] }
    { _id: "record2", rating: 4, tests: ["test3"] }
    // test collection:
    { _id: "test1", testScore: 12, createdAt: ISODate("2021-12-04") }
    { _id: "test2", testScore: 9, createdAt: ISODate("2021-12-05") }
    { _id: "test3", testScore: 15, createdAt: ISODate("2021-12-24") }
    

    我们可以申请:

    db.doctor.aggregate([
    
      { $match: { _id: "doc1" } }
    
      { $lookup: {
        from: "user",
        localField: "patients", foreignField: "_id",
        as: "patients"
      }},
    
      { $unwind: "$patients" }, { $unwind: "$patients.medicalVisits" },
    
      { $lookup: {
          from: "record",
          localField: "patients.medicalVisits", foreignField: "_id",
          as: "records"
      }},
    
      { $unwind: "$records" }, { $unwind: "$records.tests" },
    
      { $lookup: {
        from: "test",
        localField: "records.tests", foreignField: "_id",
        as: "tests"
      }},
    
      { $unwind: "$tests" },
    
      { $sort: { "tests.createdAt": 1 } },
    
      { $group: {
        _id: "$patients._id",
        name: { $first: "$patients.name" },
        email: { $first: "$patients.email" },
        rating: { $first: "$records.rating" },
        initialTestScore: { $first: "$tests.testScore" },
        recentTestScore: { $last: "$tests.testScore" }
      }},
    
      { $set: { "userId": "$_id" } }, { $unset: "_id" }
    ])
    

    为了提取:

    {
      userId: "user1",
      name: "John",
      email: "john@gmail.com",
      rating: 2,
      initialTestScore: 12,
      recentTestScore: 15
    }
    

    与您的查询相比的差异:

    • $lookup test 集合,因为您似乎可以从那里获取考试日期和考试成绩的信息。
    • 我在$group 之前的$sort 测试日期(createdAt),这样我们就可以定义正确的顺序来选择$first$last 测试分数。
    • 我通过在用户字段的每个组上使用$first 提取用户信息(因为给定用户的所有展开记录都具有相同的用户信息):例如email: { $first: "$patients.email" }
    • 我提取$sort 顺序定义的用户的$first$last 测试分数:initialTestScore: { $first: "$tests.testScore" }recentTestScore: { $last: "$tests.testScore" }
    • 我终于$set/$unset_id字段重命名为userId

    【讨论】:

      【解决方案3】:

      尽我所能理解你的情况,我认为你的聚合管道应该是这样的:

      const patientsWithNewestRecord = await Doctor.aggregate([
            { $match: { _id: docId } },
            {
              $lookup: {
                from: "users",
                localField: "patients",
                foreignField: "_id",
                as: "patients",
              },
            },
            // one patient, per doc
            { $unwind: "$patients" },
            // one patient with all his/her visit records, per doc
            {
              $lookup: {
                from: "records",
                localField: "patients.medicalVisits",
                foreignField: "_id",
                as: "patientRecords",
              },
            },
            // one patient with one visit record, per doc
            { $unwind: "$patientRecords" },
            // sort by patient first, createdAt second
            { $sort: { 'patientRecords.user': 1, 'patientRecords.createdAt': 1 } },
            {
              $group: {
                _id: { patient: '$patientRecords.user' },
                user: { $last: "$patients" },
                record: { $last: "$patientRecords"}
              },
            },
          ]);
      

      此管道返回医生的患者列表,并查看他们的最新测试记录。最早的测试记录应该在类似的战争中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-08-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-14
        • 1970-01-01
        • 2011-03-26
        相关资源
        最近更新 更多