Upserting Nested Document

I have a collection, where each document roughly takes the following pattern:

{
    "id": 1,
    "foo": {
        "id": 11,
        "barList": [
            {
                "id": 111,
                "name": "Nested Object",
                "otherProps": "Continue here"
            },
            {
                "id": 112,
                "name": "Nested Object",
                "otherProps": "Continue here"
            },
            {
                "id": 113,
                "name": "Nested Object",
                "otherProps": "Continue here"
            }
        ]
    }
}

If possible, in a single operation, I’d like to do an upsert on the array barList. For example,
if I have a function upsertBar(docId, newBar), here is how I’d like it to be have.

Given newBar as:

{
       "id": 113,
       "name": "Nested Object Modified",
}

I’d like the collection to be modified as:

{
    "id": 1,
    "foo": {
        "id": 11,
        "barList": [
            {
                "id": 111,
                "name": "Nested Object",
                "otherProps": "Continue here"
            },
            {
                "id": 112,
                "name": "Nested Object",
                "otherProps": "Continue here"
            },
            {
                "id": 113,
                "name": "Nested Object Modified",
                "otherProps": "Continue here"
            }
        ]
    }
}

Given newBar as

{
                "id": 114,
                "name": "New Fourth Bar",
                "otherProps": "Continue here"
}

I’d like the collection to return:

{
    "id": 1,
    "foo": {
        "id": 11,
        "barList": [
            {
                "id": 111,
                "name": "Nested Object",
                "otherProps": "Continue here"
            },
            {
                "id": 112,
                "name": "Nested Object",
                "otherProps": "Continue here"
            },
            {
                "id": 113,
                "name": "Nested Object",
                "otherProps": "Continue here"
            },
            {
                "id": 114,
                "name": "New Fourth Bar",
                "otherProps": "Continue here"
            }
        ]
    }
}

How would I do this with MongoDb?

Hello @Brian_Sump, Welcome to the MongoDB community forum,

Refer to this similar topic,

You can also refer to this answer as well,

Thanks - examining.

This is unexpectedly complex for an upsert operation…

Looking at this solution from the Stack Overflow Example:

db.collection.update(
  { _id: _id },
  [{
    $set: {
      myarray: {
        $cond: [
          { $in: [updateDoc.userId, "$myarray.userId"] },
          {
            $map: {
              input: "$myarray",
              in: {
                $mergeObjects: [
                  "$$this",
                  {
                    $cond: [
                      { $eq: ["$$this.userId", updateDoc.userId] },
                      uodateDoc,
                      {}
                    ]
                  }
                ]
              }
            }
          },
          { $concatArrays: ["$myarray", [uodateDoc]] }
        ]
      }
    }
  }]
)

Is this performant? Am I rewriting the entire array every time?

Yes, you are, and i think there is no better option to do this operation.

In addition, documents are completely rewritten to permanent storage when updated, so rewriting the entire array is not a major overhead. Unless you are implementing the massive array anti-pattern.

1 Like

Thanks - good to know. In my use case, it’s an array of small objects (3 or 4 attributes with short strings), though there could be around 50 - 100 elements. I’m presuming that this is less than “massive”, and access patterns are such that it really fits in this collection.

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