680

I can't find anywhere it has been documented this. By default, the find() operation will get the records from beginning. How can I get the last N records in mongodb?

Edit: also I want the returned result ordered from less recent to most recent, not the reverse.

jww
  • 97,681
  • 90
  • 411
  • 885
Bin Chen
  • 61,507
  • 53
  • 142
  • 183

16 Answers16

947

If I understand your question, you need to sort in ascending order.

Assuming you have some id or date field called "x" you would do ...

.sort()


db.foo.find().sort({x:1});

The 1 will sort ascending (oldest to newest) and -1 will sort descending (newest to oldest.)

If you use the auto created _id field it has a date embedded in it ... so you can use that to order by ...

db.foo.find().sort({_id:1});

That will return back all your documents sorted from oldest to newest.

Natural Order


You can also use a Natural Order mentioned above ...

db.foo.find().sort({$natural:1});

Again, using 1 or -1 depending on the order you want.

Use .limit()


Lastly, it's good practice to add a limit when doing this sort of wide open query so you could do either ...

db.foo.find().sort({_id:1}).limit(50);

or

db.foo.find().sort({$natural:1}).limit(50);
Justin Jenkins
  • 26,590
  • 6
  • 68
  • 1,285
  • This approach does not work when I use skip and limit with it:Item.find().sort('date',-1).skip(start).limit(limit).run(cb); It should sort the rows and then skip the first N rows. but it skips the first N rows and then sorts the remained. – Morteza Milani Mar 06 '12 at 11:45
  • 14
    @MortezaM. I'm pretty sure you've got your query order mixed up ... your sort() should be run last, not first (much like a SQL ORDER BY) **.find({}).skip(1).limit(50).sort({"date":-1})** – Justin Jenkins Mar 06 '12 at 19:11
  • 8
    What ever it is, the order of calling functions should have nothing to do with the end result. – Morteza Milani Mar 09 '12 at 09:21
  • 8
    @MortezaM. Of course the order matters. `item = 3; item.add(3).divide(3) == 2; item.divide(3).add(3) == 4;` With no order what would be the outcome??? I agree with you that this reversed order is not intuitive. This is no SQL after all it should follow normal OO paradigms. – RickyA Feb 17 '13 at 14:25
  • 51
    and yet the order DOES NOT matter. All you have to do is try it to see that it does not. all of these are called on the cursor and passed to the server so the server limits the results of the sort (top N) as anything else wouldn't make sense. – Asya Kamsky May 12 '13 at 15:42
  • 14
    Docs confirm that the order doesn't matter: [link](http://docs.mongodb.org/manual/core/read/#combine-cursor-methods) – JohnnyHK Jul 07 '13 at 04:08
  • Doesn't MongoDB save the document in the inserted order? (Object id ascending)? – popcoder Jul 07 '13 at 10:53
  • 4
    Once you do ``db.foo.find().sort({$natural:-1}).limit(50);``, you get the last 50 entries in a descending way. How do we optimally change the order to ascending? – rtnpro Oct 14 '13 at 10:32
  • maybe add manual `.to_a.sort_by { |doc| doc.id }` after that? Or use moped functions directly and reorder it. – Alexander Ulitin Nov 07 '13 at 19:49
  • 2
    @Justin Jenkins - this is terrible in terms of performance when you have a table with LOOOOOOTS of records. – Joezer Jan 11 '15 at 15:35
  • 1
    @Maimonides what does "LOOOOOOTS" mean? I've got _collections_ with 100s of millions of _documents_, but perhaps that's not lots to you. As a rule of thumb once you are talking about querying documents at any sort of scale you should be creating indexes on the key you wish you query on. :) – Justin Jenkins Jan 15 '15 at 08:22
  • @Justin Jenkins If it works well for 100s of millions it has to use an indexed field for: "Assuming you have some id or date field called "x" you would do...", but I didn't see you mention it. Did you use an indexed field there? – Joezer Jan 15 '15 at 16:27
  • 1
    @JustinJenkins the order of skip/limit/sort does not matter: "the order in which you chain the limit() and the sort() methods is not significant" ["Combine Cursor Methods"](http://docs.mongodb.org/manual/reference/method/db.collection.find/#combine-cursor-methods). Any chained methods on the Cursor object modify the cursor before any records are received; MongoDB will apply the correct ordering of the skip/limit/sort parameters intuitively. – zamnuts Aug 18 '15 at 17:16
  • 5
    this should not be accepted as the answer as it doesn't actaully do what the OP asked specifically: 'get the last N records' and ''result ordered from less recent to most recent, not the reverse'. This solution will get read N records from the tail, but it will lose it's ascending order due to the initial ordering operation which means you will need to re-order the results back to natural : 1 order – AaronHS Aug 27 '15 at 08:01
  • 3
    Natural order should _not_ be relied upon; if you are using a replica set (and you should be), different nodes might well have the same documents stored in a different order on disk. – Vince Bowdren Feb 10 '17 at 16:46
  • 2
    date part in auto created _id is one part of _id and it can not be used for 100% accuracy to sort by date. It works only if you never ever have more than 1 write/second https://docs.mongodb.com/manual/reference/method/ObjectId/ – Lukas Liesis Feb 15 '17 at 10:20
  • Please include @João Otero 's answer which contemplates performance so this answer can be really complete – zardilior May 26 '19 at 18:08
  • "If you use the auto created _id field it has a date embedded in it", I came here looking for silver and instead found gold. Thank you. – jcodes Mar 09 '22 at 17:07
163

The last N added records, from less recent to most recent, can be seen with this query:

db.collection.find().skip(db.collection.count() - N)

If you want them in the reverse order:

db.collection.find().sort({ $natural: -1 }).limit(N)

If you install Mongo-Hacker you can also use:

db.collection.find().reverse().limit(N)

If you get tired of writing these commands all the time you can create custom functions in your ~/.mongorc.js. E.g.

function last(N) {
    return db.collection.find().skip(db.collection.count() - N);
}

then from a mongo shell just type last(N)

Giacomo1968
  • 25,759
  • 11
  • 71
  • 103
Trasplazio Garzuglio
  • 3,535
  • 2
  • 25
  • 25
  • 2
    `db.collection.find().reverse().limit(1)` gives me the error `... has no method reverse` – Catfish May 09 '14 at 18:48
  • @Catfish you are right, I just noticed that reverse() was added by [Mongo-Hacker ](http://tylerbrock.github.com/mongo-hacker), I'll update my answer. Thanks. – Trasplazio Garzuglio May 09 '14 at 19:27
  • 1
    db.getCollection('COLLECTION_NAME').find().skip(db.getCollection('COLLECTION_NAME').count()-N) working great for me :) – Spl2nky Jul 24 '16 at 17:01
  • This should be the answer to the question, and not the answer by Justin Jenkins. – Jadiel de Armas Sep 05 '16 at 20:22
  • 2
    Natural order should _not_ be relied upon; if you are using a replica set (and you should be), different nodes might well have the same documents stored in a different order on disk. – Vince Bowdren Feb 10 '17 at 16:46
  • 1
    Is it possible to do this in an aggregate search where you find the last N values of multiple documents in that are aggregated? – Kelvin U Oct 08 '17 at 02:08
  • Right on! +1 thank you so much this clears up the answer much better than the first question – Josh May 05 '20 at 16:49
  • @KelvinU did you find any way, to find solution for - "Is it possible to do this in an aggregate search where you find the last N values of multiple documents in that are aggregated?" – Aman Ghanghoriya Feb 17 '23 at 07:24
22

In order to get last N records you can execute below query:

db.yourcollectionname.find({$query: {}, $orderby: {$natural : -1}}).limit(yournumber)

if you want only one last record:

db.yourcollectionname.findOne({$query: {}, $orderby: {$natural : -1}})

Note: In place of $natural you can use one of the columns from your collection.

turivishal
  • 34,368
  • 7
  • 36
  • 59
Ashwini
  • 707
  • 8
  • 5
  • this works for me db.yourcollectionname.findOne({$query:{}, $orderby : {$natural : -1}}). I think last parathensis is missing in the answer – Ajay Aug 15 '15 at 13:07
  • 1
    Natural order should _not_ be relied upon; if you are using a replica set (and you should be), different nodes might well have the same documents stored in a different order on disk. – Vince Bowdren Feb 10 '17 at 16:46
22

Sorting, skipping and so on can be pretty slow depending on the size of your collection.

A better performance would be achieved if you have your collection indexed by some criteria; and then you could use min() cursor:

First, index your collection with db.collectionName.setIndex( yourIndex ) You can use ascending or descending order, which is cool, because you want always the "N last items"... so if you index by descending order it is the same as getting the "first N items".

Then you find the first item of your collection and use its index field values as the min criteria in a search like:

db.collectionName.find().min(minCriteria).hint(yourIndex).limit(N)

Here's the reference for min() cursor: https://docs.mongodb.com/manual/reference/method/cursor.min/

João Otero
  • 948
  • 1
  • 15
  • 30
16
 db.collection.find().sort({$natural: -1 }).limit(5)
satywan kumar
  • 207
  • 2
  • 3
13

@bin-chen,

You can use an aggregation for the latest n entries of a subset of documents in a collection. Here's a simplified example without grouping (which you would be doing between stages 4 and 5 in this case).

This returns the latest 20 entries (based on a field called "timestamp"), sorted ascending. It then projects each documents _id, timestamp and whatever_field_you_want_to_show into the results.

var pipeline = [
        {
            "$match": { //stage 1: filter out a subset
                "first_field": "needs to have this value",
                "second_field": "needs to be this"
            }
        },
        {
            "$sort": { //stage 2: sort the remainder last-first
                "timestamp": -1
            }
        },
        {
            "$limit": 20 //stage 3: keep only 20 of the descending order subset
        },
        {
            "$sort": {
                "rt": 1 //stage 4: sort back to ascending order
            }
        },
        {
            "$project": { //stage 5: add any fields you want to show in your results
                "_id": 1,
                "timestamp" : 1,
                "whatever_field_you_want_to_show": 1
            }
        }
    ]

yourcollection.aggregate(pipeline, function resultCallBack(err, result) {
  // account for (err)
  // do something with (result)
}

so, result would look something like:

{ 
    "_id" : ObjectId("5ac5b878a1deg18asdafb060"),
    "timestamp" : "2018-04-05T05:47:37.045Z",
    "whatever_field_you_want_to_show" : -3.46000003814697
}
{ 
    "_id" : ObjectId("5ac5b878a1de1adsweafb05f"),
    "timestamp" : "2018-04-05T05:47:38.187Z",
    "whatever_field_you_want_to_show" : -4.13000011444092
}

Hope this helps.

lauri108
  • 1,381
  • 1
  • 13
  • 22
  • 1
    THANK YOU @lauri108! Of all the answers to this and related questions, this is THE ONLY working and reliable solution to "how to get the LAST N DOCS". And simple enough to do in one query. Job done. – randomsock Apr 25 '18 at 11:34
12

You can try this method:

Get the total number of records in the collection with

db.dbcollection.count() 

Then use skip:

db.dbcollection.find().skip(db.dbcollection.count() - 1).pretty()
csg
  • 8,096
  • 3
  • 14
  • 38
bello hargbola
  • 435
  • 6
  • 9
11

You can't "skip" based on the size of the collection, because it will not take the query conditions into account.

The correct solution is to sort from the desired end-point, limit the size of the result set, then adjust the order of the results if necessary.

Here is an example, based on real-world code.

var query = collection.find( { conditions } ).sort({$natural : -1}).limit(N);

query.exec(function(err, results) {
    if (err) { 
    }
    else if (results.length == 0) {
    }
    else {
        results.reverse(); // put the results into the desired order
        results.forEach(function(result) {
            // do something with each result
        });
    }
});
Marty Hirsch
  • 661
  • 6
  • 7
  • Nice workaround! Would be nice to be able to do the same at the query level though. Something like `var query = collection.find( { conditions } ).sort({$natural : -1}).reverse().limit(N)`. – inwpitrust Jan 24 '15 at 20:52
  • how can I use the same in springboot? – Bug May 10 '22 at 20:23
8

you can use sort() , limit() ,skip() to get last N record start from any skipped value

db.collections.find().sort(key:value).limit(int value).skip(some int value);
Praveen
  • 55,303
  • 33
  • 133
  • 164
pkp
  • 300
  • 3
  • 7
7

Look under Querying: Sorting and Natural Order, http://www.mongodb.org/display/DOCS/Sorting+and+Natural+Order as well as sort() under Cursor Methods http://www.mongodb.org/display/DOCS/Advanced+Queries

Steve Wilhelm
  • 6,200
  • 2
  • 32
  • 36
  • 1
    Thanks for your anwser, it is close,but I want to retured records ordered from less recent to most recent, is it possible? – Bin Chen Dec 12 '10 at 10:43
  • Natural order should _not_ be relied upon; if you are using a replica set (and you should be), different nodes might well have the same documents stored in a different order on disk. – Vince Bowdren Feb 10 '17 at 16:46
  • If you want to get the most recent records, you will have to rely on a date field in the document. – Vince Bowdren Feb 10 '17 at 16:47
3

You may want to be using the find options : http://docs.meteor.com/api/collections.html#Mongo-Collection-find

db.collection.find({}, {sort: {createdAt: -1}, skip:2, limit: 18}).fetch();
Lucbug
  • 475
  • 6
  • 16
3

Use .sort() and .limit() for that

Use Sort in ascending or descending order and then use limit

db.collection.find({}).sort({ any_field: -1 }).limit(last_n_records);
Sahil Thummar
  • 1,926
  • 16
  • 16
2

If you use MongoDB compass, you can use sort filed to filter,

enter image description here

Ishan Liyanage
  • 2,237
  • 1
  • 26
  • 25
1
db.collection.find().hint( { $natural : -1 } ).sort(field: 1/-1).limit(n)

according to mongoDB Documentation:

You can specify { $natural : 1 } to force the query to perform a forwards collection scan.

You can also specify { $natural : -1 } to force the query to perform a reverse collection scan.

Community
  • 1
  • 1
Gili.il
  • 11
  • 2
1

use $slice operator to limit array elements

GeoLocation.find({},{name: 1, geolocation:{$slice: -5}})
    .then((result) => {
      res.json(result);
    })
    .catch((err) => {
      res.status(500).json({ success: false, msg: `Something went wrong. ${err}` });
});

where geolocation is array of data, from that we get last 5 record.

turivishal
  • 34,368
  • 7
  • 36
  • 59
KARTHIKEYAN.A
  • 18,210
  • 6
  • 124
  • 133
-6

Last function should be sort, not limit.

Example:

db.testcollection.find().limit(3).sort({timestamp:-1}); 
Asclepius
  • 57,944
  • 17
  • 167
  • 143
Ramesh Kasi
  • 131
  • 2
  • 9