MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Calcola il valore di salto per un determinato record per il paging ordinato

Questo è chiamato "spostamento in avanti", che è un concetto che puoi utilizzare per "sfogliare in modo efficiente" i risultati in una direzione "avanti" quando usi risultati "ordinati".

Logica JavaScript inclusa (perché funziona nella shell), ma non difficile da tradurre.

Il concetto in generale:

{ "_id": 1, "a": 3 },
{ "_id": 2, "a": 3 },
{ "_id": 3, "a": 3 },
{ "_id": 4, "a": 2 },
{ "_id": 5, "a": 1 },
{ "_id": 6, "a": 0 }

Considera quei documenti "già ordinati" (per comodità) come un esempio di risultati che vogliamo "paginare" per "due" elementi per pagina.

Nella prima istanza fai qualcosa del genere:

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

Ora quei lastVal e lastSeen sono qualcosa che memorizzi in qualcosa come una "variabile di sessione" a cui puoi accedere alla richiesta successiva in termini di applicazioni web, o in altro modo qualcosa di simile dove no.

Ciò che dovrebbero contenere, tuttavia, sono l'ultimo valore su cui stavi ordinando e l'elenco di _id "univoci" valori che sono stati visti poiché quel valore non è cambiato. Quindi:

lastVal = 3,
lastSeen = [1,2];

Il punto è che quando arriva la richiesta per la "pagina successiva", allora vuoi usare quelle variabili per qualcosa del genere:

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

Quello che fa è "escludere" tutti i valori di _id che sono registrati in lastSeen dall'elenco dei risultati, nonché assicurarsi che tutti i risultati debbano essere "minori o uguali a" ( ordine decrescente ) il lastVal registrato per il campo di ordinamento "a".

Questo produce i prossimi due risultati nella raccolta:

{ "_id": 3, "a": 3 },
{ "_id": 4, "a": 2 },

Ma dopo aver elaborato i nostri valori ora appaiono così:

lastVal = 2,
lastSeen = [4];

Quindi ora segue la logica che non è necessario escludere l'altro _id valori visti prima poiché stai cercando solo valori di "a" che sono "minori o uguali a" lastVal e poiché c'era solo "uno" _id valore visto a quel valore, quindi escludere solo quello.

Questo ovviamente produce la pagina successiva sull'utilizzo dello stesso codice appena sopra:

{ "_id": 5, "a": 1 },
{ "_id": 6, "a": 0 }

Questo è il modo più efficiente per "inoltrare la pagina" attraverso i risultati in generale ed è particolarmente utile per il paging efficiente dei risultati "ordinati".

Se invece vuoi "salta" alla pagina 20 o azioni simili in qualsiasi momento, allora questo non fa per te. Sei bloccato con il tradizionale .skip() e .limit() approccio per poterlo fare in base al "numero di pagina" poiché non esiste un altro modo razionale per "calcolarlo".

Quindi tutto dipende da come la tua applicazione sta implementando il "paging" e con cosa puoi convivere. Il .skip() e .limit() l'approccio subisce le prestazioni di "salto" e può essere evitato utilizzando l'approccio qui.

D'altra parte, se vuoi "saltare alla pagina", allora "saltare" è la tua unica opzione reale a meno che tu non voglia costruire una "cache" di risultati. Ma questa è tutta un'altra questione.