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.