MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Implementando paginação no mongodb


O conceito de que você está falando pode ser chamado de "paginação direta". Uma boa razão para isso é diferente de usar .skip() e .limit() modificadores isso não pode ser usado para "voltar" para uma página anterior ou mesmo "pular" para uma página específica. Pelo menos não com muito esforço para armazenar páginas "vistas" ou "descobertas", então se esse tipo de paginação "links para página" é o que você deseja, então é melhor ficar com o .skip() e .limit() abordagem, apesar das desvantagens de desempenho.

Se for uma opção viável para você apenas "avançar", então aqui está o conceito 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 }

Claro que é a sua primeira página com um limite de 3 itens. Considere isso agora com o código iterando o 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;
}

Para que itere o cursor e faça algo, e quando for verdade que o último item no cursor foi alcançado, você armazena o lastSeen valor para o presente _id :
ObjectId("54c03f0c2f63310180151879")

Em suas iterações subsequentes, você apenas alimenta esse _id valor que você mantém (na sessão ou qualquer outro) para a 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 }

E o processo se repete várias vezes até que não seja possível obter mais resultados.

Esse é o processo básico para uma ordem natural como _id . Para outra coisa fica um pouco mais complexo. Considere o seguinte:
{ "_id": 4, "rank": 3 }
{ "_id": 8, "rank": 3 }
{ "_id": 1, "rank": 3 }    
{ "_id": 3, "rank": 2 }

Para dividir isso em duas páginas classificadas por classificação, o que você precisa saber essencialmente é o que "já viu" e excluir esses resultados. Então, olhando para uma primeira 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 }

Na próxima iteração, você deseja ser menor ou igual à pontuação "rank" do lastSeen, mas também excluindo os documentos já vistos. Você faz isso com o $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 }

Quantos "seenIds" você realmente mantém depende de quão "granulares" são seus resultados onde esse valor provavelmente mudará. Nesse caso, você pode verificar se a pontuação atual do "rank" não é igual ao lastSeen value e descarte os seenIds atuais conteúdo para que não cresça muito.

Esses são os conceitos básicos de "paginação direta" para você praticar e aprender.