简体   繁体   中英

How to group and sort in mongo db

I have a collection in mongodb with mongoose schema

var collection = new Schema({
  name : String,
  position : Number,
  category : String
});

collection.index({ category : 1, position : 1 }, { unique : true });

Now i want to fetch records in batches such that

it should group all the records on basis of category and then within each category(group), all records should be sorted on basis of position.

For example

i have following collection

[
  { name : 'Mr X', position : 34, category : 'U' },
  { name : 'Mr Y', position : 14, category : 'T' },
  { name : 'Mr G', position : 55, category : 'T' },
  { name : 'Mr I', position : 5, category : 'L' },
  { name : 'Mr D', position : 51, category : 'L' },
  { name : 'Mr P', position : 14, category : 'U' },
  { name : 'Mr P', position : 20, category : 'U' },
  { name : 'Mr E', position : 14, category : 'L' }
]

Now what should be the efficient query for this?

I am trying to fetch 3rd batch (each batch of 2 records)

db.ask.aggregate([{
            $group : {
                _id : "$category"
            }
        }, {
            $sort : {
                position : 1
            }
        }, {
            $skip : 4
        }, {
            $limit : 2
        }
    ])

And i am expecting output :

[
  { name : 'Mr G', position : 55, category : 'T' },
  { name : 'Mr P', position : 14, category : 'U' }
]

but actually getting none.

If fetching all records (without any batches), the expected output should be

[
  { name : 'Mr I', position : 5, category : 'L' },
  { name : 'Mr E', position : 14, category : 'L' }
  { name : 'Mr D', position : 51, category : 'L' },
  { name : 'Mr Y', position : 14, category : 'T' },
  { name : 'Mr G', position : 55, category : 'T' },
  { name : 'Mr P', position : 14, category : 'U' },
  { name : 'Mr P', position : 20, category : 'U' },
  { name : 'Mr X', position : 34, category : 'U' },
]

EDIT : I am looking for a query that is flexible to my changing requirements. what if i want to bring first category T then L then U.?

So my expected output (with limits and skip) was supposed to be

[
  { name : 'Mr I', position : 5, category : 'L' },
  { name : 'Mr P', position : 14, category : 'U' }
]

As far as I understand your requirements, you do not want to group at all but just want to sort by two fields and then do more or less some kind of paging.
That said, with the following aggregation pipeline I got your desired result.

db.ask.aggregate([
    { 
        $sort : {
            category : 1,
            position : 1 
        } 
    },
    {
        $project : {
            _id : 0,
            category : 1,
            position : 1,
            name : 1
        }
    },
    {  
        $skip : 4
    },
    {
        $limit : 2
    },
])

Well, you need to group on compound key to get desired result.

db.collection.aggregate([
 {$group : { _id : {category:"$category", position:"$position", name:"$name"}}}, 
 {$project:{_id:0, category:"$_id.category", name:"$_id.name", position:"$_id.position"}},
 {$sort : {"category" : 1, position:1} }, 
 {$skip : 4 }, 
 {$limit : 2 } 
])

This will print:

{ 
    "category" : "T", 
    "name" : "Mr G", 
    "position" : 55.0
}
{ 
    "category" : "U", 
    "name" : "Mr P", 
    "position" : 14.0
}

there is no need to group and other aggregation stages just sort on correct fields

we can have normal query

db.codofoode.find()
.sort({
    category : 1,
    position : 1
}).skip(4).limit(2)

or aggregate

db.codofoode.aggregate([
{
            $sort : {
              category:1,   position : 1
            }
        },

        {
            $skip : 4
        },
        {
            $limit : 2
        }])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM