[SERVER-37635] No obvious way to replace existing array with a single document through $project Created: 15/Oct/18  Updated: 08/Feb/23

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

Type: Improvement Priority: Major - P3
Reporter: Daniel Hegener Assignee: Backlog - Query Optimization
Resolution: Unresolved Votes: 0
Labels: mql-semantics
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Related
Assigned Teams:
Query Optimization
Participants:

 Description   

When using $project, replacing the contents of an existing array with a field will replace the entire array with that field. However, replacing the content of an existing array with a document will replace every element in the array with that document.

> db.collection.aggregate({$project: {_id:0, array:1}})
{ "array" : [ 1, 2, 3 ] }
> db.collection.aggregate({$project: {_id:0, array:"pie" }})
{ "array" : "pie" }
> db.collection.aggregate({$project: {_id:0, array:{snack:"pie"} }})
{ "array" : [ { "snack" : "pie" }, { "snack" : "pie" }, { "snack" : "pie" } ] }

Original Description

As per this post on SO: https://stackoverflow.com/a/52822987/6440033 and this playground:
https://mongoplayground.net/p/wdMj4ZQAHhc there is some unexpected behaviour when attempting to replace the contents of an array field using a nested $map operator.



 Comments   
Comment by Charlie Swanson [ 26/Oct/18 ]

asya can you check if we need to update our DOCS to help people figure this out?

Comment by Charlie Swanson [ 19/Oct/18 ]

Oh, I also now see based of the new title that replacing the array with a document should be possible with $literal:

>  db.collection.aggregate({$project: {_id: 0, array: {$literal: {snack: "pie"}}}}

In dnickless's example from stack overflow, it should be possible to achieve a similar thing by using a different syntax to communicate you wanted to replace the path with a document:

>db.collection.aggregate([
  {
    $project: {
      "abc": 1,
      "geometry": {
        $let: {
          vars: {
            ret: {
              "type": "LineString",
              "coordinates": {
                $map: {
                  "input": "$geometry",
                  "as": "this",
                  "in": [
                    "$$this.lng",
                    "$$this.lat"
                  ]
                }
              }
            }
          },
          in: "$$ret"
        }
      }
    }
  }
])

here's a new link with it working as I think was intended.

For context, I think the confusion stems from the fact that {$project: {a: {b: 1, c: 1}} is treated as the same thing as {$project: {"a.b": 1, "a.c": 1}}. The workaround here is to basically "trick" the parser away from that shortcut syntax to convey you mean to replace the entirety of "a", not just a sub-path of "a".

Comment by Charlie Swanson [ 19/Oct/18 ]

I see that this behavior is not intuitive, but it is indeed expected behavior today. As an example of where this makes sense, consider the following:

> db.collection.aggregate({$project: {_id:0, array:1}})
{ "array" : [ {snack: 2, x: 3}, {snack: 3, x: 4} ] }
> db.collection.aggregate({$project: {_id:0, array:{snack:"pie"} }})
{ "array" : [ { "snack" : "pie"}, { "snack" : "pie"} ] }

I might suggest turning this into a feature request to do something other than that if the path (such as "array.snack") does not exist within the array?

It's worth noting that this would be a backwards-breaking change.

Comment by Danny Hatcher (Inactive) [ 19/Oct/18 ]

Besides, I seem to be unable to edit my own tickets here. Is that expected?

Yes, that is expected.

Comment by Daniel Hegener [ 16/Oct/18 ]

Yes, indeed. That is the issue. I am using $project to redefine the field "geometry" into a subdocument with a string field and a nested array which should be based on the array previously stored in "geometry". However, the fact that "geometry" was an array before seems to result in the new "geometry" field turning into an array, too. Besides, I seem to be unable to edit my own tickets here. Is that expected?

Comment by Danny Hatcher (Inactive) [ 15/Oct/18 ]

To clarify, are you expecting to see one subdocument in the "geometry" array but instead you are seeing two? If that is not the issue, can you please elaborate in this ticket what the "unexpected result" is?

Comment by Daniel Hegener [ 15/Oct/18 ]

This is on MongoDB v3,6

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