sql >> Base de Datos >  >> NoSQL >> MongoDB

Implementando la paginación en mongodb

El concepto del que está hablando se puede llamar "paginación hacia adelante". Una buena razón para eso es diferente al uso de .skip() y .limit() modificadores, esto no se puede usar para "regresar" a una página anterior o, de hecho, "saltar" a una página específica. Al menos no con un gran esfuerzo para almacenar páginas "vistas" o "descubiertas", por lo que si ese tipo de paginación de "enlaces a la página" es lo que desea, entonces es mejor que se quede con el .skip() y .limit() enfoque, a pesar de los inconvenientes de rendimiento.

Si es una opción viable para usted solo "avanzar", este es el concepto básico:

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 }

Por supuesto, esa es su primera página con un límite de 3 elementos. Considere que ahora con código iterando el cursor:

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

Entonces itera el cursor y hace algo, y cuando es cierto que se alcanza el último elemento en el cursor, almacena el lastSeen valor al presente _id :

ObjectId("54c03f0c2f63310180151879")

En sus iteraciones posteriores, solo alimenta ese _id valor que mantiene (en sesión o lo que sea) a la consulta:

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 }

Y el proceso se repite una y otra vez hasta que no se pueden obtener más resultados.

Ese es el proceso básico para un orden natural como _id . Por otra parte, se vuelve un poco más complejo. Considere lo siguiente:

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

Para dividir eso en dos páginas ordenadas por rango, lo que esencialmente necesita saber es lo que "ya ha visto" y excluir esos resultados. Así que mirando una primera página:

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 }

En la próxima iteración, desea que sea menor o igual que el puntaje de "clasificación" de lastSeen, pero también excluyendo los documentos ya vistos. Haces esto con $nin operador:

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 }

La cantidad de "seenIds" a los que realmente se aferra depende de qué tan "granulares" sean sus resultados donde es probable que cambie ese valor. En este caso, puede verificar si el puntaje actual de "clasificación" no es igual al lastSeen valor y descartar el presente seenIds contenido para que no crezca demasiado.

Esos son los conceptos básicos de "paginación hacia adelante" para que practiques y aprendas.