skip to Main Content

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 ?

I specifically need to add fields inside an existing object of unknown structure. I cannot re-write the whole element using $set for example.

2

Answers


  1. You may update the first element of the array via the aggregation pipeline.

    Set the field.array value with:

    1. Get the first element of the field.array array and add the newField field.

    2. Get all the elements other than the first element of the field.array array.

    3. Combine the result of (1) and (2) into an array.

    db.users.updateOne{
      "_id": "F9F39JQH"
    },
    [
      {
        $set: {
          "field.array": {
            $concatArrays: [
              [
                {
                  $mergeObjects: [
                    {
                      $first: "$field.array"
                    },
                    {
                      "newField": "val"
                    }
                  ]
                }
              ],
              {
                $slice: [
                  "$field.array",
                  1,
                  {
                    $size: "$field.array"
                  }
                ]
              }
            ]
          }
        }
      }
    ])
    

    Demo @ Mongo Playground

    Note that if you are using the MongoDB version 5.2 and above, you may consider replacing the $slice operator with $lastN.

    {
      $lastN: {
        input: "$field.array",
        n: {
          $subtract: [
            {
              $size: "$field.array"
            },
            1
          ]
        }
      }
    }
    

    Note that you don’t need to update with aggregation pipeline. You need the $set operator with dot notation.

    db.users.updateOne({
      "_id": "F9F39JQH"
    },
    {
      $set: {
        "field.array.0.newField": "val"
      }
    })
    
    Login or Signup to reply.
  2. Here is the working live example: https://mongoplayground.net/p/283qGaCzhFY

    Data:

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

    Aggregation:

    db.collection.updateOne({"_id": "F9F39JQH"},
    [
      {
        "$set": {
          "temp": {
            "$mergeObjects": [
              {
                "$first": "$field.array"
              },
              {
                "newField": "provide_new_field_value"
              }
            ]
          }
        }
      },
      {
        "$project": {
          "field.array": {
            "$concatArrays": [
              ["$temp"],
              {
                "$filter": {
                  "input": "$field.array",
                  "cond": {
                    "$ne": [
                      "$$this.itemId",
                      "$temp.itemId"
                    ]
                  }
                }
              }
            ]
          }
        }
      }
    ])
    

    Output:

    [
      {
        "_id": "F9F39JQH",
        "field": {
          "array": [
            {
              "itemId": 1,
              "newField": "provide_new_field_value"
            },
            {
              "itemId": 2
            },
            {
              "itemId": 3
            },
            {
              "itemId": 4
            }
          ]
        }
      }
    ]
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search