Etwas seltsam in Bezug auf die Abfrageleistung ... Ich muss eine Abfrage ausführen, die eine Gesamtanzahl von Dokumenten ausführt und auch eine Ergebnismenge zurückgeben kann, die begrenzt und versetzt werden kann.
Ich habe also insgesamt 57 Dokumente und der Benutzer möchte 10 Dokumente, die um 20 versetzt sind.
Ich kann mir zwei Möglichkeiten vorstellen, dies zu tun: Zuerst werden alle 57 Dokumente abgefragt (als Array zurückgegeben) und dann mit array.slice die gewünschten Dokumente zurückgegeben. Die zweite Option besteht darin, zwei Abfragen auszuführen, die erste mit der nativen 'count'-Methode von mongo, und dann eine zweite Abfrage mit den nativen Aggregatoren $ limit und $ skip von mongo auszuführen.
Was denkst du würde besser skalieren? Alles in einer Abfrage erledigen oder zwei separate ausführen?
Bearbeiten:
// 1 query
var limit = 10;
var offset = 20;
Animals.find({}, function (err, animals) {
if (err) {
return next(err);
}
res.send({count: animals.length, animals: animals.slice(offset, limit + offset)});
});
// 2 queries
Animals.find({}, {limit:10, skip:20} function (err, animals) {
if (err) {
return next(err);
}
Animals.count({}, function (err, count) {
if (err) {
return next(err);
}
res.send({count: count, animals: animals});
});
});
count()
nicht sicher, aber die Standardfunktion in PHP berücksichtigtlimit
oderskip
berücksichtigt sie nur, wenn Sie dazu aufgefordert werden. Wenn Sie nur eine Abfrage von Limit ausführen und überspringen und dann die Zählung abrufen, sollte dies hier wahrscheinlich die leistungsstärkste Lösung sein. Wie können Sie jedoch feststellen, dass es 57 Dokumente gibt, wenn Sie nicht zwei Abfragen durchführen, um zu zählen, was aktuell vorhanden ist? Haben Sie eine statische Nummer, die sich nie ändert? Wenn nicht, müssen Sie sowohl überspringen als auch begrenzen und dann zählen.db.collection.find(<query>).count();
count()
Funktion von MongoDB. Diecount()
Funktion in MongoDB ist relativ langsam, aber immer noch so schnell wie die meisten clientseitigen Variationen bei größeren Sets und möglicherweise schneller als die clientseitige Zählung hier. Dieser Teil ist jedoch subjektiv für Ihre eigenen Tests. Wohlgemerkt, ich habe schon früher Arrays mit einer Länge von 10.000 gezählt, so dass es auf der Clientseite möglicherweise schneller ist. Bei 10.000 Elementen ist es sehr schwer zu sagen.Antworten:
Ich schlage vor, 2 Abfragen zu verwenden:
db.collection.count()
gibt die Gesamtzahl der Artikel zurück. Dieser Wert wird irgendwo in Mongo gespeichert und nicht berechnet.db.collection.find().skip(20).limit(10)
Hier gehe ich davon aus, dass Sie eine Sortierung nach einem Feld verwenden können. Vergessen Sie also nicht, einen Index für dieses Feld hinzuzufügen. Diese Abfrage wird auch schnell sein.Ich denke, dass Sie nicht alle Elemente abfragen und dann überspringen und nehmen sollten, da Sie später, wenn Sie über große Datenmengen verfügen, Probleme mit der Datenübertragung und -verarbeitung haben werden.
quelle
.skip()
Anweisung für die CPU schwer ist, weil sie an den Anfang der Sammlung geht und den im Parameter von angegebenen Wert erreicht.skip()
. Es kann einen echten Einfluss auf die große Sammlung haben! Aber ich weiß.skip()
sowieso nicht, welches zwischen den Einsätzen am schwersten ist, oder ich bekomme die gesamte Kollektion und schneide mit JS ... Was denkst du?.skip()
. Diese Antwort korrigiert es und empfiehlt, einen Filter für ein Datumsfeld zu verwenden. Man könnte dies mit den.skip()
&.take()
Methoden verwenden. Dies scheint eine gute Idee zu sein. Ich habe jedoch Probleme mit der Frage dieses OP, wie die Gesamtzahl der Dokumente ermittelt werden kann..skip()
Wie können wir eine genaue Zählung durchführen, wenn ein Filter verwendet wird, um die Auswirkungen auf die Leistung zu bekämpfen ? Die in der Datenbank gespeicherte Anzahl spiegelt nicht unseren gefilterten Datensatz wider.cursor.count()
die Anzahl der gefilterten Dokumente zurückzugeben (es wird keine Abfrage ausgeführt, sondern die Anzahl der übereinstimmenden Dokumente). Stellen Sie sicher, dass Ihre Filter- und Auftragseigenschaften indiziert sind und alles in Ordnung ist.cursor.count()
sollte funktionieren, wie @ user854301 hervorhob. Am Ende fügte ich meiner API (/api/my-colllection/stats
) einen Endpunkt hinzu , mit dem ich mithilfe der Funktion db.collection.stats von Mongoose verschiedene Statistiken für meine Sammlungen zurückgab . Da ich dies wirklich nur für mein Front-End benötigte, habe ich nur den Endpunkt abgefragt, um diese Informationen unabhängig von meiner serverseitigen Paginierung zurückzugeben.Anstatt zwei separate Abfragen zu verwenden, können Sie diese
aggregate()
in einer einzigen Abfrage verwenden:Aggregierte "$ facet" können schneller abgerufen werden, die Gesamtzahl und die Daten mit Skip & Limit
db.collection.aggregate([ //{$sort: {...}} //{$match:{...}} {$facet:{ "stage1" : [ {"$group": {_id:null, count:{$sum:1}}} ], "stage2" : [ { "$skip": 0}, {"$limit": 2} ] }}, {$unwind: "$stage1"}, //output projection {$project:{ count: "$stage1.count", data: "$stage2" }} ]);
Ausgabe wie folgt: -
[{ count: 50, data: [ {...}, {...} ] }]
Besuchen Sie auch https://docs.mongodb.com/manual/reference/operator/aggregation/facet/
quelle
Nachdem ich dieses Problem selbst angehen musste, möchte ich auf der Antwort von user854301 aufbauen.
Mungo ^ 4.13.8 Ich konnte eine aufgerufene Funktion verwenden, mit
toConstructor()
der ich vermeiden konnte, die Abfrage mehrmals zu erstellen, wenn Filter angewendet werden. Ich weiß, dass diese Funktion auch in älteren Versionen verfügbar ist, aber Sie müssen die Mongoose-Dokumente überprüfen, um dies zu bestätigen.Das Folgende verwendet Bluebird-Versprechen:
let schema = Query.find({ name: 'bloggs', age: { $gt: 30 } }); // save the query as a 'template' let query = schema.toConstructor(); return Promise.join( schema.count().exec(), query().limit(limit).skip(skip).exec(), function (total, data) { return { data: data, total: total } } );
Jetzt gibt die Zählabfrage die Gesamtzahl der übereinstimmenden Datensätze zurück und die zurückgegebenen Daten sind eine Teilmenge der Gesamtzahl der Datensätze.
Bitte beachten Sie das () um query (), das die Abfrage erstellt.
quelle
Es gibt eine Bibliothek, die all dies für Sie erledigt. Schauen Sie sich mongoose-paginate-v2 an
quelle
db.collection_name.aggregate([ { '$match' : { } }, { '$sort' : { '_id' : -1 } }, { '$facet' : { metadata: [ { $count: "total" } ], data: [ { $skip: 1 }, { $limit: 10 },{ '$project' : {"_id":0} } ] // add projection here wish you re-shape the docs } } ] )
Anstatt zwei Abfragen zu verwenden, um die Gesamtzahl zu ermitteln und den übereinstimmenden Datensatz zu überspringen.
$ facet ist der beste und optimierte Weg.
quelle