$addFields inside an array of objects

Please consider the following data:

{
  "_id": "F9F39JQH",
  "field": {
    "array": [
      {
        "itemId": 1
      },
      {
        "itemId": 2
      }
    ]
  }
}

I’m trying to add a single field inside the first element or the array.

{
  "_id": "F9F39JQH",
  "field": {
    "array": [
      {
        "itemId": 1,
        "newField": "val"     <- add this field & value.
      },
      {
        "itemId": 2
      }
    ]
  }
}

I tried executing either of the following pipeline updates:

const pipelineUpdate = [
    { $addFields: { "field.array.0": { newField: "val" } } }
];
const pipelineUpdate2 = [
    { $addFields: { "field.array.0.newField": "val" } }
];
db.users.updateOne({ _id: "F9F39JQH" }, pipelineUpdate);

But the result is quite unexpected:

{
  "_id": "F9F39JQH",
  "field": {
    "array": [
      {
        "0": {
          "newField": "val"
        },
        "itemId": 1
      },
      {
        "0": {
          "newField": "val"
        },
        "itemId": 2
      }
    ]
  }
}

$addFields creates a field “0” in all array elements instead of using the “.0” as path selector to identify the target.

The documentation states: To add a field or fields to embedded documents (including documents in arrays) use the dot notation

However I found it not to be the case here. What am I missing ?

Don’t pass the update statement in this case as an array but single object:

db.getCollection("Test").deleteMany({})

db.getCollection("Test").insertMany([
{
    _id:0,
    languages:[
        {
            itemID:1,
            itemVal:'A'
        },
        {
            itemID:2,
            itemVal:'B'
        }
    ]
},
{
    _id:1,
    languages:[
        {
            itemID:3,
            itemVal:'C'
        },
        {
            itemID:4,
            itemVal:'D'
        }
    ]
},
])


db.getCollection("Test").updateMany(
{
    _id:0
},
    {
        $set:{
            'languages.0.newVal2':'test'
        }
    }
)

db.getCollection("Test").find({})

Before:
image

After:
image

I’m sorry if this was not clear but my question about $addField.
I already know about the $set instruction.
$addField is supposed to handle this kind of update in a different manner that better fit my use case and the documentation states that I should be able to use it for my case.

You could then either get the aggregate pipeline to merge back in for the update or this is the documentation for using aggregation stages in the update operation:

I’ll have a play tomorrow with addFields instead see if I can get it working, unless someone else has an example to hand…

1 Like

Hello @Benjamin_Hallion,

As I can see you are using update with aggregation pipeline and it is for to handle some exceptional cases that are not handled by normal update query.

So "field.array.0.newField" is the syntax of a normal update query, the update with aggregation pipeline doesn’t have the privilege of using any of the syntax from normal update query syntax.

You can do something like this in an update with aggregation pipeline syntax,

  • $map to iterate a loop of field.array array
  • $arrayElemAt to get the first element’s value from field.array.itemId
  • $cond and $eq to check if the above first selected itemId value is equal to the current element’s itemId value then do the below merge operation otherwise return the same object $$this
  • $mergeObjects to merge the new property newField that you wanted to add and the current object $$this
const pipelineUpdate = [
  {
    $addFields: {
      "field.array": {
        $map: {
          input: "$field.array",
          in: {
            $cond: [
              {
                $eq: [
                  { $arrayElemAt: ["$field.array.itemId", 0] },
                  "$$this.itemId"
                ]
              },
              { $mergeObjects: [{ newField: "val" }, "$$this"] },
              "$$this"
            ]
          }
        }
      }
    }
  }
];
db.users.updateMany({ _id: "F9F39JQH" }, pipelineUpdate);
2 Likes

Thanks you @John_Sewell and @turivishal for your responses and your time.

@turivishal wrote “the update with aggregation pipeline doesn’t have the privilege of using any of the syntax from normal update query syntax”.
Do you have an official statement or documentation about specific dot notation differences between the aggregation and normal update pipeline ? From my point of view, this is either a bug or the documentation is not telling the whole story.

Hi @Benjamin_Hallion,

I appreciate your follow-up question. While I don’t find any explicit documentation that highlights the differences in dot notation between the aggregation pipeline and the normal update query syntax, it’s understandable that this can be a bit confusing.

The aggregation pipeline and the normal update query syntax are indeed separate mechanisms in MongoDB, each with its own set of rules and behaviors.

If you know what is the aggregation pipeline then it is easy to understand that an update with an aggregation pipeline supports an aggregation pipeline in an update query.

I have already provided the required resource links in my above post.

1 Like

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