[SERVER-59713] `$unwind` doesn't work on array subdocument fields Created: 01/Sep/21  Updated: 17/Jan/24

Status: Investigating
Project: Core Server
Component/s: None
Affects Version/s: 4.4.2
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Amit Beckenstein Assignee: Katya Kamenieva
Resolution: Unresolved Votes: 1
Labels: $unwind, aggregation-framework
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Depends
is depended on by MONGOID-5556 #tally should support splatting array... Closed
Duplicate
is duplicated by SERVER-73888 Aggregation Pipeline: $unwind always ... Closed
Operating System: ALL
Steps To Reproduce:
  1. db.collection.insertOne({

  items: [

    {

      _id: ObjectId,

      name: 'foobar'

{{    }}}

  ]

});

  1. db.collection.aggregate([
      {
        $unwind: }}{{'$items.name'
    {{  }}}
    ]);{{}}

Outputs nothing

Participants:

 Description   

Given the following document:

db.collection.insertOne({

  items: [

    {

      _id: ObjectId,

      name: 'foobar'

{{    }}}

  ]

});

Trying to `$unwind` the field path `$items.name` outputs nothing.

On the other hand, the output of this:

db.collection.aggregate([
  {
    $project: {
{{      type: { $type: '$items.name' }}}
{{    }}}
{{  }}}
]);

is the following:

{{{}}
  "_id" : ObjectId("..."),
  "type" : "array"
}

If the expression `$items.name` resolves to an array then why can I not `$unwind` this field path?



 Comments   
Comment by Johnny Shields [ 17/Jan/24 ]

kateryna.kamenieva@mongodb.com christopher.harris@mongodb.com may we get an update on this ticket? It's been in "Investigating" status for quite sometime.

Please refer to SERVER-73888 if you need additional help reproducing it.

Comment by William Tan (Inactive) [ 12/Apr/23 ]

Changing the type of this ticket to "Bug" upon customer request.

Comment by Amit Beckenstein [ 10/Feb/23 ]

@Johnny Shields haha I know that I can use `$project`, in my actual implementation I was using `$addFields` intentionally.

Comment by Johnny Shields [ 10/Feb/23 ]

@christopher.harris@mongodb.com >
When fixing this ticket, please also verify that the behavior of the $unwind "preserveNullAndEmptyArrays" parameter true/false works correctly with subdocument fields.

Comment by Johnny Shields [ 10/Feb/23 ]

FYI, you can use a $project instead of $addFields in order to avoid modifying your documents.

{ "$project": { "p" => "$addresses.city" } }
{ "$unwind": "$p" }
{ "$group": { _id: "$p", counts: { "$sum": 1 } } }

But this still should be fixed.

Comment by Amit Beckenstein [ 07/Sep/22 ]

@christopher.harris@mongodb.com Any update? It's been almost a year

Comment by Amit Beckenstein [ 22/Sep/21 ]

Hi @christopher.harris,

The system might very well be working as designed currently. I'm not sure if this is a bug or a feature request, but I'll do my best to explain my use case for support of such functionality.

First, to answer your request: the output I would expect when issuing an $unwind on "$arrayField.stringValue" (which is the equivalent to "$items.name" in my example, yes) is the same output which I would expect when issuing an $unwind on "$arrayField" itself.

An excerpt of my use case is such: 

let parameterFieldPath = '$items.name';
 
db.collection.aggregate([
  { $unwind: parameterFieldPath },
  {
    $group: {
      _id: parameterFieldPath,
      items: { $push: '$$ROOT' }
  }
]);

As seen above, I have to $unwind an array whose field path is specified in parameterFieldPath (which is "$items.name" in our example), and then $group the output documents by the same parameterFieldPath. parameterFieldPath is variable, and its value can be changed according to a user's settings.
Currently, this doesn't work, and the only workaround is doing something like this:

let parameterFieldPath = '$items.name';
 
db.collection.aggregate([
  {
    // TODO: Remove stage once using version in which https://jira.mongodb.org/browse/SERVER-59713 is fixed
    $addFields: {
      parameterValue: parameterFieldPath
    }
  },
  { $unwind: '$parameterValue' },
  {
    $group: {
      _id: '$parameterValue',
      items: { $push: '$$ROOT' }
  }
]);

But this leaves me with an unwanted temporary parameterValue field on each document. BTW the TODO is taken from our code

I hope my use case was clear enough.
As I've said before, I'm not sure whether this is a bug or a feature request - feel free to change the issue type accordingly.

Thanks,
Amit

 

Comment by Chris Harris [ 20/Sep/21 ]

Hi amitbeck@gmail.com,

Based on our reading of the ticket description, we believe that the system may be working as designed currently.  However, can you please give an example of what output you would expect when issuing an $unwind on "$arrayField.stringValue" ( "$items.name" in your example)?  Based on the workaround you noted, it seems you are looking to create documents that each contain the value of the name field that is embedded in one of the subdocuments in the items array?

The $unwind operator intends to have a field name that resolves to an array as its argument. This is why, as you've observed, the stage { $unwind: "$temp" } outputs results. In that situation, $temp now represents an array of string values that was generated from the original array of subdocuments.  You could similarly choose to $unwind the array of subdocuments ( $items ) first and then rename or remove fields afterwards based on the desired logic.  Furthermore, it is also possible to use or reference the individual values via the "$items.name" syntax in later aggregation stages after the "items" array has been unwound.  How are you intending to use this field later in the aggregation pipeline?

Similar confusion has come up before.  The fact that the projection using the $type operator reported the field path as an array has to do with the fact that the $type aggregation operator does not examine array elements.  Depending on the feedback you provide to the questions above, we may wish to convert this ticket into an improvement request.

Best,
Chris

Comment by Kelsey Schubert [ 13/Sep/21 ]

Thanks for the report, passing this along to the query team to investigate. Please continue to watch this ticket for updates.

Comment by Amit Beckenstein [ 01/Sep/21 ]

The workaround for now is something of the sorts:

 

db.collection.aggregate([

  {

    $addFields:

      temp: '$items.name'

  },

  {

    $unwind: '$temp'

      }

]);

Generated at Thu Feb 08 05:47:56 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.