Home > Back-end >  How to fix $sort is not working for array inside array in mongoDB?
How to fix $sort is not working for array inside array in mongoDB?

Time:03-04

My document is as follows :

{
    "_id" : ObjectId("622136811b68d9136e48ba4e"),
    "brand_name" : "iPhone",
    "brand_rating" : [
        {
            "cust_name" : "Sham K",
            "rating" : 5
        },
        {
            "cust_name" : "Nil",
            "rating" : 5
        }
    ],
    "models" : [
        {
            "model_name" : "iPhone 7Plus",
            "RAM" : "4GB",
            "ROM" : "64GB",
            "price" : 98000,
            "buyer" : [
                {
                    "cust_name" : "Anu",
                    "rating" : 3
                },
                {
                    "cust_name" : "Kiran",
                    "rating" : 4
                }
            ]
        },
        {
            "model_name" : "iPhone 2",
            "RAM" : "3GB",
            "ROM" : "32GB",
            "price" : 58000,
            "buyer" : [
                {
                    "cust_name" : "Kiran",
                    "rating" : 4
                }
            ]
        }
    ]
}

Question is List all the customers in descending order who bought the iPhone 7plus.

I try this but sorting is not working

db.brand.aggregate({$unwind : "$models"},{$match : {"models.model_name" :"iPhone 7Plus" }}, {$project : {_id : 0, "models.buyer.cust_name" : 1}}, {$sort : {"models.buyer.cust_name" : -1} })

Output :

    { "models" : { "buyer" : [ { "cust_name" : "Anu" }, { "cust_name" : "Kiran" } ] } }

> [5]

What is the easiest way to solve it?

CodePudding user response:

How about $unwind before $sort

db.collection.aggregate({
  $unwind: "$models"
},
{
  $match: {
    "models.model_name": "iPhone 7Plus"
  }
},
{
  $project: {
    _id: 0,
    "models.buyer.cust_name": 1
  }
},
{
  $unwind: "$models.buyer"
},
{
  $sort: {
    "models.buyer.cust_name": -1
  }
})

mongoplayground

CodePudding user response:

Here is a variation with only 3 stages and the single $unwind is pushed "down" the pipeline as far as possible to minimize both the number of docs and size thereof. The key thing in this variation is using $filter followed by a dot notation expression to get to the data we want without using $unwind too early.

db.foo.aggregate([
    {$project: {
        _id:false,
        cust_name:
        // Explaining this "inside out":                                             
        // 1.  Start by filtering the models array for iphone 7plus.              
        //     This will yield an array of 1 item (or zero).                      
        // 2.  We want to get the cust_name from buyer array.  Use                
        //     $map on the array of 1 to extract the names using the              
        //     dot notation trick buyer.cust_name to create an array              
        //     of just names.                                                     
        // 3.  We have that list -- but it is a list inside the array             
        //     of one, e.g. [ [ name1, name2 ] ] .  So we lift out                
        //     the inner array with $arrayElemAt: [ inner, 0 ]:                   
            {$arrayElemAt: [
                {$map: {
                    input: {$filter: {
                      input: "$models",
                      cond: {$eq:['$$this.model_name','iPhone 7Plus']}
                    }},
                    in: "$$this.buyer.cust_name"
                }}, 0]
            }
       }}

    // At this stage we have:                                                     
    // { "cust_name" : [ "Anu", "Kiran" ] }                                       
    // Now it's easy:                                                             
    ,{$unwind: '$cust_name'}
    ,{$sort: {'cust_name':-1}}
]);
  • Related