Search code examples
node.jsmongodbmongoose

Update a field in an array only if it exists via mongo query


I'm trying to update a field in an array, but using $set adds that field if it doesn't already exist, how to avoid that? This is my model:

[
  {
    "config": {
      "isEnabled": false,
      "test": {
        "sub": [
          {
            "sub": [
              {},
              {},
              {},
              {},
              {
                "k": "l"
              }
            ]
          },
          {
            "sub": [
              {},
              {},
              {},
              {},
              {
                "k": "l"
              }
            ]
          },
          {
            "sub": [
              {},
              {},
              {},
              {}
            ]
          }
        ]
      }
    }
  },
]

Here's the query I'm running

db.collection.update({
  "config.isEnabled": false
},
{
  "$set": {
    "config.test.sub.$[].sub.4": {
      "b": "l"
    }
  }
})

Output:

[
  {
    "_id": ObjectId("5a934e000102030405000000"),
    "config": {
      "isEnabled": false,
      "test": {
        "sub": [
          {
            "sub": [
              {},
              {},
              {},
              {},
              {
                "b": "l"
              }
            ]
          },
          {
            "sub": [
              {},
              {},
              {},
              {},
              {
                "b": "l"
              }
            ]
          },
          {
            "sub": [
              {},
              {},
              {},
              {},
              {
                "b": "l"
              }
            ]
          }
        ]
      }
    }
  }
]

But I want to attain this result:

[
  {
    "_id": ObjectId("5a934e000102030405000000"),
    "config": {
      "isEnabled": false,
      "test": {
        "sub": [
          {
            "sub": [
              {},
              {},
              {},
              {},
              {
                "b": "l"
              }
            ]
          },
          {
            "sub": [
              {},
              {},
              {},
              {},
              {
                "b": "l"
              }
            ]
          },
          {
            "sub": [
              {},
              {},
              {},
              {},
              {
                "b": "l"
              }
            ]
          }
        ]
      }
    }
  }
]

If you observe the last element in the third element in the array of the model, it does not contain k=l, hence I do not want to set that field in the last element of the array

i.e, in the model this is the element:

{
   "sub": [
    {},
    {},
    {},
    {}
   ]
}

When I run my query it updatea that element to:

{
   "sub": [
    {},
    {},
    {},
    {},
    {
     "b": "l"
    }
   ]
}

But I don't want it to update that element

Link to mongo playground: Mongo Playground For The Above Example

IF THIS CAN BE ACHIEVED IN MONGOOSE, THAT'D WORK TOO

Thanks for your time and I appriciate any help

I tried using addToSet and setOnInsert, none of those work, I know I can find the documents then manually add some logic to update the fields appropriately then update the document but I want to know if there's a way to achieve it via mongo query


Solution

  • One option is to use $[<identifier>] for this:

    db.collection.update(
      {"config.isEnabled": false},
      {$set: {"config.test.sub.$[item].sub.4": {"b": "l"}}},
      {arrayFilters: [{"item.sub.4": {$exists: true}}]}
    )
    

    See how it works on the playground example