Update array objects by condition

Hello,
I’m want to update the status of the objects (in my document it’s scanUuid) in array (in my document it’s urls) which are equal to other input array (in my code it’s scanUuids).
In other words, I’d like to emulate the SQL query of WHERE x IN (a1, a2, a3).

My document:

{
  "_id" : ObjectId("61fa721407b98252591852fd"),
  "ip" : "192.168.109.71",
  "urls" : [
    {
      "scanUuid" : "fdec656b-071d-4a5a-9fd0-1a4258313a7d",
      "url" : "http://192.168.109.81/",
    },
    {
      "scanUuid" : "b922a852-2b69-470e-b5a0-80b7d01b4c80",
      "url" : "http://hard.altoromutual.com/",
    }
  ]
}

My GOlang code:

...
	arrFilters, updateSet := bson.M{}, bson.M{}
	for i, scanUuid := range scanUuids {
		idx := fmt.Sprintf("x%s", strconv.Itoa(i))
		arrFilters[fmt.Sprintf("%s.scanUuid", idx)] = scanUuid
		updateSet[fmt.Sprintf("urls.$[%s].status", idx)] = status
	}
	
	arrayFilters := options.ArrayFilters{Filters: bson.A{arrFilters}}
	upsert := true
	opts := options.UpdateOptions{
		ArrayFilters: &arrayFilters,
		Upsert:       &upsert,
	}
	
	update := bson.M{
		"$set": updateSet,
	}

	objID, err := r.createMongoObjId(scanId)
	if err != nil {
		return "", err
	}
	
	filter := r.createMongoObjIdFilter(objID)

	updateResult, err := collection.UpdateOne(ctx, filter, update, &opts)
	if err != nil {
		return "", err
	}
...	

I’m experiencing different errors, such as:

 write exception: write errors: [Error parsing array filter :: caused by :: Expected a single top-level field name, found 'x0' and 'x1']

Thanks in advance,
Boris

Hello @Bora, welcome to the MongoDB Community forum!

I looked into your code and it is difficult to tell what the issue is - some data used in the query is not there for me to figure the problem. But, I have the below update operation which works in the mongosh or mongo shell. Let me know if this works. Note that the update uses a Aggregation Pipeline (this feature is Updates with Aggregation Pipeline).

var scanUuids = [ "fdec656b-071d-4a5a-9fd0-1a4258313a7d", "b922a852-2b69-470e-b5a0-80b7d01b4c80", "..." ]
var statusToUpdate = "some_value"

db.collection.updateOne(
{ _id : ObjectId("61fa721407b98252591852fd") },
[
  { $set: {
      urls: {
          $map: {
              input: "$urls",
              as: "url_doc",
              in: {
                  $cond: [
                      { $in: [ "$$url_doc.scanUuid", scanUuids ] },
                      { $mergeObjects: [ "$$url_doc", { status: statusToUpdate } ] },
                      "$$url_doc"
                  ]
              }
          }
      }
  }}
]
)

It seems like each element in an “arrayFilter” can only have a single top-level field name (see a similar error message in this StackOverflow question). Try updating your code to use one map key per array element.

For example:

...
	arrFilters := bson.A{}
	updateSet := bson.M{}
	for i, scanUuid := range scanUuids {
		idx := fmt.Sprintf("x%s", strconv.Itoa(i))
		arrFilters = append(arrFilters, bson.M{
			fmt.Sprintf("%s.scanUuid", idx): scanUuid,
		})
		updateSet[fmt.Sprintf("urls.$[%s].status", idx)] = status
	}

	arrayFilters := options.ArrayFilters{Filters: arrFilters}
	upsert := true
	opts := options.UpdateOptions{
		ArrayFilters: &arrayFilters,
		Upsert:       &upsert,
	}
...