简体   繁体   中英

MongoDB aggregate different collections

i've been trying to do this aggregation since yesterday with no luck, hope you guys can give me some ideas. A little background, I have 2 collections, one for results and another for questions. Results is basically where people solve questions, so it can have 1 question or up to 99 if i'm not mistaken. This is the simplified schema:

Results Schema:
_id: ObjectId("6010664ac5c4f77f26f5d005")
questions: [
  {
    _id: ObjectId("5d2627c94bb703bfcc910763"),
    correct: false
  },
  {
    _id: ObjectId("5d2627c94bb703bfcc910764"),
    correct: true
  },
  {
    _id: ObjectId("5d2627c94bb703bfcc910765"),
    correct: true
  }
]

So, on this specific object, the user answered 3 questions and got 2 of them correct.

Questions Schema:
_id: ObjectId("5d2627c94bb703bfcc910763")

What i'm struggling to do is: for each element in all the questions schema, I have to check if the question was answered - ie (check if there's an _id of questions array == _id on the Questions Schema, yes there can be multiple questions with the same _id as Questions Schema, but Questions Schema _id is unique). If that question was answered, I need to check if correct = true, if so, I add it to a correctAnswer variable, if not, wrongAnswer.

I've tried many things so far with conditions, group to get the $sum of correct and wrong answers, lookup to join both collections but so far, I can't even get to show just the aggregation result.

One of the things I tried (i was trying to do baby steps first) but as mentioned before, couldn't even get the result printed.

Result.aggregate([
    $lookup: {
      from: 'question',
      localField: 'questions._id',
      foreignField: '_id',
      as: 'same'
    },

But this gets me both collections combined, and 'same' comes as empty array, tried using match with also no luck. I also did a $project to just get the information I wanted

$project: {
  _id: 0,
  questions: {
  _id: 1,
  correct: 1
  }
},

Tried using $group: $group: { _id: "$_id", $cond: {if: {"$correct": { $eq: true}}, then: {testField: {$sum: 1}}, else: {testField: 0}} } And as I said, i was just trying to do baby steps so it the testField was beeing manually set, also tried many other things from stackoverflow. Would appreciate the help and sorry for the very long text, just wanted to put in some examples that I did and tried.

TLDR : Need to find a question from the Results Schema where _id matches an _id from the Questions Schema , if there is, check if correct: true or correct: false . Update Questions Schema accordingly with how many were correct and how many were wrong for each question from Questions Schema . Example: newField: {correctAnswer: 4, wrongAnswer: 3} so in this case, there were 7 questions from the Result schema question array that matched an _id from Question Schema , 4 had correct: true and 3 had correct: false . Then it goes on like this for the rest of Question Schema

I don't know of a way to "$lookup" and update at the same time. There's probably a better way to do this, but the aggregation pipeline below creates the documents that could be used in a subsequent update . The pipeline correctly counts repeat questions by a single Result _id , in case someone keeps trying a question until they get it right. One possible issue is that if a question has no Result answers, then no "newField": { "correctAnswers": 0, "wrongAnswers": 0 } document is created.

db.Question.aggregate([
  {
    // lookup documents in Result that match _id
    "$lookup": {
      "from": "Result",
      "localField": "_id",
      "foreignField": "questions._id",
      "as": "results"
    }
  },
  {
    // unwind everything
    "$unwind": "$results"
  },
  {
    // more of everything
    "$unwind": "$results.questions"
  },
  {
    // only keep answers that match question
    "$match": {
      "$expr": { "$eq": [ "$_id", "$results.questions._id" ] }
    }
  },
  {
    // reassemble and count correct/wrong answers
    "$group": {
      "_id": "$_id",
      "correct": {
        "$sum": {
          "$cond": [ { "$eq": [ "$results.questions.correct", true ] }, 1, 0 ]
        }
      },
      "wrong": {
        "$sum": {
          "$cond": [ { "$eq": [ "$results.questions.correct", false ] }, 1, 0 ]
        }
      }
    }
  },
  {
    // project what you want as output
    "$project": {
      newField: {
        correctAnswers: "$correct",
        wrongAnswers: "$wrong"
      }
    }
  }
])

Try it on mongoplayground.net .

For a scenario where "$lookup" can't be used because the Question collection is in a different database, the Result collection may be used to generate output documents to update the Question collection.

Here's one way to do it.

db.Result.aggregate([
  {
    "$unwind": "$questions"
  },
  {
    "$group": {
      "_id": "$questions._id",
      "correct": {
        "$push": "$questions.correct"
      }
    }
  },
  {
    "$project": {
      "newField": {
        "correctAnswers": {
          "$size": {
            "$filter": {
              "input": "$correct",
              "as": "bool",
              "cond": "$$bool"
            }
          }
        },
        "wrongAnswers": {
          "$size": {
            "$filter": {
              "input": "$correct",
              "as": "bool",
              "cond": { "$not": "$$bool" }
            }
          }
        }
      }
    }
  }
])

Try it on mongoplayground.net .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM