Node.jsとmongooseでwebappを書いています。 .find()
呼び出しから取得した結果をページ分割するにはどうすればよいですか? SQLの"LIMIT 50,100"
に匹敵する機能が欲しい。
Rodolpheから提供された情報を使用してMongoose APIを詳しく調べた後、このソリューションを見つけました。
MyModel.find(query, fields, { skip: 10, limit: 5 }, function(err, results) { ... });
この質問で受け入れられた答えに非常に失望しています。これはスケーリングしません。 cursor.skip()の細字部分を読む場合:
Cursor.skip()メソッドは、多くの場合、結果を返す前にサーバーがコレクションまたはインデックスの先頭からオフセットを取得するか、位置をスキップする必要があるため、高価です。オフセット(上記のpageNumberなど)が増加すると、cursor.skip()は遅くなり、CPUを集中的に使用します。より大きなコレクションでは、cursor.skip()はIOにバインドされる場合があります。
スケーラブルな方法でページネーションを達成するには、limit()を少なくとも1つのフィルター基準と組み合わせて、createdOn日付が多くの目的に適合します。
MyModel.find( { createdOn: { $lte: request.createdOnBefore } } )
.limit( 10 )
.sort( '-createdOn' )
マングース、エクスプレス、ヒスイを使用したページネーション- http://madhums.me/2012/08/20/pagination-using-mongoose-express-and-jade/
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
})
})
})
次のようにチェーンできます:
var query = Model.find().sort('mykey', 1).skip(2).limit(5)
exec
を使用してクエリを実行します
query.exec(callback);
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 pageOptions = {
page: req.query.page || 0,
limit: req.query.limit || 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);
})
この場合、クエリpage
やlimit
をhttp URLに追加できます。サンプル?page=0&limit=25
BTWページネーションは0
で始まります
これはあなたがこれを試すことができるサンプル例です、
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
});
それは私がそれをやった方法です。
すべてのモデルに添付するバージョンを次に示します。便宜上アンダースコアに依存し、パフォーマンスに非同期に依存します。 optsは、フィールドの選択と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;
}
モデルスキーマにアタッチします。
MySchema.statics.findPaginated = findPaginated;
シンプルで強力なページネーションソリューション
last_doc_id
:最後に取得したドキュメントID
no_of_docs_required
:取得するドキュメントの数(5、10、50など).
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
}
最も簡単で迅速な方法は、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)は、スキップを使用して、限られたコレクションまたはドキュメント内でBUTを制限することです。
限られたドキュメント内でクエリを作成するために、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)
上記の答えは良好です。
約束ではなく非同期待ちに興味がある人のためのアドオンです!
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
});
}
};
これは、ページネーションと制限オプションを備えたスキルモデルの結果を取得するためのサンプル関数です
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
}
ページネーションのための最も簡単なプラグイン。
https://www.npmjs.com/package/mongoose-paginate-v2
プラグインをスキーマに追加してから、モデルのページ編集メソッドを使用します。
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
次のコード行も使用できます
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()
このコードはmongoの最新バージョンで動作します
このようなクエリを書くことができます。
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:リクエストパラメータとしてクライアントから送信されるページ番号。
per_page:ページごとに結果が表示されません
ブログ投稿に続くMEANスタックを使用している場合、アンギュラーUI bootstrapを使用し、バックエンドでmongoose skipおよびlimitメソッドを使用して、フロントエンドでページネーションを作成するための多くの情報を提供します。
参照: https://techpituwa.wordpress.com/2015/06/06/mean-js-pagination-with-angular-ui-bootstrap/
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});
}
});
});
安らかなAPIのソースとしてmongooseを使用している場合は、「 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
したがって、基本的には、コレクションに対するクエリのロード時間が比較的線形の汎用サーバーを取得します。これは素晴らしいことであり、独自の実装に移行したい場合は検討する必要があります。
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;
}
}
}
**//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);
});
});
}
Skip()とlimit()を使用できますが、非常に非効率的です。より良い解決策は、インデックス付きフィールドとlimit()の並べ替えです。 Wunderflatsの小さなライブラリをここに公開しました: https://github.com/wunderflats/goosepage 最初の方法を使用します。