【问题标题】:MongoDB lookup when foreign field and local field are array of objects当外部字段和本地字段是对象数组时的MongoDB查找
【发布时间】:2020-05-30 06:57:23
【问题描述】:

我有两个集合“问题”和“答案”。 问题文档是这样的:

      [
        {
          _id: "8326ccbn73487290nc",
          questions: [
           {id: "12345", question: "Test", correct_answer: '0'},
           {id: "123456", question: "Test 2", correct_answer: '1'},
           {id: "1234567", question: "Test 3", correct_answer: '1'},
        ]
       },
       {
          _id: "8326ccbn734872ytruy90nclk",
          questions: [
           {id: "345", question: "Test", correct_answer: '0'},
           {id: "3456", question: "Test 2", correct_answer: '1'},
           {id: "34567", question: "Test 3", correct_answer: '1'},
        ]
       }
     ]

answers文档是这样的:

{
  id: '327rhrne7fr873',
  user_id: '43757fn574057fnf',
  question_id: '8326ccbn73487290nc',
  answers: [
    { id: '12345', student_answer: '1'},
    { id: '123456', student_answer: '0'},
  ]
}

所以我想返回这样的东西:

[
    {
        _id: '8326ccbn73487290nc',
        questions: [
          {id: "12345", question: "Test", correct_answer: '0', student_answers: '1'},
          {id: "123456", question: "Test 2", correct_answer: '1', , student_answers: '0'},
          {id: "1234567", question: "Test 3", correct_answer: '1'},
        ]
    },
    {
          _id: "8326ccbn734872ytruy90nclk",
          questions: [
           {id: "345", question: "Test", correct_answer: '0'},
           {id: "3456", question: "Test 2", correct_answer: '1'},
           {id: "34567", question: "Test 3", correct_answer: '1'},
        ]
       }
]

谁能帮我做这样的事情。

【问题讨论】:

  • 整个集合中只有一个问题对象吗?一个问题可以在多个_id不同的问题对象中吗?
  • 整个集合可以有多个问题。不,一个问题不能针对具有不同 _id 的多个问题。
  • 再来一个问题,一个答案对象可以匹配2个问题对象吗?
  • 不,不能在两个问题对象上匹配
  • 我只想要一个答案,因为我用 user_id 提出了 get 请求。

标签: mongodb mongodb-query aggregation-framework aggregate


【解决方案1】:

虽然可以在 MongoDB 中获得您想要的结构。如果你不需要额外的聚合步骤,我建议只查询你需要的数据,并在你的应用层做数据映射,因为它更容易,我们不想在数据库上做太多的工作。

但是,您可以使用以下方法来获得(接近)您的预期输出,我保留了来自answers 集合的字段student_answer,并且没有将其重命名为student_answers,您可以做额外的事情如果需要,请执行步骤。

db.questions.aggregate([
  {
    $lookup: {
      from: "answers",
      let: {
        question_id: "$_id"
      },
      as: "answer",
      pipeline: [
        {
          $match: {
            $expr: {
              $and: [
                {
                  $eq: [
                    "$question_id",
                    "$$question_id"
                  ]
                },
                {
                  $eq: [
                    "$user_id",
                    userId // insert user id variable here
                  ]
                }
              ]
            }
          }
        }
      ]
    }
  },
  {
    $unwind: {
      path: "$answer",
      preserveNullAndEmptyArrays: true
    }
  }, // up until this step, you already got all the data you need. You can stop here and do the extra steps in your application layer
  {
    $set: {
      questions: {
        $map: {
          input: "$questions",
          in: {
            $mergeObjects: [
              "$$this",
              {
                $arrayElemAt: [
                  {
                    $filter: {
                      input: "$answer.answers",
                      as: "answer",
                      cond: {
                        $eq: [
                          "$$this.id",
                          "$$answer.id"
                        ]
                      }
                    }
                  },
                  0
                ]
              }
            ]
          }
        }
      }
    }
  },
  {
    $unset: "answer" // cleanup by removing temporary field answer
  }
])

Mongo Playground

【讨论】:

  • 你做得很好。十分感谢。当我在我的代码上实现它时,我必须解决为什么我得到“”Unrecognized pipeline stage name: '$set'“”。我的结构看起来不错,和你的一样。
  • 您可能使用的是较旧的 MongoDB 版本,请尝试使用 $addFields 而不是 $set{ $project: { answer: false } } 而不是 $unset: "answer"
  • 非常感谢。这就是我想要的
【解决方案2】:

您可以在答案对象与单个问题对象匹配的假设下使用此管道。

db.answers.aggregate([
    {
        $lookup: {
            from: "questions",
            let: {answerIds: {$map: {input: "$answers", as: "answer", in: "$$answer.id"}}},
            pipeline: [
                {
                    $match: {
                        $expr: {
                            $gt: [
                                {
                                    $size: {
                                        $filter: {
                                            input: "$questions",
                                            as: "question",
                                            cond: {
                                                $setIsSubset: [["$$question.id"], "$$answerIds"]
                                            }
                                        }
                                    }
                                },
                                0
                            ]
                        }
                    }
                }
            ],
            as: "questions"
        }
    },
    {
        $unwind: "$questions"
    },
    {
        $project: {
            _id: "$questions._id",
            questions: {
                $map: {
                    input: "$questions.questions",
                    as: "question",
                    in: {
                        $mergeObjects: [
                            "$$question",
                            {
                                $ifNull: [
                                    {
                                        $arrayElemAt: [
                                            {
                                                $filter: {
                                                    input: "$answers",
                                                    as: "answer",
                                                    cond: {$eq: ["$$answer.id", "$$question.id"]}
                                                }
                                            }
                                            , 0
                                        ]
                                    },
                                    {}
                                ]
                            }
                        ]
                    }
                }
            }
        }
    }
])

【讨论】:

  • 对不起汤姆,也许我不清楚。我想要的回复与问题文档中的 JSON 相同,但只是添加“student_answer”(如果有价值)。当我打电话询问我所有的问题时,我想要的回复。
  • 是的,返回的响应不是这样吗?你试过了吗?
  • 是的。我试试看。我只得到对答案集合有价值的问题(在我的情况下,我想要所有问题,如果它们对答案集合没有价值),我的回答中没有正确答案
  • 我认为查询将在问题集合上,而不是在答案上
  • 一个问题不能有多个答案吗?多个学生..?从答案中做到这一点更有意义,但理论上你可以反转$lookup,其余的几乎相同。
猜你喜欢
  • 2019-07-04
  • 1970-01-01
  • 2021-01-09
  • 1970-01-01
  • 2019-07-11
  • 2020-05-16
  • 1970-01-01
  • 1970-01-01
  • 2019-05-17
相关资源
最近更新 更多