programing

Node.js에서 Mongoose로 페이지 번호를 매기는 방법

magicmemo 2023. 3. 17. 20:32
반응형

Node.js에서 Mongoose로 페이지 번호를 매기는 방법

Node.js(mongoose) webapp.에서 얻은 결과를 어떻게 페이지 매길 수 있습니까?.find() ??에 . 비슷한 기능을 원합니다."LIMIT 50,100"SQL sql sql sql sql sql 。

이 질문에서 받아들여진 답변에 매우 실망했습니다.이것은 확장되지 않을 것이다.cursor.skip()의 세세한 글씨를 읽을 경우:

cursor.skip() 메서드는 결과 반환을 시작하기 전에 서버가 오프셋 또는 건너뛰기 위치를 얻기 위해 수집 또는 인덱스의 시작부터 걸어야 하기 때문에 비용이 많이 듭니다.오프셋(예를 들어 위의 pageNumber)이 증가하면 cursor.skip()은 속도가 느려지고 CPU 부하가 높아집니다.컬렉션이 클수록 cursor.skip()은 IO 바인딩이 될 수 있습니다.

크기 조정 가능한 방법으로 페이지 수를 제한()과 하나 이상의 필터 기준을 결합하기 위해 created On 날짜는 다양한 목적에 적합합니다.

MyModel.find( { createdOn: { $lte: request.createdOnBefore } } )
.limit( 10 )
.sort( '-createdOn' )

Rodolphe가 제공한 정보로 Mongoose API를 자세히 살펴본 후 다음과 같은 해결책을 찾았습니다.

MyModel.find(query, fields, { skip: 10, limit: 5 }, function(err, results) { ... });

mongoose, express, jade를 사용한 페이지화 - 자세한블로그 링크입니다.

var perPage = 10
  , page = Math.max(0, req.params.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);

쿼리를 할 수 .page "/"limitURL 입니다.

예를 들어 다음과 같습니다.
?page=0&limit=25 // this would be added onto your URL: http:localhost:5000?page=0&limit=25

String 해서 요.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);
    });

BTW 페이지화는 다음으로 시작합니다.0

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);
  }
}

쿼리:

search = productName

파라미터:

page = 1 

// Pagination
router.get("/search/:page", (req, res, next) => {
    const resultsPerPage = 5;
    let page = req.params.page >= 1 ? req.params.page : 1;
    const query = req.query.search;

    page = page - 1

    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 _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
      });
  });
 });

페이지 표시에 mongoose 기능을 사용해 보십시오.제한은 페이지당 레코드 수 및 페이지 수입니다.

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
    });

그게 내가 한 방법이에요.

심플하고 강력한 페이지 처리 솔루션

async getNextDocs(no_of_docs_required: number = 5, 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 "

no_of_docs_required 5, 등 5, 10, 50 ) 。

  1. 「 를 ,last_doc_id의 최신 수
  2. 이 하신 경우last_doc_id5시 정각

skip() & limit()를 사용하는 솔루션을 제공하는 좋은 답변도 있지만, 페이지 수를 생성하기 위해 문서 수가 필요할 수 있습니다.델의 프로젝트는 다음과 같습니다.

const PaginatePlugin = (schema, options) => {
  options = options || {}
  schema.query.paginate = async function(params) {
    const pagination = {
      limit: options.limit || 10,
      page: 1,
      count: 0
    }
    pagination.limit = parseInt(params.limit) || pagination.limit
    const page = parseInt(params.page)
    pagination.page = page > 0 ? page : pagination.page
    const offset = (pagination.page - 1) * pagination.limit

    const [data, count] = await Promise.all([
      this.limit(pagination.limit).skip(offset),
      this.model.countDocuments(this.getQuery())
    ]);
    pagination.count = count;
    return { data, pagination }
  }
}

mySchema.plugin(PaginatePlugin, { limit: DEFAULT_LIMIT })

