How to update an sub array with map operation by index in array?

I have a document like that:

{
	"_id": "61e7e78372d3221d2c5fb242",
	"name": "First Task",
	"desc": "This is description",
	"status": "completed",
	"done": 100,
	"level": "medium",
	"company": "BBB",
	"project": "AAA",
	"from_date": "2022/01/01 00:00:00",
	"to_date": "2022/01/01 00:00:00",
	"todos": [
		{
			"name": "sub1",
			"desc": "desc1",
			"active": true,
			"owner": "61e6125db0f102060951aa53"
		},
		{
			"name": "sub2",
			"desc": "desc2",
			"active": true,
			"owner": "61e6125db0f102060951aa53"
		}
	],
	"owner": "61e6125db0f102060951aa53"
}

I want to update “todos” array with rule: update sub item by index like “todos.0.active”: false.
When I try it every sub item are updated like:

"todos": [
		{
			"0": {
				"active": false
			},
			"name": "sub1",
			"desc": "desc1",
			"active": true,
			"owner": "61e6125db0f102060951aa53"
		},
		{
			"0": {
				"active": false
			},
			"name": "sub2",
			"desc": "desc2",
			"active": true,
			"owner": "61e6125db0f102060951aa53"
		}
	]

0 doesn’t mean index (0, 1, 2…), it means key in every items. Im only need to update active to false with index in [0,1,…].
I think about map operator like javascript but I dont know how to do. My full query is:

db.ecom.updateOne(
	{_id: ObjectId('61e7e78372d3221d2c5fb242')},
	[
		{$set: {"todos.0.active": false}}
	]
)

and:

db.ecom.updateOne(
	{_id: ObjectId('61e7e78372d3221d2c5fb242')},
	[
		{$set: {"todos.$[0].active": false}} // error with $
	]
)

I want to code like:

db.ecom.updateOne(
	{_id: ObjectId('61e7e78372d3221d2c5fb242')},
	[
		{$set: {"todos": {
			"$map": function(item, index) {
				if ([0, 1].inArray(index)) {
					item.active = false;
				}
				return item;
			}
		}}}
	]
)

Hi @MAY_CHEAPER,

I’m not very advanced so this is more an idea… there should be a built in way to do this. Anyways, this is the query, you should test with update before doing it in the actual collection:

db.collection.aggregate([
  {
    "$addFields": {
      "todos": {
        "$function": {
          "body": function(todos){
       
          const el1 = todos[1];
       
          if(el1.active===false){ todos[1].active=true }
          
          return todos
         },
        "args": ["$todos"],
        "lang": "js"
          }
        }
      }
    }
])

May not be a good prettify as it is from ffox browser console.

Basically, it adds the field “todos” by reading the current $todos and setting an element with index 1 to a particular value. Feel free to extend this function, for example, you could set up an array of indexes and then iterate, etc.

1 Like

@santimir it’s amazing when I can code like this, thank you so much.

1 Like