Updating a document in an array of array

my document:

{
    "_id" : ObjectId("60784451b5d2b589bcb138e8"),
    "regions" : [ 
        {
            "name" : "us-east-1",
            "steps" : [ 
                {
                    "name" : "step0",
                    "number" : 0,
                    "completed" : false,
                    "successful" : false,
                    "message" : ""
                }, 
                {
                    "name" : "step1",
                    "number" : 1,
                    "completed" : false,
                    "successful" : false,
                    "message" : ""
                }
            ]
        }, 
        {
            "name" : "us-west-2",
            "steps" : [ 
                {
                    "name" : "step0",
                    "number" : 0,
                    "completed" : false,
                    "successful" : false,
                    "message" : ""
                }, 
                {
                    "name" : "step1",
                    "number" : 1,
                    "completed" : false,
                    "successful" : false,
                    "message" : ""
                }
            ]
        }
    ]
}

What I wanted to do: update the particular step of a particular region.

golang code snipet:


func UpdateStep(id string, step *Step) bool {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	collection := config.Singleton().Database.Collection("deploys")
	objID, _ := primitive.ObjectIDFromHex(id)
	filter := options.ArrayFilters{
		Filters: bson.A{bson.M{"_id": objID}, bson.M{"x.name": step.Region}, bson.M{"y.name": step.Name}}}
	opts := options.Update().SetUpsert(false)
	update := bson.M{
		"$set": bson.M{
			"regions.$[x].steps.$[y].completed":  step.Completed,
			"regions.$[x].steps.$[y].successful": step.Successful,
		},
	}
	ret, err := collection.UpdateOne(ctx, filter, update, opts)
	if err != nil {
		return false
	}

	return true
}

Got error when ran:
multiple write errors: [{write errors: [{No array filter found for identifier ‘x’ in path ‘regions.[x].steps.[y].completed’}]}, {}]

Any help is greatly appreciated.

Hello @mystery_bbs, welcome to the MongoDB Community forum!

What I wanted to do: update the particular step of a particular region.

To update a nested array based upon a condition matching the nested array element’s field, you need to use the arrayFilters option of the UpdateOne operation .

The UpdateOne method takes the arguments: ctx, filter, update and opts. There are two UpdateOptions you are working with; these are the Upsert and the ArrayFilters. The code for update options can look like this in your case.

// Create an instance of an options and set the desired options
upsert := true
arrayFilters := options.ArrayFilters{ // .... }
updateOpts := options.UpdateOptions{
                           ArrayFilters: &arrayFilters
                           Upsert:       &upsert
}

And, use the update options:

ret, err := collection.UpdateOne(ctx, filter, update, &updateOpts)

Your filter field still requires a definition - it needs to be based upon what you want to filter upon. I think you want to filter upon the objID. Then the filter would be:

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

For usage of array filters see this post (with native code, not golang): Updating nested array of objects using $addToSet

@Prasad_Saya thank you for the help! This works:

func UpdateStep(id string, step *Step) bool {
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	collection := config.Singleton().Database.Collection("deploys")
	objID, _ := primitive.ObjectIDFromHex(id)
	filter := bson.D{primitive.E{Key: "_id", Value: objID}}
	arrayFilters := options.ArrayFilters{Filters: bson.A{bson.M{"x.name": step.Region}, bson.M{"y.name": step.Name}}}
	upsert := true
	opts := options.UpdateOptions{
		ArrayFilters: &arrayFilters,
		Upsert:       &upsert,
	}
	update := bson.M{
		"$set": bson.M{
			"regions.$[x].steps.$[y].completed": step.Completed,
		},
	}
	ret, err := collection.UpdateOne(ctx, filter, update, &opts)
	if err != nil {
		fmt.Printf("error updating db: %+v\n", err)
		return false
	}

	return true
}
1 Like

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