// using async/await
const { data, pagination } = await MyModel.find(...)
  .populate(...)
  .sort(...)
  .paginate({ page: 1, limit: 10 })

// or using Promise
MyModel.find(...).paginate(req.query)
  .then(({ data, pagination }) => {

  })
  .catch(err => {

  })

여기 모든 모델에 첨부한 버전이 있습니다.편의상 언더스코어에 의존하며 성능상 비동기적입니다.옵션을 사용하면 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;

위의 답변은 유효합니다.

약속보다 비동기 대기에 관심이 있는 분들을 위한 애드온!!

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()

이 코드는 최신 버전의 mongo에서 작동합니다.

이를 구현하기 위한 확실한 접근법은 쿼리 문자열을 사용하여 프런트 엔드에서 값을 전달하는 것입니다.예를 들어 페이지 #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);

다양한 루트/컨트롤러에서 사용할 수 있도록 미들웨어에 이 로직을 실장하는 것이 좋습니다.

mongoose-paginate-v2를 사용할 수 있습니다.자세한 내용은 여기를 클릭해 주세요.

const mongoose         = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');

const mySchema = new mongoose.Schema({
    // your schema code
}); 
mySchema.plugin(mongoosePaginate); 
const myModel = mongoose.model('SampleModel',  mySchema);

myModel.paginate().then({}) // Usage

저는 매우 효율적인 방법을 찾아 직접 구현했습니다.이 방법이 가장 좋은 이유는 다음과 같습니다.

  • 스킵을 사용하지 않기 때문에 시간의 복잡성이 적절히 확장되지 않습니다.
  • ID를 사용하여 문서를 조회합니다.ID는 기본적으로 MongoDB로 인덱싱되므로 매우 빠르게 조회할 수 있습니다.
  • Mongoose에서 "매직"을 많이 제거하고 MongoDB에서 "원시" 문서를 반환하기 때문에 매우 성능이 뛰어난 것으로 알려진 린 쿼리를 사용합니다.
  • 취약점이 포함되거나 취약한 종속성을 가질 수 있는 타사 패키지에 종속되지 않습니다.

유일한 일부 , 몽구스의 방법이 있다는 이다..save()lean 쿼리에서는 잘 동작하지 않습니다.이 훌륭한 블로그 포스트에는 이러한 방법이 기재되어 있습니다.이것은 타입 보안(중요한 에러를 방지하는 것)이나 PUT/Patch 등 많은 측면을 고려하기 때문입니다.

은 다음과 같습니다.이치노페이지 구성은 다음과 같습니다.를 수신합니다.req.body표현하다NoSQL 주입을 방지하기 위해 이 오브젝트를 문자열로 변환해야 합니다(부정한 필터가 있는 오브젝트일 수 있습니다).이 secureId는 빈 문자열 또는 이전 페이지 마지막 항목의 ID일 수 있습니다.이 ID는 다음과 같습니다.

 /**
   * @description GET All with pagination, will return 200 in success
   * and receives the last ID of the previous page or undefined for the first page
   * Note: You should take care, read and consider about Off-By-One error
   * @param {string|undefined|unknown} unsafeId - An entire page that comes after this ID will be returned
   */
  async readPages(unsafeId) {
    try {
      const id = String(unsafeId || '');
      let criteria;
      if (id) {
        criteria = {_id: {$gt: id}};
      } // else criteria is undefined

      // This query looks a bit redundant on `lean`, I just really wanted to make sure it is lean
      const pokemon = await PokemonSchema.find(
          criteria || {},
      ).setOptions({lean: true}).limit(15).lean();

      // This would throw on an empty page
      // if (pokemon.length < 1) {
      //  throw new PokemonNotFound();
      // }

      return pokemon;
    } catch (error) {
      // In this implementation, any error that is not defined by us
      // will not return on the API to prevent information disclosure.
      // our errors have this property, that indicate
      // that no sensitive information is contained within this object
      if (error.returnErrorResponse) {
        throw error;
      } // else
      console.error(error.message);
      throw new InternalServerError();
    }
  }

