[SERVER-29846] new field name changes expression result for array in $project Created: 24/Jun/17  Updated: 27/Oct/23  Resolved: 27/Jun/17

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

Type: Bug Priority: Major - P3
Reporter: Asya Kamsky Assignee: Asya Kamsky
Resolution: Works as Designed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Backwards Compatibility: Fully Compatible
Operating System: ALL
Participants:

 Description   

Or another way of stating the issue is, I cannot change array field to non-array field if I want to use any of its contents in the expression (without changing its name).

db.coll.insert({a:[1,2]})

I can change it to be embedded inside another field like this:

db.coll.aggregate({$project:{b:{a:"$a"},_id:0}})
{ "b" : { "a" : [ 1, 2 ] } }

However, if I need the top level name of new field is to be "a" then there seems to be no way to avoid projection happening for every element of original array:

// I want to get back {"a" : {"b" : [1, 2]} }
db.coll.aggregate({$project:{a:{b:"$a"},_id:0}})
{ "a" : [ { "b" : [ 1, 2 ] }, { "b" : [ 1, 2 ] } ] }

I haven't found a way to do this except by having two $projects, one to create newA and the second to rename newA to a. I understanding adding fields to existing arrays of objects requires preserving cardinality (and type) of array field, but this seems to be a full replace.

Looked for existing ticket related to this, SERVER-25200 is possibly the closest (though it mentions dotted notation, the behavior is similar).



 Comments   
Comment by Asya Kamsky [ 27/Jun/17 ]

The challenge appears to be the fact that "a.b" for an existing field has to take into account the existing structure of "a" - if "a" is an array then field "b" needs to be set in every element of array "a". If "a" is scalar then "b" replaces the scalar field.

The scenario I wanted in this case is possible to achieve by using a new field name (and then renaming via an extra $project or $addFields stage if necessary) but using $replaceRoot may be more readable, and it would have the advantage of being a single stage.

For completeness here is how to achieve the result I expected in the original $project via $replaceRoot:

db.coll.aggregate({$replaceRoot:{newRoot:{a:{b:"$a"}}}})
{ "a" : { "b" : [ 1, 2 ] } }

To get the effect of $addFields stage to set the new "a" in place of existing field "a" while retaining the existing fields, $mergeObjects (new in 3.5) is needed:

// $addFields takes type of "a" into account
db.coll.aggregate({$addFields:{a:{b:"$a"}}})
{ "_id" : ObjectId("594e8917dc53e7205b7b1604"), "a" : [ { "b" : [ 1, 2 ] }, { "b" : [ 1, 2 ] } ] }
// but I want to add field {"a":{"b":[1,2]}}
db.coll.aggregate({$replaceRoot:{newRoot:{$mergeObjects:["$$ROOT", {a:{b:"$a"}}]}}})
{ "_id" : ObjectId("594e8917dc53e7205b7b1604"), "a" : { "b" : [ 1, 2 ] } }

Comment by Charlie Swanson [ 26/Jun/17 ]

asya we parse these two to the same thing to ensure that "a.b" and a: {b: ...} always mean the same thing:

{$project: {a: {b: "$a"}}}
{$project: {"a.b": "$a"}}

Are you trying to say that the first one seems more like a replacement, and the second more like a traversal? I can see that point, but this would definitely be a backwards breaking change. I of course wasn't around for the initial design of $project, but I was under the impression that we invented this syntax so that something like

{$project: {
  "longFieldName.x": 1,
  "longFieldName.y": 1,
  "longFieldName.z": 1
}}

could be condensed into something like this:

{$project: {
  longFieldName: {
    x: 1,
    y: 1,
    z: 1
  }
}}

haven't found a way to do this except by having two $projects, one to create newA and the second to rename newA to a. I understanding adding fields to existing arrays of objects requires preserving cardinality (and type) of array field, but this seems to be a full replace.

Have you tried a $replaceRoot?

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