Update array object mongoDB

Hello everyone,

I have an array of objects and would like to update the count of the object where categoryId = “menu2” and subCategoryId = “1”.

in my mongodb i currently have two records in the array:

{
    "_id": "xyz",
    "badges": [{
        "count": 2,
        "categorieId": "menu1",
        "subCategorieId": "1"
    }, {
        "count": 1,
        "categorieId": "menu2",
        "subCategorieId": "1"
    }]
}

if i now execute the following method the object with categorieId “menu1” will be updated and not my menu2…

return getCollection()
      .updateOne(
        and(
          eq("badges.categorieId", "menu2"),
          eq("badges.subCategorieId", "1")
        ),
        Updates.inc("badges.$.count", 1)
      );

I am using the io.quarkus.mongodb.reactive.ReactiveMongoCollection.

Thanks in advance!

The filtered positional operator is working:

return getCollection().updateOne(
      eq("_id", "xyz"),
      Updates.combine(
        Updates.inc("badges.$[badges].count", 1)
      ),
      new UpdateOptions()
        .arrayFilters(Arrays.asList(
          and(
            eq("badges.categorieId", "menu2"),
            eq("badges.subCategorieId", "1")
          ))));

Why the other method does not work, i unfortunately do not know.

Hi @neeeextL - Welcome to the community.

I will start by saying I am not too familiar with “io.quarkus.mongodb.reactive.ReactiveMongoCollection.” but I have performed my testing below in mongosh to perhaps help illustrate a possible change to the update operation which may suit your use case.

if i now execute the following method the object with categorieId “menu1” will be updated and not my menu2…

If you need multiple conditions to match an array element, then you’ll need to use $elemMatch.

Please correct me if I am wrong here but I believe the behaviour you’re after would be demonstrated in the Update Embedded Documents Using Multiple Field Matches documentation in which you will be required to use $elemMatch operator in the query filter portion of your code as mentioned above. You can select your specific language at the top right corner of the page (Java) as shown below:

I have performed the above in a test environment using the sample document provided in mongosh. You may need to alter this to suit your environment but this is more so for demonstration purposes to see if it updates the document to how you desire:

/// Original Document
db> db.collection.find()
[
  {
    _id: 'xyz',
    badges: [
      { count: 2, categorieId: 'menu1', subCategorieId: '1' },
      { count: 1, categorieId: 'menu2', subCategorieId: '1' }
    ]
  }
]
/// Using `$elemMatch` in the update
db> db.collection.updateOne({ "badges": { "$elemMatch": { "categorieId": "menu2", "subCategorieId": "1" } } },{$inc:{"badges.$.count":1}})
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}
/// Resulting document after the update
db> db.collection.find()
[
  {
    _id: 'xyz',
    badges: [
      { count: 2, categorieId: 'menu1', subCategorieId: '1' },
      { count: 2, categorieId: 'menu2', subCategorieId: '1' } /// <--- count incremented by 1
    ]
  }
]

Please note that the above was only tested briefly in my test environment (MongoDB Version 5.0) with a single sample document. It is highly recommended to perform any code changes against a test environment to verify it suits your use case before performing the changes in production

In addition to the above, the query parameters specified in your initial update operation are not an array field. They are fields within objects inside the "badges" array field. The $ (update) documentation states:

When used with update operations, e.g. db.collection.updateOne() and db.collection.findAndModify() ,

  • the positional $ operator acts as a placeholder for the first element that matches the query document, and
  • The array field must appear as part of the query document.

Why the other method does not work, i unfortunately do not know.

The documentation does provide a similar example to this which may help illustrate why your initial update operation updated the unexpected element.

However in saying so, the second method you had attempted states the query filter to match on within the arrayFilters section which ended up matching the object inside the badges array you wanted to have updated. To update all elements that match an array filter condition or conditions, see the filtered positional operator instead $[<identifier>] .

However, if you have any further questions please feel free to post them here.

Regards,
Jason

2 Likes