How to update elements of 2 arrays in the same document

Hi,

I know a document exists for an update operation I want to perform. Also I know one of the array elements exists. I want to update that known array element and an element in another array that potentially might require different match criteria. Both arrays contain objects.

Can I use a single update operation to update both arrays when the match criteria for the 2 arrays might be different?
Does “arrayFilters” allow you to get an array index for 2 different arrays?

Sample documents BEFORE update

When array1 does not have match criteria (no prizeId = prize1) matching element for array2

{
  id: "docId-1",
  array1: [
    {id: "asset-01"}
  ],
  array2: [
    {prizeId: "prize1", image: "asset-01"}
  ]
}

When array1 does have match criteria (prizeId = prize1) matching array2

{
  id: "docId-1",
  array1: [
    {id: "asset-01", prizeId: "prize1"}
  ],
  array2: [
    {prizeId: "prize1", image: "asset-01"}
  ]
}

Sample document AFTER update (asset-01 → asset-02)

{
  id: "docId-1",
  array1: [
    {id: "asset-02", prizeId: "prize1"}
  ],
  array2: [
    {prizeId: "prize1", image: "asset-02"}
  ]
}

For both of the sample documents above, I would like to update the element in array2, identified by “prizeId = prize1”, as well as make sure an element matching “prizeId = prize1” in array1 is updated, or array1 is appended if array1 does not contain a matching element.

Thank you for any help!

Hi @John_Grant1,
I hope i’ ve understand in the best way your question. below i’ ve simulate your request:

> db.array.find().pretty()
{
        "_id" : ObjectId("63c18e1727fe9408c37b0258"),
        "id" : "docId-1",
        "array1" : [ ],
        "array2" : [
                {
                        "prizeId" : "prize1",
                        "image" : "asset-01"
                }
        ]
}
{
        "_id" : ObjectId("63c18e1727fe9408c37b0259"),
        "id" : "docId-1",
        "array1" : [
                {
                        "id" : "asset-01",
                        "prizeId" : "prize1"
                }
        ],
        "array2" : [
                {
                        "prizeId" : "prize1",
                        "image" : "asset-01"
                }
        ]
}

And the query for do that:

> db.array.updateMany({"array1":{$exists:true,$ne:[]},"array1.prizeId":"prize1","array2.prizeId":"prize1"},{$set:{"array1.$.id":"asset-02","array2.$.image":"asset-02"}})
{ "acknowledged" : true, "matchedCount" : 1, "modifiedCount" : 1 }
> db.array.find().pretty()
{
        "_id" : ObjectId("63c18e1727fe9408c37b0258"),
        "id" : "docId-1",
        "array1" : [ ],
        "array2" : [
                {
                        "prizeId" : "prize1",
                        "image" : "asset-01"
                }
        ]
}
{
        "_id" : ObjectId("63c18e1727fe9408c37b0259"),
        "id" : "docId-1",
        "array1" : [
                {
                        "id" : "asset-02",
                        "prizeId" : "prize1"
                }
        ],
        "array2" : [
                {
                        "prizeId" : "prize1",
                        "image" : "asset-02"
                }
        ]
}

So in this way i’ ve updated only the field asset of the second document from 01 to 02 And the first document remained unchanged.

Hoping is useful!!

Regards

Hi @Fabio_Ramohitaj ,

Nice to e-meet you and thank you for trying to help!

Unfortunately I don’t think you understood the challenge. I want to update a single document. I provided 2 examples of the state of that single document. BOTH examples should have the SAME RESULT after a single update operation. The “first document remained unchanged” is NOT desired.

Also please note I made an edit to array1 in example 1. array1 is not empty, but lacks the same criteria as array2.

Best,
John

Here is what I’ve tried

  • Update one array at a time :frowning:
// Given:
const params = {
  docId: "docId-1",
  prizeId: "prize1",
  image: "asset-id-after-update",
  oldImage: "asset-01",
}

// Solution:

const filter1 = {
  id: params.docId,
  "array1": {$elemMatch: {$or: [
    {id: params.oldImage},
    {prizeId: params.prizeId}
  ]}},
  // "array2": {$elemMatch: {prizeId: params.prizeId}},
}

const filter2 = {
  id: params.docId,
//  "array1": {$elemMatch: {$or: [
//    {id: params.oldImage},
//    {prizeId: params.prizeId}
//  ]}},
  "array2": {$elemMatch: {prizeId: params.prizeId}},
}

const update1 = {
  $set: {
    "array1.$": {id: params.image, prizeId: params.prizeId},
    // "array2.$.image": params.image,
  },
}

const update2 = {
  $set: {
    //"array1.$": {id: params.image, prizeId: params.prizeId},
    "array2.$.image": params.image,
  },
}

db.getCollection('my-docs').updateOne(filter1, update1)
db.getCollection('my-docs').updateOne(filter2, update2)

Yes, for example, starting with documents:

{ _id: ObjectId("63c18e1727fe9408c37b0259"),
  id: 'docId-1',
  array1: [ { id: 'asset-02', prizeId: 'prize1' } ],
  array2: [ { prizeId: 'prize1', image: 'asset-03' } ] }

The following update:

arrayFilters = [
    { "filter1.id" : "asset-02" } ,
    { "filter2.image" : "asset-03" }
]
dbécollection.updateOne( 
    { "id" : "docId-1" } ,
    { "$set" : {
        "array1.$[filter1].result" : 123 ,
        "array2.$[filter2].result" : 456
    }} ,
    { arrayFilters }
)

Should update the document to:

{ _id: ObjectId("63c18e1727fe9408c37b0259"),
  id: 'docId-1',
  array1: [ { id: 'asset-02', prizeId: 'prize1', result: 123 } ],
  array2: [ { prizeId: 'prize1', image: 'asset-03', result: 456 } ] }

@steevej Your answer helped me arrive at a final solution. I use your 2-filter technique to get an index to each array for $set, and one of the filters uses the $or operator to handle the 2 scenarios array1 can be in.

I will mark this as solved. Thank you!

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.