Update with array filter doesn't update anything mongo go driver

Issue

I have the following function :

func AuthorizeClasses(teacherId primitive.ObjectID, classesNames []string) error {
    if res := usersCollection.FindOneAndUpdate(
        context.TODO(),
        bson.M{
            "_id": teacherId,
        },
        bson.M{
            "$set": bson.M{
                "teachersettings.classes.$[elem].authorized": true,
            },
        },
        options.FindOneAndUpdate().SetArrayFilters(
            options.ArrayFilters{
                Filters: []interface{}{
                    bson.M{
                        "elem": bson.M{
                            "elem.name": bson.M{
                                "$in": classesNames,
                            },
                        },
                    },
                },
            },
        ),
    ); res.Err() != nil {
        return res.Err()
    }
    return nil
}

It is supposed to set the authorized field to true on all the classes whose name is in the slice that’s an argument to the function.

Here is an example of the documents I’m dealing with :

{
   "_id":{
      "$oid":"625a68ef7cda954a2c60a28b"
   },
   "email":"test@gmail.com",
   "username":"test",
   "password":"40b52e5d6c2d00f632d4b2adb13ed9e90c007619701dee7757124d411e0e8ada",
   "salt":"xksV2aFlihZHlFE",
   "isteacher":true,
   "teachersettings":{
      "schedule":null,
      "classes":[
         {
            "name":"wxipsbkuqyrfhir",
            "authorized":false,
            "students":[
               {
                  "email":"eqfvgvazsrpimtx@ajmje.gmg",
                  "username":"yehfmvpjuf",
                  "id":{
                     "$oid":"626ac9ddd6806cb8838898e2"
                  }
               },
               {
                  "email":"wmhkuhnbrslrlcb@pqdaw.rmo",
                  "username":"xkylcaotxn",
                  "id":{
                     "$oid":"626ac9ddd6806cb8838898e3"
                  }
               },
               {
                  "email":"wsnlgxzmuwjjwow@ziwxh.gsj",
                  "username":"bnazhckxqe",
                  "id":{
                     "$oid":"626ac9ddd6806cb8838898e4"
                  }
               }
            ]
         },
         {
            "name":"rcoelwwhfjilqzo",
            "authorized":false,
            "students":[
               {
                  "email":"eqfvgvazsrpimtx@ajmje.gmg",
                  "username":"yehfmvpjuf",
                  "id":{
                     "$oid":"626ac9ddd6806cb8838898e2"
                  }
               },
               {
                  "email":"wmhkuhnbrslrlcb@pqdaw.rmo",
                  "username":"xkylcaotxn",
                  "id":{
                     "$oid":"626ac9ddd6806cb8838898e3"
                  }
               },
               {
                  "email":"wsnlgxzmuwjjwow@ziwxh.gsj",
                  "username":"bnazhckxqe",
                  "id":{
                     "$oid":"626ac9ddd6806cb8838898e4"
                  }
               }
            ]
         }
      ]
   }
}

For example, if i called the function like this :

AuthorizeClasses(teacher.Id, []string{"wxipsbkuqyrfhir", "rcoelwwhfjilqzo"})

I would expect the two corresponding authorized fields to switch to true.

The problem is the function doesn’t ever update anything. The error attribute of the result is also empty.

Here is the log sequence associated to this operation :

{"t":{"$date":"2022-04-30T19:43:07.596+03:00"},"s":"I",  "c":"WRITE",    "id":51803,   "ctx":"conn56","msg":"Slow query","attr":{"type":"update","ns":"MYPROJECT.users","command":{"q":{"_id":{"$oid":"626d626a355c74c07681428b"}},"u":{"$set":{"teachersettings.classes.$[elem].authorized":true}},"arrayFilters":[{"elem":{"elem.name":{"$in":["bvzhahfllofihjh"]}}}],"multi":false,"upsert":false},"planSummary":"IDHACK","keysExamined":1,"docsExamined":1,"nMatched":1,"nModified":0,"nUpserted":0,"numYields":0,"locks":{"ParallelBatchWriterMode":{"acquireCount":{"r":1}},"ReplicationStateTransition":{"acquireCount":{"w":1}},"Global":{"acquireCount":{"w":1}},"Database":{"acquireCount":{"w":1}},"Collection":{"acquireCount":{"w":1}},"Mutex":{"acquireCount":{"r":1}}},"flowControl":{"acquireCount":1,"timeAcquiringMicros":1},"storage":{},"remote":"127.0.0.1:63557","durationMillis":0}}
{"t":{"$date":"2022-04-30T19:43:07.596+03:00"},"s":"D2", "c":"REPL",     "id":22549,   "ctx":"conn56","msg":"Waiting for write concern. OpTime: {replOpTime}, write concern: {writeConcern}","attr":{"replOpTime":{"ts":{"$timestamp":{"t":0,"i":0}},"t":-1},"writeConcern":{"w":1,"wtimeout":0,"provenance":"implicitDefault"}}}
{"t":{"$date":"2022-04-30T19:43:07.596+03:00"},"s":"I",  "c":"COMMAND",  "id":51803,   "ctx":"conn56","msg":"Slow query","attr":{"type":"command","ns":"MYPROJECT.$cmd","command":{"update":"users","ordered":true,"lsid":{"id":{"$uuid":"92834e13-e3cc-4938-944a-2ce19f578b39"}},"$db":"MYPROJECT"},"numYields":0,"reslen":60,"locks":{"ParallelBatchWriterMode":{"acquireCount":{"r":1}},"ReplicationStateTransition":{"acquireCount":{"w":2}},"Global":{"acquireCount":{"r":1,"w":1}},"Database":{"acquireCount":{"w":1}},"Collection":{"acquireCount":{"w":1}},"Mutex":{"acquireCount":{"r":1}}},"flowControl":{"acquireCount":1,"timeAcquiringMicros":1},"storage":{},"remote":"127.0.0.1:63557","protocol":"op_msg","durationMillis":0}}
{"t":{"$date":"2022-04-30T19:43:07.596+03:00"},"s":"D2", "c":"QUERY",    "id":22783,   "ctx":"conn56","msg":"Received interrupt request for unknown op","attr":{"opId":23521,"knownOps":[]}}

We can clearly see the mathedCount at 1 and the modifiedCount at 0.

Any ideas ? I based myself on this doc : https://www.mongodb.com/docs/drivers/go/current/fundamentals/crud/write-operations/embedded-arrays/

Solution

The mistake I made came from the fact I was repeating the filter name. Instead of :

opt := options.FindOneAndUpdate().SetArrayFilters(
            options.ArrayFilters{
                Filters: []interface{}{
                    bson.M{
                        "elem": bson.M{
                            "elem.name": bson.M{
                                "$in": classesNames,
                            },
                        },
                    },
                },
            },
        )

I should use :

opt := options.FindOneAndUpdate().SetArrayFilters(
            options.ArrayFilters{
                Filters: []interface{}{
                    bson.M{
                        "elem.name": bson.M{
                             "$in": classesNames,
                        },
                    },
                },
            },
        )

Answered By – rrrrr

Answer Checked By – Candace Johnson (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.