Home > other >  Having trouble in excluding nested object property in project stage in aggregation pipeline
Having trouble in excluding nested object property in project stage in aggregation pipeline

Time:11-05

I have an aggregation query that returns array of questions with different schema depending on type of document. The documents are in the following format. Some questions may look like this

{
   "_id": "ID01",
   "Description": "1.Any Question?",
   "answer": { "option05": "E. " },
   "options": {
               "option01": "A.",
               "option02": "B.",
               "option03": "C.",
               "option04": "D.",
               "option05": "E."
              },
   "type": "1",
   "score": 10
},
    

and some questions have the following structure

{
  "_id": "ID02",
  "Description": "Question Description.",
  "emq": {
           "emq3": {
                     "answer": "option12",
                     "explanation": "",
                     "question": "1 Any Qstn?"
                   },
           "emq4": {
                     "answer": "option03",
                     "explanation": "",
                     "question": "2 Any Qstn?"
                   },
           "emq5": {
                     "answer": "option06",
                     "explanation": "",
                     "question": "3 Any Qstn?"
                    }
            },
                               
  "options": {
               "option01": "",
               "option02": "",
               "option03": "",
               "option04": "",
             },
  "type": "2",
  "score": 100
},

I have used the following aggregation pipeline for hiding answers in the questions, (the schema in question is migrated and I cannot change its structure)

Aggregation pipeline

{
          $lookup: {
            from: 'questions',
            let: {
              question_id: { $toObjectId: '$qId' },
              score: '$score',
            },
            pipeline: [
              {
                $match: {
                  $expr: {
                    $eq: ['$_id', '$$question_id'],
                  },
                },
              },
              {
                $project: {
                  answer: {
                    $cond: {
                      if: { $eq: ['$type', '1'] },
                      then: 0,
                      else: {
                        $cond: {
                          if: { $eq: ['$type', '3'] },
                          then: {
                            $cond: {
                              if: {
                                emq: {
                                  regex: /^emq\d $/,
                                },
                              },
                              then: 0,
                              else: 1,
                            },
                          },
                          else: 1,
                        },
                      },
                    },
                  },
                  Description: 1,
                  options: 1,
                  type: 1,
                  emq: 1,
                  score: 1,
                },
              },
          }

It works for questions that are of type 1 but unfortunately I am unable to hide answers for type 2 given the fact that they are not only within 'emq' object but also within nested 'emq3' object with dynamic numbers at the end of string 'emq', I applied regex but somehow I am unable to project answer within emq3 object, is there anyway to achieve the following result that hides answers from both questions like this

{
   "_id": "ID01",
   "Description": "1.Any Question?",
   "options": {
               "option01": "A.",
               "option02": "B.",
               "option03": "C.",
               "option04": "D.",
               "option05": "E."
              },
   "type": "1",
   "score": 10
},
{
      "_id": "ID02",
      "Description": "Question Description.",
      "emq": {
               "emq3": {
                         "question": "1 Any Qstn?"
                       },
               "emq4": {
                         "question": "2 Any Qstn?"
                       },
               "emq5": {
                         "question": "3 Any Qstn?"
                        }
                },
                                   
      "options": {
                   "option01": "",
                   "option02": "",
                   "option03": "",
                   "option04": "",
                 },
      "type": "2",
      "score": 100
    },

CodePudding user response:

You can do the followings in an aggregation pipeline:

  1. convert the object emq into an array of k-v tuple using $objectToArray
  2. $project to remove the field answer and explanation
  3. convert the array emq` back to object
db.collection.aggregate([
  {
    "$addFields": {
      "emq": {
        "$objectToArray": "$emq"
      }
    }
  },
  {
    "$project": {
      "emq.v.answer": false,
      "emq.v.explanation": false,
      "answer": false
    }
  },
  {
    "$addFields": {
      "emq": {
        "$arrayToObject": "$emq"
      }
    }
  },
  {
    "$addFields": {
      emq: {
        "$cond": {
          "if": {
            $eq: [
              "$emq",
              null
            ]
          },
          "then": "$$REMOVE",
          "else": "$emq"
        }
      }
    }
  }
])

Here is the Mongo playground for your reference.

  • Related