Tôi đang viết một ứng dụng web với Node.js và mongoose. Làm cách nào tôi có thể phân trang kết quả tôi nhận được từ một .find()
cuộc gọi? Tôi muốn một chức năng tương đương với "LIMIT 50,100"
trong SQL.
Tôi đang viết một ứng dụng web với Node.js và mongoose. Làm cách nào tôi có thể phân trang kết quả tôi nhận được từ một .find()
cuộc gọi? Tôi muốn một chức năng tương đương với "LIMIT 50,100"
trong SQL.
Câu trả lời:
Tôi rất thất vọng bởi những câu trả lời được chấp nhận trong câu hỏi này. Điều này sẽ không mở rộng. Nếu bạn đọc bản in đẹp trên con trỏ.skip ():
Phương thức con trỏ.skip () thường tốn kém vì nó yêu cầu máy chủ đi bộ từ đầu bộ sưu tập hoặc chỉ mục để lấy vị trí bù hoặc bỏ qua trước khi bắt đầu trả về kết quả. Khi offset (ví dụ: số trang ở trên) tăng lên, con trỏ.skip () sẽ trở nên chậm hơn và tốn nhiều CPU hơn. Với các bộ sưu tập lớn hơn, con trỏ.skip () có thể bị ràng buộc IO.
Để đạt được phân trang theo cách có thể mở rộng, hãy kết hợp một giới hạn () cùng với ít nhất một tiêu chí bộ lọc, ngày được tạo phù hợp với nhiều mục đích.
MyModel.find( { createdOn: { $lte: request.createdOnBefore } } )
.limit( 10 )
.sort( '-createdOn' )
-createdOn
', bạn sẽ thay thế giá trị request.createdOnBefore
bằng giá trị createdOn
trả về ít nhất trong tập kết quả trước đó, và sau đó yêu cầu.
Sau khi xem xét kỹ hơn về API Mongoose với thông tin được cung cấp bởi Rodolphe, tôi đã tìm ra giải pháp này:
MyModel.find(query, fields, { skip: 10, limit: 5 }, function(err, results) { ... });
Phân trang bằng cầy mangut, thể hiện và ngọc bích - Đây là một liên kết đến blog của tôi với nhiều chi tiết hơn
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)
sẽ trở lại undefined
, Điều này làm việc cho tôi:let limit = Math.abs(req.query.limit) || 10;
let page = (Math.abs(req.query.page) || 1) - 1;
Schema.find().limit(limit).skip(limit * page)
Bạn có thể xâu chuỗi như thế:
var query = Model.find().sort('mykey', 1).skip(2).limit(5)
Thực hiện truy vấn bằng cách sử dụng 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 }); }); });
Trong trường hợp này, bạn có thể thêm truy vấn page
và / hoặc limit
vào URL của bạn dưới dạng chuỗi truy vấn.
Ví dụ:
?page=0&limit=25 // this would be added onto your URL: http:localhost:5000?page=0&limit=25
Vì nó sẽ là một thứ String
chúng ta cần chuyển đổi nó thành một Number
tính toán của chúng ta. Chúng ta hãy làm điều đó bằng parseInt
phương pháp và cũng cung cấp một số giá trị mặc định.
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);
});
Phân trang BTW
bắt đầu bằng0
mongoose
.
Bạn có thể sử dụng một gói nhỏ gọi là Mongoose Paginate giúp dễ dàng hơn.
$ npm install mongoose-paginate
Sau khi trong tuyến đường hoặc bộ điều khiển của bạn, chỉ cần thêm:
/**
* 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);
}
}
Đây là một ví dụ mẫu bạn có thể thử,
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
});
});
});
Hãy thử sử dụng chức năng cầy mangut để phân trang. Giới hạn là số lượng hồ sơ trên mỗi trang và số lượng trang.
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){
});
Đây là những gì tôi đã làm nó trên mã
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
});
Đó là cách tôi đã làm nó.
count()
bị phản đối sử dụngcountDocuments()
Truy vấn;
tìm kiếm = tên sản phẩm,
Param;
trang = 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);
});
});
Đây là một phiên bản mà tôi đính kèm cho tất cả các mô hình của tôi. Nó phụ thuộc vào gạch dưới để thuận tiện và không đồng bộ cho hiệu suất. Các opts cho phép lựa chọn và sắp xếp trường bằng cú pháp mongoose.
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;
}
Đính kèm nó vào lược đồ mô hình của bạn.
MySchema.statics.findPaginated = findPaginated;
Giải pháp phân trang đơn giản và mạnh mẽ
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
: id tài liệu cuối cùng mà bạn nhận được
no_of_docs_required
: số lượng tài liệu mà bạn muốn tìm nạp, tức là 5, 10, 50, v.v.
last_doc_id
phương thức, bạn sẽ nhận được tức là 5 tài liệu mới nhấtlast_doc_id
thì bạn sẽ nhận được 5 tài liệu tiếp theo.Trên câu trả lời giữ tốt.
Chỉ là một tiện ích bổ sung cho bất kỳ ai không đồng bộ - chờ đợi hơn là hứa hẹn !!
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
});
}
};
bạn cũng có thể sử dụng dòng mã sau
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()
mã này sẽ hoạt động trong phiên bản mới nhất của mongo
Một cách tiếp cận vững chắc để thực hiện điều này sẽ là chuyển các giá trị từ frontend bằng chuỗi truy vấn . Giả sử chúng tôi muốn lấy trang số 2 và cũng giới hạn đầu ra ở 25 kết quả .
Chuỗi truy vấn sẽ như thế này:?page=2&limit=25 // this would be added onto your URL: http:localhost:5000?page=2&limit=25
Hãy xem mã:
// 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);
Tôi sẽ đề nghị triển khai logic này vào phần mềm trung gian để bạn có thể sử dụng nó cho các tuyến / bộ điều khiển khác nhau.
Cách dễ nhất và nhanh hơn là, phân trang với Ví dụ objectId;
Điều kiện tải ban đầu
condition = {limit:12, type:""};
Lấy ObjectId đầu tiên và cuối cùng từ dữ liệu phản hồi
Trang điều kiện tiếp theo
condition = {limit:12, type:"next", firstId:"57762a4c875adce3c38c662d", lastId:"57762a4c875adce3c38c6615"};
Trang điều kiện tiếp theo
condition = {limit:12, type:"next", firstId:"57762a4c875adce3c38c6645", lastId:"57762a4c875adce3c38c6675"};
Ở cầy mangut
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);
});
Cách tiếp cận tốt nhất (IMO) là sử dụng bỏ qua và giới hạn NHƯNG trong một bộ sưu tập hoặc tài liệu giới hạn.
Để thực hiện truy vấn trong các tài liệu giới hạn, chúng ta có thể sử dụng chỉ mục cụ thể như chỉ mục trên trường loại DATE. Xem bên dưới
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 dễ nhất cho phân trang.
https://www.npmjs.com/package/mongoose-paginate-v2
Thêm plugin vào một lược đồ và sau đó sử dụng phương pháp phân trang mô hình:
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
Đây là chức năng ví dụ để nhận kết quả của mô hình kỹ năng với các tùy chọn phân trang và giới hạn
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
}
Bạn có thể viết truy vấn như thế này.
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);
}
});
trang: số trang đến từ khách hàng như tham số yêu cầu.
per_page: không có kết quả nào được hiển thị trên mỗi trang
Nếu bạn đang sử dụng MEAN stack theo bài đăng trên blog, sẽ cung cấp nhiều thông tin để tạo phân trang ở giao diện người dùng bằng cách sử dụng bootstrap góc và sử dụng phương pháp bỏ qua và giới hạn mongoose trong phần phụ trợ.
xem: https://techpituwa.wordpress.com/2015/06/06/mean-js-pagination-with-angular-ui-bootstrap/
Bạn có thể sử dụng Skip () và giới hạn (), nhưng nó rất không hiệu quả. Một giải pháp tốt hơn sẽ là sắp xếp trên trường được lập chỉ mục cộng với giới hạn (). Chúng tôi tại Wunderflats đã xuất bản một lib nhỏ ở đây: https://github.com/wunderflats/goosepage Nó sử dụng cách đầu tiên.
Nếu bạn đang sử dụng cầy mangut làm nguồn cho một api yên tĩnh, hãy xem ' restify-mongoose ' và các truy vấn của nó. Nó có chính xác chức năng này được xây dựng trong.
Bất kỳ truy vấn nào trên bộ sưu tập đều cung cấp các tiêu đề hữu ích ở đây
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
Vì vậy, về cơ bản, bạn nhận được một máy chủ chung với thời gian tải tương đối tuyến tính cho các truy vấn đến bộ sưu tập. Đó là điều tuyệt vời và một cái gì đó để xem xét nếu bạn muốn đi vào thực hiện riêng.
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);
});
});
}
Sử dụng plugin đơn giản này.
https://github.com/WebGangster/mongoose-paginate-v2
Cài đặt
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
dựa theo
câu trả lời:
//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
Sử dụng 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.
Đã có thể đạt được kết quả với async / await.
Ví dụ mã dưới đây bằng trình xử lý async với hapi v17 và 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;
}
}
}