MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Paginierung in mongodb implementieren

Das Konzept, von dem Sie sprechen, kann als "Forward Paging" bezeichnet werden. Ein guter Grund dafür ist der Unterschied zur Verwendung von .skip() und .limit() Modifikatoren Dies kann nicht verwendet werden, um zu einer vorherigen Seite "zurückzugehen" oder tatsächlich zu einer bestimmten Seite zu "springen". Zumindest nicht mit viel Aufwand, um "gesehene" oder "entdeckte" Seiten zu speichern, wenn Sie also diese Art von "Links zu Seite"-Seiten suchen, bleiben Sie am besten bei der .skip() und .limit() Ansatz, trotz der Leistungsnachteile.

Wenn es für Sie eine praktikable Option ist, nur "vorwärts zu gehen", dann ist hier das Grundkonzept:

db.junk.find().limit(3)

{ "_id" : ObjectId("54c03f0c2f63310180151877"), "a" : 1, "b" : 1 }
{ "_id" : ObjectId("54c03f0c2f63310180151878"), "a" : 4, "b" : 4 }
{ "_id" : ObjectId("54c03f0c2f63310180151879"), "a" : 10, "b" : 10 }

Das ist natürlich Ihre erste Seite mit einem Limit von 3 Artikeln. Betrachten Sie das jetzt mit Code, der den Cursor iteriert:

var lastSeen = null;
var cursor = db.junk.find().limit(3);

while (cursor.hasNext()) {
   var doc = cursor.next();
   printjson(doc);
   if (!cursor.hasNext())
     lastSeen = doc._id;
}

Damit iteriert der Cursor und tut etwas, und wenn es wahr ist, dass das letzte Element im Cursor erreicht ist, speichern Sie den lastSeen Wert auf die aktuelle _id :

ObjectId("54c03f0c2f63310180151879")

In Ihren nachfolgenden Iterationen füttern Sie einfach diese _id Wert, den Sie (in Sitzung oder was auch immer) für die Abfrage behalten:

var cursor = db.junk.find({ "_id": { "$gt": lastSeen } }).limit(3);

while (cursor.hasNext()) {
   var doc = cursor.next();
   printjson(doc);
   if (!cursor.hasNext())
     lastSeen = doc._id;
}

{ "_id" : ObjectId("54c03f0c2f6331018015187a"), "a" : 1, "b" : 1 }
{ "_id" : ObjectId("54c03f0c2f6331018015187b"), "a" : 6, "b" : 6 }
{ "_id" : ObjectId("54c03f0c2f6331018015187c"), "a" : 7, "b" : 7 }

Und der Prozess wiederholt sich immer und immer wieder, bis keine Ergebnisse mehr erzielt werden können.

Das ist der grundlegende Prozess für eine natürliche Reihenfolge wie _id . Bei etwas anderem wird es etwas komplexer. Beachten Sie Folgendes:

{ "_id": 4, "rank": 3 }
{ "_id": 8, "rank": 3 }
{ "_id": 1, "rank": 3 }    
{ "_id": 3, "rank": 2 }

Um das in zwei nach Rang sortierte Seiten aufzuteilen, müssen Sie im Wesentlichen wissen, was Sie "bereits gesehen" haben, und diese Ergebnisse ausschließen. Betrachtet man also eine erste Seite:

var lastSeen = null;
var seenIds = [];
var cursor = db.junk.find().sort({ "rank": -1 }).limit(2);

while (cursor.hasNext()) {
   var doc = cursor.next();
   printjson(doc);
   if ( lastSeen != null && doc.rank != lastSeen )
       seenIds = [];
   seenIds.push(doc._id);
   if (!cursor.hasNext() || lastSeen == null)
     lastSeen = doc.rank;
}

{ "_id": 4, "rank": 3 }
{ "_id": 8, "rank": 3 }

Bei der nächsten Iteration möchten Sie kleiner oder gleich dem „Rang“-Score von lastSeen sein, aber auch die bereits gesehenen Dokumente ausschließen. Das machst du mit $nin Betreiber:

var cursor = db.junk.find(
    { "_id": { "$nin": seenIds }, "rank": "$lte": lastSeen }
).sort({ "rank": -1 }).limit(2);

while (cursor.hasNext()) {
   var doc = cursor.next();
   printjson(doc);
   if ( lastSeen != null && doc.rank != lastSeen )
       seenIds = [];
   seenIds.push(doc._id);
   if (!cursor.hasNext() || lastSeen == null)
     lastSeen = doc.rank;
}

{ "_id": 1, "rank": 3 }    
{ "_id": 3, "rank": 2 }

Wie viele "seenIds" Sie tatsächlich behalten, hängt davon ab, wie "granular" Ihre Ergebnisse sind, wo sich dieser Wert wahrscheinlich ändert. In diesem Fall können Sie überprüfen, ob der aktuelle "Rang"-Score nicht gleich dem lastSeen ist Wert und verwerfen Sie die vorhandenen seenIds Inhalt, damit er nicht zu sehr anwächst.

Das sind die Grundkonzepte des "Weiterleitens", die Sie üben und lernen können.