이것을 소비해, 프런트엔드의 오프 바이 원 에러를 회피하려면 , 다음과 같이 실행합니다.pokemons는 API에서 반환되는 Pokémons 문서 배열입니다.

// Page zero
const pokemons = await fetchWithPagination({'page': undefined});
// Page one
// You can also use a fixed number of pages instead of `pokemons.length`
// But `pokemon.length` is more reliable (and a bit slower)
// You will have trouble with the last page if you use it with a constant
// predefined number 
const id = pokemons[pokemons.length - 1]._id;

if (!id) {
    throw new Error('Last element from page zero has no ID');
} // else

const page2 = await fetchWithPagination({'page': id});

여기에서는 Mongoose ID는 항상 순차적입니다.이는 새로운 ID가 항상 이전 ID보다 크다는 것을 의미하며, 이것이 이 답변의 기초가 됩니다.

이 접근방식은 Off-By-One 오류로 테스트되고 있습니다.예를 들어, 페이지의 마지막 요소가 다음 요소의 첫 번째 요소로 반환되거나(복제), 이전 페이지의 마지막 요소와 현재 페이지의 첫 번째 요소 사이에 있는 요소가 사라지는 경우가 있습니다.

모든 페이지를 완료하고 마지막 요소(존재하지 않는 요소) 뒤에 페이지를 요청하면 응답이 200(OK)의 빈 배열이 됩니다.이것은 매우 훌륭합니다.

가장 쉽고 빠른 방법은 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

스키마에 플러그인을 추가한 다음 모델 페이지 지정 메서드를 사용합니다.

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
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.
const page = req.query.page * 1 || 1;
const limit = req.query.limit * 1 || 1000;
const skip = (page - 1) * limit;

query = query.skip(skip).limit(limit);

페이지 매기기 및 제한 옵션과 함께 스킬 모델의 결과를 얻기 위한 예제 함수입니다.

 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

}

ts-mongoose-pagation 사용

    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)

아래 코드는 정상 작동 중입니다. countDocs 쿼리에 검색 필터와 동일한 사용자를 추가하여 정확한 결과를 얻을 수 있습니다.

export const yourController = async (req, res) => {
  const { body } = req;

  var perPage = body.limit,
  var page = Math.max(0, body.page);

  yourModel
    .find() // You Can Add Your Filters inside
    .limit(perPage)
    .skip(perPage * (page - 1))
    .exec(function (err, dbRes) {
      yourModel.count().exec(function (err, count) { // You Can Add Your Filters inside
        res.send(
          JSON.stringify({
            Articles: dbRes,
            page: page,
            pages: count / perPage,
          })
        );
      });
    });
};

이렇게 쿼리를 작성할 수 있습니다.

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 스택을 사용하는 경우 다음 블로그 게시물에서는 angular-UI 부트스트랩을 사용하여 백엔드에서 mongoose skip and limit 메서드를 사용하여 프런트 엔드에서 페이지를 작성하기 위한 많은 정보를 제공합니다.

참조: https://techpituwa.wordpress.com/2015/06/06/mean-js-pagination-with-angular-ui-bootstrap/

skip()과 limit() 중 하나를 사용할 수 있지만 매우 비효율적입니다.더 좋은 해결책은 인덱스필드에 제한을 더한 정렬입니다().Wunderflats에서는 다음과 같은 작은 lib를 발행했습니다.https://github.com/wunderflats/goosepage 첫 번째 방법을 사용합니다.

restful 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

따라서 기본적으로는 수집에 대한 쿼리의 로드 시간이 비교적 선형적인 범용 서버를 얻을 수 있습니다.그것은 훌륭하고, 독자적인 실장을 원하신다면 검토해 보시기 바랍니다.

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});
            }
        });
});

언급URL : https://stackoverflow.com/questions/5539955/how-to-paginate-with-mongoose-in-node-js

반응형