Я пишу веб-приложение с Node.js и мангуст. Как я могу разбить на страницы результаты, которые я получаю от .find()
звонка? Я хотел бы функциональность, сравнимую с "LIMIT 50,100"
SQL.
Я пишу веб-приложение с Node.js и мангуст. Как я могу разбить на страницы результаты, которые я получаю от .find()
звонка? Я хотел бы функциональность, сравнимую с "LIMIT 50,100"
SQL.
Ответы:
Я очень разочарован принятыми ответами на этот вопрос. Это не будет масштабироваться. Если вы читаете мелкий шрифт на cursor.skip ():
Метод cursor.skip () часто является дорогим, потому что он требует, чтобы сервер прошел от начала коллекции или индекса, чтобы получить смещение или позицию пропуска, прежде чем начать возвращать результат. По мере увеличения смещения (например, pageNumber выше) cursor.skip () будет медленнее и интенсивнее загружать процессор. В больших коллекциях cursor.skip () может стать связанным с IO.
Чтобы добиться масштабируемости нумерации страниц, объедините limit () и хотя бы один критерий фильтра. Дата createOn подходит для многих целей.
MyModel.find( { createdOn: { $lte: request.createdOnBefore } } )
.limit( 10 )
.sort( '-createdOn' )
-createdOn
', вы бы заменили значение request.createdOnBefore
на наименьшее значение, createdOn
возвращенное в предыдущем наборе результатов, а затем запрос.
После более пристального взгляда на API Mongoose с информацией, предоставленной Родольфом, я нашел это решение:
MyModel.find(query, fields, { skip: 10, limit: 5 }, function(err, results) { ... });
Пагинация с использованием мангуста, экспресса и нефрита - вот ссылка на мой блог с более подробной информацией
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)
вернется undefined
, это сработало для меня:let limit = Math.abs(req.query.limit) || 10;
let page = (Math.abs(req.query.page) || 1) - 1;
Schema.find().limit(limit).skip(limit * page)
Вы можете цепочкой просто так:
var query = Model.find().sort('mykey', 1).skip(2).limit(5)
Выполните запрос, используя 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 }); }); });
В этом случае вы можете добавить запрос page
и / или limit
ваш URL в виде строки запроса.
Например:
?page=0&limit=25 // this would be added onto your URL: http:localhost:5000?page=0&limit=25
Поскольку это было бы String
нам, нам нужно преобразовать его в a Number
для наших расчетов. Давайте сделаем это, используя parseInt
метод, и давайте также предоставим некоторые значения по умолчанию.
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);
});
Кстати,
нумерация страниц начинается с0
mongoose
.
Вы можете использовать небольшую упаковку под названием Mongoose Paginate, которая делает его проще.
$ npm install mongoose-paginate
После в ваших маршрутах или контроллере, просто добавьте:
/**
* 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);
}
}
Это пример, который вы можете попробовать,
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
});
});
});
Попробуйте использовать функцию мангуста для нумерации страниц. Лимит - это количество записей на странице и номер страницы.
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){
});
Это то, что я сделал на коде
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
});
Вот как я это сделал.
count()
устарела. использованиеcountDocuments()
Запрос;
search = productName,
Params;
страница = 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);
});
});
Вот версия, которую я прилагаю ко всем моим моделям. Это зависит от подчеркивания для удобства и асинхронности для производительности. Опции позволяют выбирать и сортировать поля с использованием синтаксиса мангуста.
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;
}
Прикрепите его к схеме вашей модели.
MySchema.statics.findPaginated = findPaginated;
Простое и мощное решение для нумерации страниц
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
: последний идентификатор документа, который вы получаете
no_of_docs_required
: количество документов, которые вы хотите получить, т.е. 5, 10, 50 и т. д.
last_doc_id
метод, то получите 5 последних документовlast_doc_id
вы получите следующие 5 документов.Над ответом остается в силе.
Просто дополнение для тех, кто любит асинхронное ожидание, а не обещание!
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
});
}
};
Вы также можете использовать следующую строку кода
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()
этот код будет работать в последней версии Монго
Надежный подход для реализации этого заключается в передаче значений из внешнего интерфейса с помощью строки запроса . Допустим, мы хотим получить страницу № 2, а также ограничить вывод 25 результатами .
Строка запроса будет выглядеть так:?page=2&limit=25 // this would be added onto your URL: http:localhost:5000?page=2&limit=25
Давайте посмотрим код:
// 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);
Я бы предложил реализовать эту логику в промежуточном программном обеспечении, чтобы вы могли использовать ее для различных маршрутов / контроллеров.
Самый простой и быстрый способ - разбить на страницы с помощью примера objectId;
Начальная нагрузка
condition = {limit:12, type:""};
Возьмите первый и последний ObjectId из данных ответа
Страница следующего условия
condition = {limit:12, type:"next", firstId:"57762a4c875adce3c38c662d", lastId:"57762a4c875adce3c38c6615"};
Страница следующего условия
condition = {limit:12, type:"next", firstId:"57762a4c875adce3c38c6645", lastId:"57762a4c875adce3c38c6675"};
В мангусте
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);
});
Наилучший подход (IMO) - использовать пропуск и ограничение НО в пределах ограниченных коллекций или документов.
Чтобы сделать запрос в ограниченных документах, мы можем использовать определенный индекс, такой как индекс в поле типа DATE. Смотрите ниже
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)
Самый простой плагин для нумерации страниц.
https://www.npmjs.com/package/mongoose-paginate-v2
Добавьте плагин в схему, а затем используйте метод paginate модели:
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
Это примерная функция для получения результата модели навыков с опциями пагинации и ограничения
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
}
Вы можете написать запрос, как это.
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);
}
});
страница: номер страницы, поступающей от клиента в качестве параметров запроса.
per_page: на странице нет результатов
Если вы используете стек MEAN, следующий пост в блоге предоставляет большую часть информации для создания нумерации страниц во внешнем интерфейсе, используя загрузку с угловым пользовательским интерфейсом и использование мангуста пропуска и ограничения в серверной части.
см .: https://techpituwa.wordpress.com/2015/06/06/mean-js-pagination-with-angular-ui-bootstrap/
Вы можете использовать skip () и limit (), но это очень неэффективно. Лучшим решением будет сортировка по индексируемому полю плюс limit (). Мы в Wunderflats опубликовали небольшую библиотеку здесь: https://github.com/wunderflats/goosepage Он использует первый способ.
Если вы используете mongoose в качестве источника для успокоительного API, взгляните на ' restify-mongoose ' и его запросы. Он имеет именно эту встроенную функциональность.
Любой запрос к коллекции предоставляет полезные заголовки
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
Таким образом, вы получаете общий сервер с относительно линейным временем загрузки для запросов к коллекциям. Это потрясающе и на что-то посмотреть, если вы хотите заняться собственной реализацией.
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);
});
});
}
Используйте этот простой плагин.
https://github.com/WebGangster/mongoose-paginate-v2
Монтаж
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
в соответствии с
ответ:
//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
Использование 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.
Также удалось достичь результатов с помощью async / await.
Пример кода ниже с использованием асинхронного обработчика с hapi v17 и 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;
}
}
}