Cuando se usa MongoDB, ¿existen patrones especiales para, por ejemplo, hacer una vista paginada? digamos un blog que enumera las 10 últimas publicaciones donde puede navegar hacia atrás a publicaciones más antiguas.
¿O uno lo resuelve con un índice en, por ejemplo, blogpost.publishdate y simplemente salta y limita el resultado?
Respuestas:
Usar skip + limit no es una buena manera de realizar la paginación cuando el rendimiento es un problema o con colecciones grandes; se volverá más y más lento a medida que aumente el número de página. El uso de omitir requiere que el servidor recorra todos los documentos (o valores de índice) desde 0 hasta el valor de compensación (omitir).
Es mucho mejor usar una consulta de rango (+ límite) donde pasa el valor de rango de la última página. Por ejemplo, si está ordenando por "fecha de publicación", simplemente pasaría el último valor de "fecha de publicación" como criterio para que la consulta obtenga la siguiente página de datos.
fuente
Posible solución: intente simplificar el diseño, pensando si solo podemos ordenar por id o algún valor único.
Y si podemos, entonces se puede utilizar la paginación basada en rango.
La forma común es usar sort (), skip () y limit () para implementar la paginación que se describe arriba.
fuente
{ _id: { $gt: ... } }
... simplemente no funciona si se utilizan pedidos personalizados, por ejemplo.sort(...)
.Esta es la solución que utilicé cuando mi colección creció demasiado para devolverla en una sola consulta. Aprovecha el orden inherente del
_id
campo y le permite recorrer una colección por tamaño de lote especificado.Aquí está como un módulo npm, paginación de mongoose , el código completo está a continuación:
function promiseWhile(condition, action) { return new Promise(function(resolve, reject) { process.nextTick(function loop() { if(!condition()) { resolve(); } else { action().then(loop).catch(reject); } }); }); } function findPaged(query, fields, options, iterator, cb) { var Model = this, step = options.step, cursor = null, length = null; promiseWhile(function() { return ( length===null || length > 0 ); }, function() { return new Promise(function(resolve, reject) { if(cursor) query['_id'] = { $gt: cursor }; Model.find(query, fields, options).sort({_id: 1}).limit(step).exec(function(err, items) { if(err) { reject(err); } else { length = items.length; if(length > 0) { cursor = items[length - 1]._id; iterator(items, function(err) { if(err) { reject(err); } else { resolve(); } }); } else { resolve(); } } }); }); }).then(cb).catch(cb); } module.exports = function(schema) { schema.statics.findPaged = findPaged; };
Adjúntelo a su modelo de esta manera:
Luego consulta como esta:
MyModel.findPaged( // mongoose query object, leave blank for all {source: 'email'}, // fields to return, leave blank for all ['subject', 'message'], // number of results per page {step: 100}, // iterator to call on each set of results function(results, cb) { console.log(results); // this is called repeatedly while until there are no more results. // results is an array of maximum length 100 containing the // results of your query // if all goes well cb(); // if your async stuff has an error cb(err); }, // function to call when finished looping function(err) { throw err; // this is called once there are no more results (err is null), // or if there is an error (then err is set) } );
fuente
La paginación basada en rango es factible, pero debe ser inteligente acerca de cómo minimiza / maximiza la consulta.
Si puede permitírselo, intente almacenar en caché los resultados de una consulta en un archivo o colección temporal. Gracias a las colecciones TTL en MongoDB, puede insertar sus resultados en dos colecciones.
El uso de ambos asegura que no obtendrá resultados parciales cuando el TTL esté cerca de la hora actual. Puede utilizar un contador simple cuando almacena los resultados para hacer una consulta de rango MUY simple en ese punto.
fuente
A continuación se muestra un ejemplo de cómo recuperar una lista de
User
documentos ordenados porCreatedDate
(dondepageIndex
está basado en cero) utilizando el controlador oficial de C #.public void List<User> GetUsers() { var connectionString = "<a connection string>"; var client = new MongoClient(connectionString); var server = client.GetServer(); var database = server.GetDatabase("<a database name>"); var sortBy = SortBy<User>.Descending(u => u.CreatedDate); var collection = database.GetCollection<User>("Users"); var cursor = collection.FindAll(); cursor.SetSortOrder(sortBy); cursor.Skip = pageIndex * pageSize; cursor.Limit = pageSize; return cursor.ToList(); }
Todas las operaciones de clasificación y paginación se realizan en el lado del servidor. Aunque este es un ejemplo en C #, supongo que lo mismo se puede aplicar a otros puertos de idioma.
Consulte http://docs.mongodb.org/ecosystem/tutorial/use-csharp-driver/#modifying-a-cursor-before-enumerating-it .
fuente
// file:ad-hoc.js // an example of using the less binary as pager in the bash shell // // call on the shell by: // mongo localhost:27017/mydb ad-hoc.js | less // // note ad-hoc.js must be in your current directory // replace the 27017 wit the port of your mongodb instance // replace the mydb with the name of the db you want to query // // create the connection obj conn = new Mongo(); // set the db of the connection // replace the mydb with the name of the db you want to query db = conn.getDB("mydb"); // replace the products with the name of the collection // populate my the products collection // this is just for demo purposes - you will probably have your data already for (var i=0;i<1000;i++ ) { db.products.insert( [ { _id: i, item: "lamp", qty: 50, type: "desk" }, ], { ordered: true } ) } // replace the products with the name of the collection cursor = db.products.find(); // print the collection contents while ( cursor.hasNext() ) { printjson( cursor.next() ); } // eof file: ad-hoc.js
fuente