James
James

Reputation: 2542

MongoDB: Update one element of an array based on the presence of another

I have an array field on my data. I want to find all documents having a specific two values in that array, and update one of the two. I think I need the $ to do this, but I'm stuck as to how to update the correct element. To make it a little more challenging, the elements of the array are documents, not simple values.

so far I have

db.getCollection('Submissions').update(
{
    "data": {$elemMatch:{
        label:"Level 2", 
        value: {$ne: ""}}},
    "data": {$elemMatch:{
        label:"Level"
    }}
},{})

Using find with that query gets me the right document, now I want to update from those the element with label "Level" and set the value to 2. That'sthe bit that stumps me.

Upvotes: 1

Views: 2447

Answers (1)

Alex Blex
Alex Blex

Reputation: 37128

You can't use $ directly. It refers to the first matching sub-document, and you have at least 2 matching ones.

You need to do it on application level. Select document id, then update by id:

db.getCollection('Submissions').find(
{
    "data": {$elemMatch:{
        label:"Level 2", 
        value: {$ne: ""}}},
    "data": {$elemMatch:{
        label:"Level"
    }}
},{_id:1});

db.getCollection('Submissions').update(
{
    "_id": "_id from previous query",
    "data": {$elemMatch:{
        label:"Level"
    }}
},{ $set: { "data.$.value": 2} })

Alternatively you can shift second match to $where function to leave only one $elemMatch that should match the sub-document to update:

db.getCollection('Submissions').update(
{
    "data": {$elemMatch:{ label:"Level"} },     
    $where: function() { 
        return obj.data.filter(
            el => el.label == "Level 2" && el.value != ""
        ).length >= 1 
    },    
},{ $set: { "data.$.value": 2} })

It is quite slow though. I didn't benchmark it, but I believe the first approach should be faster.

Upvotes: 2

Related Questions