J'écris une webapp avec Node.js et mangouste. Comment paginer les résultats que j'obtiens d'un .find()
appel? Je voudrais une fonctionnalité comparable à "LIMIT 50,100"
SQL.
J'écris une webapp avec Node.js et mangouste. Comment paginer les résultats que j'obtiens d'un .find()
appel? Je voudrais une fonctionnalité comparable à "LIMIT 50,100"
SQL.
Réponses:
Je suis très déçu par les réponses acceptées dans cette question. Cela ne sera pas mis à l'échelle. Si vous lisez les petits caractères sur cursor.skip ():
La méthode cursor.skip () est souvent coûteuse car elle nécessite que le serveur marche depuis le début de la collection ou de l'index pour obtenir la position de décalage ou de saut avant de commencer à renvoyer le résultat. Au fur et à mesure que l'offset (par exemple pageNumber ci-dessus) augmente, cursor.skip () deviendra plus lent et plus gourmand en CPU. Avec des collections plus importantes, cursor.skip () peut devenir lié aux entrées-sorties.
Pour obtenir une pagination de manière évolutive, combinez une limite () avec au moins un critère de filtre, une date createdOn convient à de nombreuses fins.
MyModel.find( { createdOn: { $lte: request.createdOnBefore } } )
.limit( 10 )
.sort( '-createdOn' )
-createdOn
', vous remplaceriez la valeur de request.createdOnBefore
par la plus petite valeur createdOn
retournée dans le jeu de résultats précédent, puis requery.
Après avoir examiné de plus près l'API Mongoose avec les informations fournies par Rodolphe, j'ai trouvé cette solution:
MyModel.find(query, fields, { skip: 10, limit: 5 }, function(err, results) { ... });
Pagination avec mangouste, express et jade - Voici un lien vers mon blog avec plus de détails
var perPage = 10
, page = Math.max(0, req.param('page'))
Event.find()
.select('name')
.limit(perPage)
.skip(perPage * page)
.sort({
name: 'asc'
})
.exec(function(err, events) {
Event.count().exec(function(err, count) {
res.render('events', {
events: events,
page: page,
pages: count / perPage
})
})
})
Math.max(0, undefined)
reviendra undefined
, Cela a fonctionné pour moi:let limit = Math.abs(req.query.limit) || 10;
let page = (Math.abs(req.query.page) || 1) - 1;
Schema.find().limit(limit).skip(limit * page)
Vous pouvez enchaîner comme ça:
var query = Model.find().sort('mykey', 1).skip(2).limit(5)
Exécutez la requête à l'aide de exec
query.exec(callback);
var page = req.param('p'); var per_page = 10; if (page == null) { page = 0; } Location.count({}, function(err, count) { Location.find({}).skip(page*per_page).limit(per_page).execFind(function(err, locations) { res.render('index', { locations: locations }); }); });
Dans ce cas, vous pouvez ajouter la requête page
et / ou limit
à votre URL en tant que chaîne de requête.
Par exemple:
?page=0&limit=25 // this would be added onto your URL: http:localhost:5000?page=0&limit=25
Puisque ce serait un String
nous devons le convertir en un Number
pour nos calculs. Faisons-le en utilisant la parseInt
méthode et fournissons également quelques valeurs par défaut.
const pageOptions = {
page: parseInt(req.query.page, 10) || 0,
limit: parseInt(req.query.limit, 10) || 10
}
sexyModel.find()
.skip(pageOptions.page * pageOptions.limit)
.limit(pageOptions.limit)
.exec(function (err, doc) {
if(err) { res.status(500).json(err); return; };
res.status(200).json(doc);
});
La pagination BTW commence par0
mongoose
.
Vous pouvez utiliser un petit paquet appelé Mongoose Paginate qui le rend plus facile.
$ npm install mongoose-paginate
Après dans vos routes ou votre contrôleur, ajoutez simplement:
/**
* querying for `all` {} items in `MyModel`
* paginating by second page, 10 items per page (10 results, page 2)
**/
MyModel.paginate({}, 2, 10, function(error, pageCount, paginatedResults) {
if (error) {
console.error(error);
} else {
console.log('Pages:', pageCount);
console.log(paginatedResults);
}
}
Voici un exemple d'exemple que vous pouvez essayer,
var _pageNumber = 2,
_pageSize = 50;
Student.count({},function(err,count){
Student.find({}, null, {
sort: {
Name: 1
}
}).skip(_pageNumber > 0 ? ((_pageNumber - 1) * _pageSize) : 0).limit(_pageSize).exec(function(err, docs) {
if (err)
res.json(err);
else
res.json({
"TotalCount": count,
"_Array": docs
});
});
});
Essayez d'utiliser la fonction mangouste pour la pagination. La limite est le nombre d'enregistrements par page et le numéro de la page.
var limit = parseInt(body.limit);
var skip = (parseInt(body.page)-1) * parseInt(limit);
db.Rankings.find({})
.sort('-id')
.limit(limit)
.skip(skip)
.exec(function(err,wins){
});
Voilà ce que je l'ai fait sur le code
var paginate = 20;
var page = pageNumber;
MySchema.find({}).sort('mykey', 1).skip((pageNumber-1)*paginate).limit(paginate)
.exec(function(err, result) {
// Write some stuff here
});
C'est comme ça que je l'ai fait.
count()
est obsolète. utilisationcountDocuments()
Requete;
search = productName,
Params;
page = 1
// Pagination
router.get("/search/:page", (req, res, next) => {
const resultsPerPage = 5;
const page = req.params.page >= 1 ? req.params.page : 1;
const query = req.query.search;
Product.find({ name: query })
.select("name")
.sort({ name: "asc" })
.limit(resultsPerPage)
.skip(resultsPerPage * page)
.then((results) => {
return res.status(200).send(results);
})
.catch((err) => {
return res.status(500).send(err);
});
});
Voici une version que j'attache à tous mes modèles. Cela dépend du trait de soulignement pour plus de commodité et de l'async pour les performances. L'option permet la sélection et le tri des champs à l'aide de la syntaxe mangouste.
var _ = require('underscore');
var async = require('async');
function findPaginated(filter, opts, cb) {
var defaults = {skip : 0, limit : 10};
opts = _.extend({}, defaults, opts);
filter = _.extend({}, filter);
var cntQry = this.find(filter);
var qry = this.find(filter);
if (opts.sort) {
qry = qry.sort(opts.sort);
}
if (opts.fields) {
qry = qry.select(opts.fields);
}
qry = qry.limit(opts.limit).skip(opts.skip);
async.parallel(
[
function (cb) {
cntQry.count(cb);
},
function (cb) {
qry.exec(cb);
}
],
function (err, results) {
if (err) return cb(err);
var count = 0, ret = [];
_.each(results, function (r) {
if (typeof(r) == 'number') {
count = r;
} else if (typeof(r) != 'number') {
ret = r;
}
});
cb(null, {totalCount : count, results : ret});
}
);
return qry;
}
Attachez-le à votre schéma de modèle.
MySchema.statics.findPaginated = findPaginated;
Solution de pagination simple et puissante
async getNextDocs(no_of_docs_required: number, last_doc_id?: string) {
let docs
if (!last_doc_id) {
// get first 5 docs
docs = await MySchema.find().sort({ _id: -1 }).limit(no_of_docs_required)
}
else {
// get next 5 docs according to that last document id
docs = await MySchema.find({_id: {$lt: last_doc_id}})
.sort({ _id: -1 }).limit(no_of_docs_required)
}
return docs
}
last_doc_id
: le dernier identifiant de document que vous obtenez
no_of_docs_required
: le nombre de documents que vous souhaitez récupérer soit 5, 10, 50 etc.
last_doc_id
pas la méthode, vous obtiendrez, par exemple, les 5 derniers documentslast_doc_id
vous obtiendrez les 5 documents suivants.La réponse ci-dessus est valable.
Juste un add-on pour quiconque est en asynchrone attend plutôt que de promettre !!
const findAllFoo = async (req, resp, next) => {
const pageSize = 10;
const currentPage = 1;
try {
const foos = await FooModel.find() // find all documents
.skip(pageSize * (currentPage - 1)) // we will not retrieve all records, but will skip first 'n' records
.limit(pageSize); // will limit/restrict the number of records to display
const numberOfFoos = await FooModel.countDocuments(); // count the number of records for that model
resp.setHeader('max-records', numberOfFoos);
resp.status(200).json(foos);
} catch (err) {
resp.status(500).json({
message: err
});
}
};
vous pouvez également utiliser la ligne de code suivante
per_page = parseInt(req.query.per_page) || 10
page_no = parseInt(req.query.page_no) || 1
var pagination = {
limit: per_page ,
skip:per_page * (page_no - 1)
}
users = await User.find({<CONDITION>}).limit(pagination.limit).skip(pagination.skip).exec()
ce code fonctionnera dans la dernière version de mongo
Une approche solide pour implémenter cela serait de passer les valeurs du frontend en utilisant une chaîne de requête . Disons que nous voulons obtenir la page n ° 2 et limiter également la sortie à 25 résultats .
La chaîne de requête ressemblerait à ceci:?page=2&limit=25 // this would be added onto your URL: http:localhost:5000?page=2&limit=25
Voyons le code:
// We would receive the values with req.query.<<valueName>> => e.g. req.query.page
// Since it would be a String we need to convert it to a Number in order to do our
// necessary calculations. Let's do it using the parseInt() method and let's also provide some default values:
const page = parseInt(req.query.page, 10) || 1; // getting the 'page' value
const limit = parseInt(req.query.limit, 10) || 25; // getting the 'limit' value
const startIndex = (page - 1) * limit; // this is how we would calculate the start index aka the SKIP value
const endIndex = page * limit; // this is how we would calculate the end index
// We also need the 'total' and we can get it easily using the Mongoose built-in **countDocuments** method
const total = await <<modelName>>.countDocuments();
// skip() will return a certain number of results after a certain number of documents.
// limit() is used to specify the maximum number of results to be returned.
// Let's assume that both are set (if that's not the case, the default value will be used for)
query = query.skip(startIndex).limit(limit);
// Executing the query
const results = await query;
// Pagination result
// Let's now prepare an object for the frontend
const pagination = {};
// If the endIndex is smaller than the total number of documents, we have a next page
if (endIndex < total) {
pagination.next = {
page: page + 1,
limit
};
}
// If the startIndex is greater than 0, we have a previous page
if (startIndex > 0) {
pagination.prev = {
page: page - 1,
limit
};
}
// Implementing some final touches and making a successful response (Express.js)
const advancedResults = {
success: true,
count: results.length,
pagination,
data: results
}
// That's it. All we have to do now is send the `results` to the frontend.
res.status(200).json(advancedResults);
Je suggérerais d'implémenter cette logique dans un middleware afin que vous puissiez l'utiliser pour divers itinéraires / contrôleurs.
Le moyen le plus simple et le plus rapide est de paginer avec l'exemple objectId;
Condition de charge initiale
condition = {limit:12, type:""};
Prendre le premier et le dernier ObjectId des données de réponse
Page condition suivante
condition = {limit:12, type:"next", firstId:"57762a4c875adce3c38c662d", lastId:"57762a4c875adce3c38c6615"};
Page condition suivante
condition = {limit:12, type:"next", firstId:"57762a4c875adce3c38c6645", lastId:"57762a4c875adce3c38c6675"};
Chez la mangouste
var condition = {};
var sort = { _id: 1 };
if (req.body.type == "next") {
condition._id = { $gt: req.body.lastId };
} else if (req.body.type == "prev") {
sort = { _id: -1 };
condition._id = { $lt: req.body.firstId };
}
var query = Model.find(condition, {}, { sort: sort }).limit(req.body.limit);
query.exec(function(err, properties) {
return res.json({ "result": result);
});
La meilleure approche (OMI) consiste à utiliser sauter et limiter MAIS dans une collection ou des documents limités.
Pour effectuer la requête dans des documents limités, nous pouvons utiliser un index spécifique tel qu'un index sur un champ de type DATE. Voir ci-dessous
let page = ctx.request.body.page || 1
let size = ctx.request.body.size || 10
let DATE_FROM = ctx.request.body.date_from
let DATE_TO = ctx.request.body.date_to
var start = (parseInt(page) - 1) * parseInt(size)
let result = await Model.find({ created_at: { $lte: DATE_FROM, $gte: DATE_TO } })
.sort({ _id: -1 })
.select('<fields>')
.skip( start )
.limit( size )
.exec(callback)
Plugin le plus simple pour la pagination.
https://www.npmjs.com/package/mongoose-paginate-v2
Ajoutez un plugin à un schéma, puis utilisez la méthode de pagination du modèle:
var mongoose = require('mongoose');
var mongoosePaginate = require('mongoose-paginate-v2');
var mySchema = new mongoose.Schema({
/* your schema definition */
});
mySchema.plugin(mongoosePaginate);
var myModel = mongoose.model('SampleModel', mySchema);
myModel.paginate().then({}) // Usage
Ceci est un exemple de fonction pour obtenir le résultat d'un modèle de compétences avec des options de pagination et de limite
export function get_skills(req, res){
console.log('get_skills');
var page = req.body.page; // 1 or 2
var size = req.body.size; // 5 or 10 per page
var query = {};
if(page < 0 || page === 0)
{
result = {'status': 401,'message':'invalid page number,should start with 1'};
return res.json(result);
}
query.skip = size * (page - 1)
query.limit = size
Skills.count({},function(err1,tot_count){ //to get the total count of skills
if(err1)
{
res.json({
status: 401,
message:'something went wrong!',
err: err,
})
}
else
{
Skills.find({},{},query).sort({'name':1}).exec(function(err,skill_doc){
if(!err)
{
res.json({
status: 200,
message:'Skills list',
data: data,
tot_count: tot_count,
})
}
else
{
res.json({
status: 401,
message: 'something went wrong',
err: err
})
}
}) //Skills.find end
}
});//Skills.count end
}
Vous pouvez écrire une requête comme celle-ci.
mySchema.find().skip((page-1)*per_page).limit(per_page).exec(function(err, articles) {
if (err) {
return res.status(400).send({
message: err
});
} else {
res.json(articles);
}
});
page: numéro de page provenant du client comme paramètres de requête.
per_page: aucun résultat affiché par page
Si vous utilisez la pile MEAN, le billet de blog suivant fournit une grande partie des informations pour créer la pagination en amont à l'aide de bootstrap angular-UI et des méthodes de saut et de limitation de mangouste dans le backend.
voir: https://techpituwa.wordpress.com/2015/06/06/mean-js-pagination-with-angular-ui-bootstrap/
Vous pouvez soit utiliser skip () et limit (), mais c'est très inefficace. Une meilleure solution serait un tri sur un champ indexé plus une limite (). Chez Wunderflats, nous avons publié une petite bibliothèque ici: https://github.com/wunderflats/goosepage Il utilise la première façon.
Si vous utilisez la mangouste comme source pour une api reposante, jetez un œil à ' restify-mongoose ' et ses requêtes. Il a exactement cette fonctionnalité intégrée.
Toute requête sur une collection fournit des en-têtes utiles ici
test-01:~$ curl -s -D - localhost:3330/data?sort=-created -o /dev/null
HTTP/1.1 200 OK
link: </data?sort=-created&p=0>; rel="first", </data?sort=-created&p=1>; rel="next", </data?sort=-created&p=134715>; rel="last"
.....
Response-Time: 37
Donc, fondamentalement, vous obtenez un serveur générique avec un temps de chargement relativement linéaire pour les requêtes vers les collections. C'est génial et quelque chose à regarder si vous voulez entrer dans une propre implémentation.
app.get("/:page",(req,res)=>{
post.find({}).then((data)=>{
let per_page = 5;
let num_page = Number(req.params.page);
let max_pages = Math.ceil(data.length/per_page);
if(num_page == 0 || num_page > max_pages){
res.render('404');
}else{
let starting = per_page*(num_page-1)
let ending = per_page+starting
res.render('posts', {posts:data.slice(starting,ending), pages: max_pages, current_page: num_page});
}
});
});
**//localhost:3000/asanas/?pageNo=1&size=3**
//requiring asanas model
const asanas = require("../models/asanas");
const fetchAllAsanasDao = () => {
return new Promise((resolve, reject) => {
var pageNo = parseInt(req.query.pageNo);
var size = parseInt(req.query.size);
var query = {};
if (pageNo < 0 || pageNo === 0) {
response = {
"error": true,
"message": "invalid page number, should start with 1"
};
return res.json(response);
}
query.skip = size * (pageNo - 1);
query.limit = size;
asanas
.find(pageNo , size , query)
.then((asanasResult) => {
resolve(asanasResult);
})
.catch((error) => {
reject(error);
});
});
}
Utilisez ce plugin simple.
https://github.com/WebGangster/mongoose-paginate-v2
Installation
npm install mongoose-paginate-v2
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
const mySchema = new mongoose.Schema({
/* your schema definition */
});
mySchema.plugin(mongoosePaginate);
const myModel = mongoose.model('SampleModel', mySchema);
myModel.paginate().then({}) // Usage
selon
répondre:
//assume every page has 50 result
const results = (req.query.page * 1) * 50;
MyModel.find( { fieldNumber: { $lte: results} })
.limit( 50 )
.sort( '+fieldNumber' )
//one thing left is create a fieldNumber on the schema thas holds ducument number
Utilisation de ts-mongoose-pagination
const trainers = await Trainer.paginate(
{ user: req.userId },
{
perPage: 3,
page: 1,
select: '-password, -createdAt -updatedAt -__v',
sort: { createdAt: -1 },
}
)
return res.status(200).json(trainers)
let page,limit,skip,lastPage, query;
page = req.params.page *1 || 1; //This is the page,fetch from the server
limit = req.params.limit * 1 || 1; // This is the limit ,it also fetch from the server
skip = (page - 1) * limit; // Number of skip document
lastPage = page * limit; //last index
counts = await userModel.countDocuments() //Number of document in the collection
query = query.skip(skip).limit(limit) //current page
const paginate = {}
//For previous page
if(skip > 0) {
paginate.prev = {
page: page - 1,
limit: limit
}
//For next page
if(lastPage < counts) {
paginate.next = {
page: page + 1,
limit: limit
}
results = await query //Here is the final results of the query.
A également pu obtenir des résultats avec async / wait.
Exemple de code ci-dessous utilisant un gestionnaire asynchrone avec hapi v17 et mongoose v5
{
method: 'GET',
path: '/api/v1/paintings',
config: {
description: 'Get all the paintings',
tags: ['api', 'v1', 'all paintings']
},
handler: async (request, reply) => {
/*
* Grab the querystring parameters
* page and limit to handle our pagination
*/
var pageOptions = {
page: parseInt(request.query.page) - 1 || 0,
limit: parseInt(request.query.limit) || 10
}
/*
* Apply our sort and limit
*/
try {
return await Painting.find()
.sort({dateCreated: 1, dateModified: -1})
.skip(pageOptions.page * pageOptions.limit)
.limit(pageOptions.limit)
.exec();
} catch(err) {
return err;
}
}
}