Nazywa się to „przekazywaniem stron do przodu”, które jest koncepcją, której można użyć do „wydajnego stronicowania” wyników w kierunku „do przodu” podczas korzystania z wyników „posortowanych”.
Dołączona logika JavaScript (ponieważ działa w powłoce), ale nietrudna do przetłumaczenia.
Ogólna koncepcja:
{ "_id": 1, "a": 3 },
{ "_id": 2, "a": 3 },
{ "_id": 3, "a": 3 },
{ "_id": 4, "a": 2 },
{ "_id": 5, "a": 1 },
{ "_id": 6, "a": 0 }
Rozważ te „już posortowane” dokumenty (dla wygody) jako przykład wyników, które chcemy „stronicować” według „dwóch” elementów na stronie.
W pierwszej kolejności robisz coś takiego:
var lastVal = null,
lastSeen = [];
db.collection.find().sort({ "a": -1 }).limit(2).forEach(function(doc) {
if ( lastVal != doc.a ) {
lastSeen = [];
}
lastVal = doc.a;
lastSeen.push( doc._id );
// do something useful with each document matched
});
Teraz te lastVal
i lastSeen
są czymś, co przechowujesz w czymś w rodzaju „zmiennej sesji”, do której można uzyskać dostęp przy następnym żądaniu w zakresie aplikacji internetowych lub w inny sposób podobny, jeśli nie.
Powinny jednak zawierać ostatnią wartość, według której sortowałeś i listę "unikalnych" _id
wartości, które były widoczne od czasu, gdy ta wartość się nie zmieniła. Stąd:
lastVal = 3,
lastSeen = [1,2];
Chodzi o to, że gdy pojawi się żądanie "następnej strony", chcesz użyć tych zmiennych do czegoś takiego:
var lastVal = 3,
lastSeen = [1,2];
db.collection.find({
"_id": { "$nin": lastSeen },
"a": { "$lte": lastVal }
}).sort({ "a": -1 }).limit(2).forEach(function(doc) {
if ( lastVal != doc.a ) {
lastSeen = [];
}
lastVal = doc.a;
lastSeen.push( doc._id );
// do something useful with each document matched
});
Powoduje to „wykluczenie” wszystkich wartości _id
które są zapisane w lastSeen
z listy wyników, a także upewnij się, że wszystkie wyniki muszą być „mniejsze lub równe” (kolejność malejąca) lastVal
zapisane dla pola sortowania „a”.
Daje to dwa kolejne wyniki w kolekcji:
{ "_id": 3, "a": 3 },
{ "_id": 4, "a": 2 },
Ale po przetworzeniu nasze wartości wyglądają teraz tak:
lastVal = 2,
lastSeen = [4];
Więc teraz logika polega na tym, że nie musisz wykluczać drugiego _id
wartości widziane wcześniej, ponieważ naprawdę szukasz tylko wartości „a”, które są „mniejsze lub równe” lastVal
a ponieważ był tylko "jeden" _id
wartość widziana przy tej wartości, to wyklucza tylko tę.
To oczywiście daje następną stronę na temat używania tego samego kodu, co powyżej:
{ "_id": 5, "a": 1 },
{ "_id": 6, "a": 0 }
Jest to najskuteczniejszy sposób na „przekazywanie strony do przodu” przez wyniki w ogóle i jest szczególnie przydatny do wydajnego stronicowania „posortowanych” wyników.
Jeśli jednak chcesz „przeskoczyć” na stronę 20
lub podobne działanie na dowolnym etapie, to nie jest dla Ciebie. Utknąłeś z tradycyjnym .skip()
i .limit()
podejście, aby móc to zrobić za pomocą „numeru strony”, ponieważ nie ma innego racjonalnego sposobu na „obliczenie” tego.
Wszystko zależy więc od tego, w jaki sposób Twoja aplikacja implementuje „stronicowanie” i z czym możesz żyć. .skip()
i .limit()
podejście cierpi na wydajność „pomijania” i można go uniknąć, stosując podejście tutaj.
Z drugiej strony, jeśli chcesz „przeskoczyć do strony”, to „pomijanie” jest jedyną realną opcją, chyba że chcesz zbudować „pamięć podręczna” wyników. Ale to zupełnie inna kwestia.