Search code examples
node.jsmongodbmongoose

How do I update an array within an array in mongodb


In the function below, if the user, topic and subtopic do not exist, a new testRecord is inserted into the testRecords array of the User collection. This works fine. On the other hand, if the combination of the user, topic and subtopic exist, only the scores array of testRecords is to be populated with the new scores. This is not working. The code segment that begins: if(existingTopicSubtopic) is the part that is not working.

exports.updateUserTestRecord = async (req, res) => {
console.log('req.body', req.body)
const {topicName, subtopicName, rightAnswers, wrongAnswers, date} = req.body;

const {
    userId
} = jwt.verify(req.headers.authorization, process.env.JWT_SECRET);

const existingTopicSubtopic = await User.findOne(
    {
        _id: userId,
        'testRecords.topic': topicName,
        'testRecords.subtopic': subtopicName
    },
)

let user;

if(existingTopicSubtopic) {
    console.log('EXISTING USER')
    user = await User.findOneAndUpdate(
        {
            _id: userId,
            'testRecords.topic': topicName,
            'testRecords.subtopic': subtopicName
        },
        { 
            $addToSet : { 
                testRecords : [
                { 
                    'scores.$.rightAnswers': rightAnswers,
                    'scores.$.wrongAnswers': wrongAnswers,
                    'scores.$.date': date,
                }
                ] 
            } 
            
        },
    )

} else {
    console.log('NOT EXISTING USER')
    user = await User.findOneAndUpdate(
        {
            _id: userId,
        },
        { 
            $addToSet : { 
                testRecords : [
                    {
                        topic: topicName,
                        subtopic: subtopicName,
                        scores: [
                            { 
                                rightAnswers,
                                wrongAnswers,
                                date,
                            }
                        ]
                    }
                ] 
            } 
        },
    )
}

}


Solution

  • For inner array operation, we need to use $[identifier] like $[element]. In your case:

    if (existingTopicSubtopic) {
        await User.findOneAndUpdate(
          {
            _id: userId,
            "testRecords.topic": topicName,
            "testRecords.subtopic": subtopicName,
          },
          {
            $addToSet: {
              "testRecords.$[element].scores": {
                rightAnswers: rightAnswers,
                wrongAnswers: wrongAnswers,
                date,
              },
            },
          },
          {
            arrayFilters: [
              {
                "element.topic": topicName,
                "element.subtopic": subtopicName,
              },
            ],
          }
        );
      } else {
        await User.findOneAndUpdate(
          {
            _id: userId,
          },
          {
            $addToSet: {
              testRecords: {
                topic: topicName,
                subtopic: subtopicName,
                scores: [
                  {
                    rightAnswers,
                    wrongAnswers,
                    date,
                  },
                ],
              },
            },
          }
        );
      }