How to update last item of an embedded array in MongoDB using Go?

Issue

I have a collection with documents with the following structure:

{
    "_id": ObjectId("..."),
    "rides": [
        {
            status_history: ["status1", "status2", "status3"]
        },
        {
            status_history: ["status4", "status5"]
        }
        ...
    ]
}

What I want to do is append a new status to the status_history array of the the last ride in the rides array, achieving something like:

{
    "_id": ObjectId("..."),
    "rides": [
        {
            status_history: ["status1", "status2", "status3"]
        },
        {
            status_history: ["status4", "status5", "NEW_STATUS_HERE"]
        }
        ...
    ]
}

But I can’t quite figure out how to do that. I’m using the mongo-driver package to connect to the db.

Here’s the code I managed to come up with:

filter := bson.M{"_id": objectId}

arrayFilters := options.ArrayFilters{
    Filters: bson.A{
        bson.M{"s": bson.M{"rides": bson.A{"$slice", -1}}},
    },
}
upsert := true
opts := options.UpdateOptions{
    ArrayFilters: &arrayFilters,
    Upsert:       &upsert,
}
update := bson.M{
    "$push": bson.M{
        "rides.$[s].status_history": statusEntry,
    },
}

result, err := col.UpdateOne(ctx, filter, update, &opts)

fmt.Println(fmt.Sprintf(
    "Matched %v, Modified %v, Upserted %v",
    result.MatchedCount,
    result.ModifiedCount,
    result.UpsertedCount,
))

The output of that Println is Matched 1, Modified 0, Upserted 0. Indeed, inspecting the item in the collection shows that it is unchanged.

What exactly am I getting wrong here?

Solution

This Update With Aggregation Pipeline (feature requires MongoDB v4.2 or higher) will update the last element (nested document) of the rides array, with a new status value added to the status_history array field. The update runs in mongo shell.

let NEW_VALUE = "new_status_999";

db.test.updateOne(
{ _id: 1 },
[
{ 
    $set: {
        rides: { 
            $concatArrays: [
                { $slice: [ "$rides", { $subtract: [ { $size: "$rides" }, 1 ] } ] },
                [ {
                    $let: {
                        vars: { lastEle: { $arrayElemAt: [ { $slice: [ "$rides", -1 ] }, 0 ] } }, 
                        in: {
                            $mergeObjects: [ 
                                "$$lastEle", 
                                { status_history: { $concatArrays: [ "$$lastEle.status_history", [ NEW_VALUE ] ] } } 
                            ]
                        }
                    }
                }]
            ]
        }
    }
}
])

The updated document:

{
        "_id" : 1,
        "rides" : [
                {
                        "status_history" : [
                                "status1",
                                "status2",
                                "status3"
                        ]
                },
                {
                        "status_history" : [
                                "status4",
                                "status5",
                                "new_status_999"    // <--- the update result
                        ]
                }
        ]
}

Answered By – prasad_

Answer Checked By – Marilyn (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.