Wie bekomme ich die letzten N Datensätze in Mongodb?

523

Ich kann nirgendwo finden, dass dies dokumentiert wurde. Standardmäßig werden bei der Operation find () die Datensätze von Anfang an abgerufen. Wie kann ich die letzten N Datensätze in Mongodb bekommen?

Bearbeiten: Ich möchte auch, dass das zurückgegebene Ergebnis von weniger aktuell bis zuletzt sortiert wird, nicht umgekehrt.

Bin Chen
quelle
7
@Haim, bitte geben Sie genau an, welcher Teil der Webseite meine Frage löst.
Bin Chen
Hallo @BinChen, ich habe das gleiche Problem in letzter Zeit, ist es gelöst?
Hanton

Antworten:

736

Wenn ich Ihre Frage verstehe, müssen Sie in aufsteigender Reihenfolge sortieren.

Angenommen, Sie haben ein ID- oder Datumsfeld mit dem Namen "x", würden Sie ...

.Sortieren()


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

Die 1 sortiert aufsteigend (älteste bis neueste) und -1 sortiert absteigend (neueste bis älteste).

Wenn Sie das automatisch erstellte Feld _id verwenden, ist ein Datum darin eingebettet. Sie können dieses also verwenden, um nach ... zu bestellen.

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

Dadurch werden alle Ihre Dokumente vom ältesten zum neuesten sortiert zurückgegeben.

Natürliche Reihenfolge


Sie können auch eine oben erwähnte natürliche Ordnung verwenden ...

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

Verwenden Sie wieder 1 oder -1, je nach gewünschter Reihenfolge.

Verwenden Sie .limit ()


Schließlich empfiehlt es sich, bei dieser Art von weit offener Abfrage ein Limit hinzuzufügen, damit Sie entweder ...

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

oder

db.foo.find().sort({$natural:1}).limit(50);
Justin Jenkins
quelle
11
@ MortezaM. Ich bin mir ziemlich sicher, dass Sie Ihre Abfragereihenfolge durcheinander gebracht haben ... Ihre sort () sollte zuletzt und nicht zuerst ausgeführt werden (ähnlich wie bei einer SQL ORDER BY) .find ({}). Skip (1) .limit ( 50) .sort ({"Datum": - 1})
Justin Jenkins
6
Was auch immer es ist, die Reihenfolge der aufrufenden Funktionen sollte nichts mit dem Endergebnis zu tun haben.
Morteza Milani
7
@ MortezaM. Natürlich ist die Reihenfolge wichtig. item = 3; item.add(3).divide(3) == 2; item.divide(3).add(3) == 4;Ohne Bestellung, was wäre das Ergebnis? Ich stimme Ihnen zu, dass diese umgekehrte Reihenfolge nicht intuitiv ist. Dies ist kein SQL, schließlich sollte es normalen OO-Paradigmen folgen.
RickyA
38
und doch spielt die Reihenfolge keine Rolle. Alles was Sie tun müssen, ist es zu versuchen, um zu sehen, dass dies nicht der Fall ist. All dies wird auf dem Cursor aufgerufen und an den Server übergeben, sodass der Server die Ergebnisse der Sortierung (oben N) einschränkt, da alles andere keinen Sinn ergibt.
Asya Kamsky
10
Die Dokumente bestätigen, dass die Bestellung keine Rolle spielt: Link
JohnnyHK
120

Die letzten N hinzugefügten Datensätze, von weniger aktuell bis zuletzt, können mit dieser Abfrage angezeigt werden:

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

Wenn Sie sie in umgekehrter Reihenfolge wünschen:

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

Wenn Sie Mongo-Hacker installieren , können Sie auch Folgendes verwenden:

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

Wenn Sie es leid sind, diese Befehle ständig zu schreiben, können Sie benutzerdefinierte Funktionen in Ihren ~ / .mongorc.js erstellen. Z.B

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

dann von einer Mongo-Muschel einfach tippen last(N)

Trasplazio Garzuglio
quelle
1
db.collection.find().reverse().limit(1)gibt mir den Fehler... has no method reverse
Catfish
@Catfish Sie haben Recht, ich habe gerade bemerkt, dass reverse () von [Mongo-Hacker] ( tylerbrock.github.com/mongo-hacker ) hinzugefügt wurde. Ich werde meine Antwort aktualisieren. Vielen Dank.
Trasplazio Garzuglio
1
db.getCollection ('COLLECTION_NAME'). find (). skip (db.getCollection ('COLLECTION_NAME'). count () - N) funktioniert hervorragend für mich :)
Spl2nky
Dies sollte die Antwort auf die Frage sein und nicht die Antwort von Justin Jenkins.
Jadiel de Armas
Auf natürliche Ordnung sollte man sich nicht verlassen; Wenn Sie einen Replikatsatz verwenden (und dies sollten Sie auch tun), werden auf verschiedenen Knoten möglicherweise dieselben Dokumente in einer anderen Reihenfolge auf der Festplatte gespeichert.
Vince Bowdren
14

Um die letzten N Datensätze zu erhalten, können Sie die folgende Abfrage ausführen:

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

Wenn Sie nur einen letzten Datensatz möchten:

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

Hinweis: Anstelle von $ natural können Sie eine der Spalten aus Ihrer Sammlung verwenden.

Ashwini
quelle
Dies funktioniert für mich db.yourcollectionname.findOne ({$ query: {}, $ orderby: {$ natural: -1}}). Ich denke, letzte Parathensis fehlt in der Antwort
Ajay
Auf natürliche Ordnung sollte man sich nicht verlassen; Wenn Sie einen Replikatsatz verwenden (und dies sollten Sie auch tun), werden auf verschiedenen Knoten möglicherweise dieselben Dokumente in einer anderen Reihenfolge auf der Festplatte gespeichert.
Vince Bowdren
6

