0

How can I push value into multiple nested array with specific conditions?
I have a document like this

[
  {
    "_id": "class_a",
    "students": [
      {
        "_id": "1a",
        "name": "John",
        "grades": []
      },
      {
        "_id": "1b",
        "name": "Katie",
        "grades": []
      },
      {
        "_id": "1c",
        "name": "Andy",
        "grades": []
      },
      
    ]
  }
]

Query to insert into nested array. (Not sure what is missing here)

db.collection.update({
  "_id": "class_a",
  "students": {
    $elemMatch: {
      "_id": {
        "$in": [
          "1a",
          "1b"
        ]
      }
    }
  }
},
{
  $push: {
    "students.$.grades": "A+"
  }
})

Got the following result. But I was expecting both John and Katie have A+ in grades

[
  {
    "_id": "class_a",
    "students": [
      {
        "_id": "1a",
        "grades": ["A+"],
        "name": "John"
      },
      {
        "_id": "1b",
        "grades": [],
        "name": "Katie"
      },
      {
        "_id": "1c",
        "grades": [],
        "name": "Andy"
      }
    ]
  }
]

Expected result

[
  {
    "_id": "class_a",
    "students": [
      {
        "_id": "1a",
        "grades": ["A+"],
        "name": "John"
      },
      {
        "_id": "1b",
        "grades": ["A+"],
        "name": "Katie"
      },
      {
        "_id": "1c",
        "grades": [],
        "name": "Andy"
      }
    ]
  }
]

Mongo playground to test the code

2 Answers 2

1

You can use $[<identifier>] to update only the items that match a condition. Your first {} is to find the relevant documents, while the arrayFilters is to find the relevant items inside the document nested array:

db.collection.update(
  {_id: "class_a", students: {$elemMatch: {_id: {$in: ["1a", "1b"]}}}},
  {$push: {"students.$[item].grades": "A+"}},
  {arrayFilters: [{"item._id": {$in: ["1a", "1b"]}}], upsert: true}
)

See how it works on the playground example

Sign up to request clarification or add additional context in comments.

5 Comments

This is a good solution also, I would have mark this is the answer if I can mark more than one. Thank you for your help!
This is the first answer and a better one
Ok then, can you justify how is this solution better? Like is this going to run faster in terms of performance?
It is justified in the explanation and on the comments of the other answer...
After reading the second time, I agree that your solution might be better performance-wise.
1

You should really use arrayFilters for these otherwse it'll only match the first entity. You don't need to use $elemMatch at all.

Playground - https://mongoplayground.net/p/_7y89KB83Ho

db.collection.update({
  "_id": "class_a"
},
{
  $push: {
    "students.$[students].grades": "A+"
  }
},
{
  "arrayFilters": [
    {
      "students._id": {
        "$in": [
          "1a",
          "1b"
        ]
      }
    }
  ]
})

3 Comments

There is a need to use $elemMatch in order to find the relevant document. Your answer will try the arrayFilters on all items on all documents
Are you sure? I'm happy to be wrong and learn, but I was under the impression that an arrayFilter would be all that is needed. The playground version works as expected. Ah, I see what you mean, the initial match (_id:"class_a") should limit the ATTEMPT to apply the $push and $arrayFilters, eg, if there were no record of 1a or 1b, it would still ATTEMPT to apply the array filters. I would be interested to know the actual performance impact of this. I think I would prefer this option in terms of readability anyway. But I get what you're saying. Thanks
In terms of readability, this solution is better. Didn't knew we can use arrayFilter like this. Thank you so much!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.