Continuando una query (paginando) su un indice composto
-
21-12-2019 - |
Domanda
Ho una domanda (spero veloce) sulle query mongodb su indici composti.
Dì che ho un set di dati (ad esempio, commenti) che voglio ordinare discendente per punteggio, quindi data:
{ "score" : 10, "date" : ISODate("2014-02-24T00:00:00.000Z"), ...}
{ "score" : 10, "date" : ISODate("2014-02-18T00:00:00.000Z"), ...}
{ "score" : 10, "date" : ISODate("2014-02-12T00:00:00.000Z"), ...}
{ "score" : 9, "date" : ISODate("2014-02-22T00:00:00.000Z"), ...}
{ "score" : 9, "date" : ISODate("2014-02-16T00:00:00.000Z"), ...}
...
.
La mia comprensione finora è che posso creare un indice composto per supportare questa query, che sembra {"score":-1,"date":-1}
. (Per la chiarezza, non sto usando una data nell'indice, ma un oggetto per un ordine unico, approssimativamente basato sul tempo)
Ora, dì che voglio supportare il paging attraverso i commenti. La prima pagina è abbastanza facile, posso semplicemente attaccare un'opzione .limit(n)
sulla fine del cursore. Di cosa sto facendo lottare sta continuando la ricerca.
Mi riferisco a mongodb: la guida definitiva di Kristina Chodorow. In questo libro, Kristina menziona che l'utilizzo di Skip () su set di dati di grandi dimensioni non è molto esibita e consiglia di utilizzare intervallo di query sui parametri dall'ultimo risultato visto (ad esempio l'ultima data vista).
Cosa vorrei fare è eseguire una gamma di interrogazioni che agisce su due campi, ma tratta il secondo campo come secondario al primo (proprio come l'indice è ordinato), dal momento che il mio indice composto è già ordinato esattamente nell'ordine Voglio, sembra che ci sia un modo per saltare nella ricerca puntando su un elemento specifico nell'indice e lo attraversò nell'ordine di ordinamento. Tuttavia, dalla mia comprensione della mia (ammessolmente rudimentale) delle query in MongoDB, questo non sembra possibile.
Per quanto posso vedere, ho tre opzioni:
- .
- Utilizzo di
skip()
comunque - Utilizzo di $ o query o due query distinti:
{$or : [{"score" : lastScore, "date" : { $lt : lastDate}}, {'score' : {$lt : lastScore}]}
- Utilizzo dell'opzione Query speciale
$max
- C'è un modo per eseguire l'operazione che ho descritto, che potrei aver perso? (Saltando in un indice e attraversandolo nell'ordine di ordinamento)
- In caso contrario, delle tre opzioni che ho descritto (o di qualsiasi hanno trascurato), il che dovrebbe (in generale in generale) fornire le prestazioni più coerenti nell'ambito dell'indice composto?
- Perché $ lt preferito oltre $ max nella maggior parte dei casi?
Il numero 3 sembra il più vicino all'ideale per me, ma il testo di riferimento nota che "dovresti generalmente usare" $ lt "invece di" $ max "'.
Per riassumere, ho alcune domande:
- .
Grazie in anticipo per il tuo aiuto!
Soluzione
Un'altra opzione è di memorizzare score
e date
in un sotto-documento e quindi indicizzare il sottococumento. Ad esempio:
{
"a" : { "score" : 9,
"date" : ISODate("2014-02-22T00:00:00Z") },
...
}
db.foo.ensureIndex( { a : 1 } )
db.foo.find( { a : { $lt : { score : lastScore,
date: lastDate } } } ).sort( { a : -1 } )
.
Con questo approccio è necessario assicurarsi che i campi nel sotto-documento BSON siano sempre memorizzati nello stesso ordine, altrimenti la query non corrisponde a ciò che ti aspetti dal momento che il confronto del tasto di indice è il confronto binario dell'intero BSON sub- Documento.
Verrei con l'uso di $max
per specificare il limite superiore, in combinazione con $hint
per assicurarti che il database utilizzi l'indice che desideri. Il motivo per cui $lt
è in generale preferito su $max
è perché $max
seleziona l'indice utilizzando i limiti dell'indice specificati. Questo significa:
- .
- L'indice scelto potrebbe non essere necessariamente la scelta migliore.
- Se esistono più indici sugli stessi campi con ordini di ordinamento diversi, la selezione dell'indice potrebbe essere ambigua.
I punti precedenti sono coperti da ulteriori dettagli qui .
Un ultimo punto: max
è equivalente a $lte
, non $lt
, quindi utilizzare questo approccio per il paginazione dovrai saltare sul primo documento restituito per evitare di emettere lo stesso documento due volte.