Search code examples
databasemongodbmongoosefind

Find Specific Field in nested Object with mongoose


I Have a document like this :

{
  "_id": {
    "$oid": "64494e6a2f90fa940d5a406c"
  },
  "theme": "Harry Potter",
  "date": {
    "$date": "2023-04-26T22:00:00.000Z"
  },
  "quiz": [
    {
      "question": "Qui a une cicatrice sur le front ?",
      "responses": [
        {
          "nbVote": 1,
          "label": "Ron",
          "isCorrect": false,
          "_id": {
            "$oid": "64494e6a2f90fa940d5a405a"
          }
        },
        {
          "nbVote": 0,
          "label": "Hermione",
          "isCorrect": false,
          "_id": {
            "$oid": "64494e6a2f90fa940d5a405b"
          }
        },
        {
          "nbVote": 0,
          "label": "Malfoy",
          "isCorrect": false,
          "_id": {
            "$oid": "64494e6a2f90fa940d5a405c"
          }
        },
        {
          "nbVote": 0,
          "label": "Harry",
          "isCorrect": true,
          "_id": {
            "$oid": "64494e6a2f90fa940d5a405d"
          }
        }
      ],
      "_id": {
        "$oid": "64494e6a2f90fa940d5a405e"
      }
    },
    {
      "question": "De quel maison fait partie Harry Potter ?",
      "responses": [
        {
          "nbVote": 0,
          "label": "Poufsouffle",
          "isCorrect": false,
          "_id": {
            "$oid": "64494e6a2f90fa940d5a4063"
          }
        },
        {
          "nbVote": 0,
          "label": "Serpentard",
          "isCorrect": false,
          "_id": {
            "$oid": "64494e6a2f90fa940d5a4064"
          }
        },
        {
          "nbVote": 0,
          "label": "Serdaigle",
          "isCorrect": false,
          "_id": {
            "$oid": "64494e6a2f90fa940d5a4065"
          }
        },
        {
          "nbVote": 1,
          "label": "Gryffondor",
          "isCorrect": true,
          "_id": {
            "$oid": "64494e6a2f90fa940d5a4066"
          }
        }
      ],
      "_id": {
        "$oid": "64494e6a2f90fa940d5a4067"
      }
    }
  ],
  "nbPlayers": 0,
  "__v": 0
}

And I would like get the only the nbVote field in my request, I try like that :

async getNbVote(id: string) {
    return this.dailyQuizModel
      .findOne(
        { 'quiz.responses._id': id },
        {
          'quiz.responses.nbVote': 1,
        },
      )
      .lean()
      .exec();
  }

For example if id === 64494e6a2f90fa940d5a405a, mongoose will return this :

{
  _id: {
  },
  quiz: [
    {
      responses: [
        {
          nbVote: 1,
        },
        {
          nbVote: 0,
        },
        {
          nbVote: 0,
        },
        {
          nbVote: 0,
        },
      ],
    },
    {
      responses: [
        {
          nbVote: 0,
        },
        {
          nbVote: 0,
        },
        {
          nbVote: 0,
        },
        {
          nbVote: 1,
        },
      ],
    },
  ],
}

But I only want the nbVote from the Id I gave, I don't know what can i do... Thanks for help ! I Try with arrayFilters, and Aggregate but with no success


Solution

  • Here's one way you could do it. Comments are in the aggregation pipeline.

    db.collection.aggregate([
      {// find doc with correct quiz.responses._id
        "$match": {
          "quiz.responses._id": ObjectId("64494e6a2f90fa940d5a405a")
        }
      },
      {
        "$project": {
          // only want nbVote
          "_id": 0,
          "nbVote": {
            "$reduce": {
              // input is an array of arrays
              "input": "$quiz.responses",
              "initialValue": null,
              "in": {
                "$reduce": {
                  // this input is an aray of objects
                  "input": "$$this",
                  // initialValue is set as outer reduce $$value
                  "initialValue": "$$value",
                  "in": {
                    "$cond": [
                      // is this the correct response?
                      {"$eq": ["$$this._id", ObjectId("64494e6a2f90fa940d5a405a")]},
                      // Yes! Get nbVote!
                      "$$this.nbVote",
                      // No.  Keep current $$value
                      "$$value"
                    ]
                  }
                }
              }
            }
          }
        }
      }
    ])
    

    Example output:

    [
      {
        "nbVote": 1
      }
    ]
    

    Try it on mongoplayground.net.

    Rather than using aggregate, you could use find with just minor modifications. See this mongoplayground.net example that uses find instead.