[SERVER-25200] $project computed fields on arrays sets every element of the array -- inconsistent with $set Created: 21/Jul/16  Updated: 06/Dec/22  Resolved: 05/Jul/19

Status: Closed
Project: Core Server
Component/s: Aggregation Framework
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Carly Robison Assignee: Backlog - Query Team (Inactive)
Resolution: Won't Do Votes: 0
Labels: query-44-grooming
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
related to SERVER-30812 When using an array element as the lo... Closed
is related to SERVER-5781 Implement $addFields aggregation stag... Closed
Assigned Teams:
Query
Operating System: ALL
Steps To Reproduce:

> db.test.insert({_id: 0, a: [1, 3, 5]})
> db.test.aggregate({$project: {"a.0": {$literal: 10}}})

Participants:

 Description   

> db.test.insert({_id: 0, a: [1, 3, 5]})
> db.test.aggregate({$project: {"a.0": {$literal: 10}}})
{ "_id" : 0, "a" : [ { "0" : 10 }, { "0" : 10 }, { "0" : 10 } ] }

This behavior replaces every element of the array, when what $set does is more elegant:

> db.test.update({a: [1, 3, 5]}, {$set: {"a.0": 10}})
> db.test.find()
{ "_id" : 0, "a" : [ 10, 3, 5 ] }

However, $set errors when "a" is not an array.

This behavior occurs in both the $project stage and the new $addFields stage.



 Comments   
Comment by David Storch [ 05/Jul/19 ]

This ticket is suggesting a breaking change, since it would require changing the meaning of an existing projection. We do not plan on pursuing this breaking change, so I'm closing the ticket as "Won't Do". We may wish to achieve a similar effect in the future by extending the projection language to support something like {$project: {"a.$[0]": 42}}, where "$[0]" is special syntax to refer to an index of an array.

Comment by Asya Kamsky [ 25/Jul/17 ]

To set a single field in existing array at position X is currently somewhat complex in aggregation projection, but it's not clear how common this use case is.

> X = 0;
> db.test.insert({_id: 0, a: [1, 3, 5]})
> db.test.aggregate({$project: {"a": {$map:{
      input:{$range:[0, {$size:"$a"}]},
      in: {$cond:{
             if:{$eq:[X, "$$this"]},
             then: {$literal:10},
             else:{$arrayElemAt:["$a", "$$this"]}
      } }
} } } })
{ "_id" : 0, "a" : [ 10, 3, 5 ] }

Generated at Thu Feb 08 04:08:32 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.