Sie verwenden können sort(), limit(), skip()zuletzt N Aufzeichnungsstart von jedem übersprungenen Wert zu erhalten

db.collections.find().sort(key:value).limit(int value).skip(some int value);
pkp
quelle
6

Sie können diese Methode ausprobieren:

Holen Sie sich die Gesamtzahl der Datensätze in der Sammlung mit

db.dbcollection.count() 

Verwenden Sie dann überspringen:

db.dbcollection.find().skip(db.dbcollection.count() - 1).pretty()
Bello Hargbola
quelle
5

Schauen Sie unter Abfragen: Sortieren und natürliche Reihenfolge, http://www.mongodb.org/display/DOCS/Sorting+and+Natural+Order sowie sort () unter Cursormethoden http://www.mongodb.org/display nach / DOCS / Advanced + Abfragen

Steve Wilhelm
quelle
1
Vielen Dank für Ihre Antwort, es ist nah, aber ich möchte Datensätze zurückhalten, die von weniger aktuell bis zuletzt bestellt wurden. Ist das möglich?
Bin Chen
Auf natürliche Ordnung sollte man sich nicht verlassen; Wenn Sie einen Replikatsatz verwenden (und dies sollten Sie auch tun), werden auf verschiedenen Knoten möglicherweise dieselben Dokumente in einer anderen Reihenfolge auf der Festplatte gespeichert.
Vince Bowdren
Wenn Sie die neuesten Datensätze abrufen möchten, müssen Sie sich auf ein Datumsfeld im Dokument verlassen.
Vince Bowdren
5

Sie können nicht basierend auf der Größe der Sammlung "überspringen", da die Abfragebedingungen nicht berücksichtigt werden.

Die richtige Lösung besteht darin, nach dem gewünschten Endpunkt zu sortieren, die Größe der Ergebnismenge zu begrenzen und gegebenenfalls die Reihenfolge der Ergebnisse anzupassen.

Hier ist ein Beispiel, das auf realem Code basiert.

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
quelle
Schöne Problemumgehung! Wäre schön, wenn ich das auch auf Abfrageebene machen könnte. So etwas wie var query = collection.find( { conditions } ).sort({$natural : -1}).reverse().limit(N).
Inwpitrust
4

@ bin-chen,

Sie können eine Aggregation für die letzten n Einträge einer Teilmenge von Dokumenten in einer Sammlung verwenden. Hier ist ein vereinfachtes Beispiel ohne Gruppierung (was Sie in diesem Fall zwischen den Stufen 4 und 5 tun würden).

Dies gibt die letzten 20 Einträge (basierend auf einem Feld namens "Zeitstempel") in aufsteigender Reihenfolge zurück. Anschließend werden die einzelnen Dokumente _id, timestamp und was auch immer_field_y_want_to_show in die Ergebnisse projiziert.

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)
}

Das Ergebnis würde also ungefähr so ​​aussehen:

{ 
    "_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
}

Hoffe das hilft.

lauri108
quelle
1
DANKE @ lauri108! Von allen Antworten auf diese und verwandte Fragen ist dies DIE EINZIGE funktionierende und zuverlässige Lösung, um "die letzten N DOCS zu erhalten". Und einfach genug, um es in einer Abfrage zu tun. Job erledigt.
Randomsock
@randomsock wenn es dir gefällt,
stimme ab
4

Das Sortieren, Überspringen usw. kann je nach Größe Ihrer Sammlung sehr langsam sein.

Eine bessere Leistung würde erzielt, wenn Sie Ihre Sammlung nach bestimmten Kriterien indizieren lassen. und dann könnten Sie min () Cursor verwenden:

Indizieren Sie zuerst Ihre Sammlung mit db.collectionName.setIndex( yourIndex ) Sie können eine aufsteigende oder absteigende Reihenfolge verwenden, was cool ist, da Sie immer die "N letzten Elemente" möchten. Wenn Sie also in absteigender Reihenfolge indizieren, entspricht dies dem Abrufen der "ersten N Elemente". .

Dann finden Sie das erste Element Ihrer Sammlung und verwenden seine Indexfeldwerte als Mindestkriterium für eine Suche wie:

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

Hier ist die Referenz für den min () Cursor: https://docs.mongodb.com/manual/reference/method/cursor.min/

João Otero
quelle
Einzige Antwort, die Leistung berücksichtigt, tolle Ergänzung :)
Zardilior
Garantiert diese Antwort Ordnung?
Zardilior
Ja, es garantiert, basierend auf Ihrem Index
João Otero
1
Anscheinend können Sie die min einfach vergessen und verwenden: db.collectionName.find().hint(yourIndex).limit(N) Wenn der Index in absteigender Reihenfolge ist, erhalten Sie die N Mindestwerte.
haltux vor
0
db.collection.find().hint( { $natural : -1 } ).sort(field: 1/-1).limit(n)

laut mongoDB Dokumentation :

Sie können {$ natural: 1} angeben, um die Abfrage zu zwingen, einen Vorwärtssammlungsscan durchzuführen.

Sie können auch {$ natural: -1} angeben, um die Abfrage zu zwingen, einen Reverse Collection-Scan durchzuführen.

Gili.il
quelle
0

Verwenden Sie den Operator $ Slice , um Array-Elemente einzuschränken

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}` });
});

Wenn die Geolokalisierung ein Datenarray ist, erhalten wir die letzten 5 Datensätze.

KARTHIKEYAN.A
quelle
-5

Letzte Funktion sollte sortnicht sein limit.

Beispiel:

db.testcollection.find().limit(3).sort({timestamp:-1}); 
Ramesh Kasi
quelle
2
Das scheint falsch. Warum sortsollte danach sein limit?
Acumenus