it-swarm.dev

Jak paginować z Mongoose w Node.js?

Piszę webapp z Node.js i mongoose. Jak mogę pogrupować wyniki uzyskane w wywołaniu .find()? Chciałbym funkcjonalność porównywalną z "LIMIT 50,100" w SQL.

182
Thomas

Po bliższym przyjrzeniu się API Mongoose z informacjami dostarczonymi przez Rodolphe, zorientowałem się w tym rozwiązaniu:

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

Jestem bardzo rozczarowany przyjętymi odpowiedziami w tym pytaniu. To się nie skaluje. Jeśli przeczytasz drobny druk na cursor.skip ():

Metoda cursor.skip () jest często kosztowna, ponieważ wymaga przejścia serwera od początku kolekcji lub indeksu, aby uzyskać przesunięcie lub pominąć pozycję przed rozpoczęciem zwracania wyniku. Wraz ze wzrostem przesunięcia (np. PageNumber powyżej), cursor.skip () stanie się wolniejszy i bardziej obciążony procesorem. Przy większych kolekcjach, cursor.skip () może zostać powiązane IO.

Aby uzyskać paginację w skalowalny sposób, należy połączyć limit () z co najmniej jednym kryterium filtrowania, a utworzona data odpowiada wielu celom. 

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

Paginacja z użyciem mangusty, ekspresowej i jade - 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
            })
        })
    })
74
Madhusudhan

Możesz tak połączyć łańcuch:

var query = Model.find().sort('mykey', 1).skip(2).limit(5)

Wykonaj zapytanie używając exec

query.exec(callback);
50
Rodolphe

Lepiej późno niż wcale.

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

W takim przypadku możesz dodać zapytanie page i/lub limit do adresu URL http. Próbka ?page=0&limit=25

BTW Podział na strony zaczyna się od 0

31
CENT1PEDE

Możesz użyć małego pakietu o nazwie Mongoose Paginate który to ułatwi.

$ npm install mongoose-paginate

Po trasach lub kontrolerze dodaj:

/**
 * 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);
  }
}
31
Clément Renaud

To jest przykładowy przykład, którego możesz spróbować,

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

Spróbuj użyć funkcji mangusty do paginacji. Limit to liczba rekordów na stronę i numer strony.

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){
 });
9
Karthick

Właśnie to zrobiłem na kodzie

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

Tak to zrobiłem.

8
Indra Santosa

Oto wersja, którą dołączam do wszystkich moich modeli. Dla wygody zależy od podkreślenia i asynchronizacji dla wydajności. Opcje pozwalają na wybór pola i sortowanie przy użyciu składni mangusty.

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

Dołącz go do schematu modelu.

MySchema.statics.findPaginated = findPaginated;
5
kberg

Najłatwiejszym i szybszym sposobem jest paginacja za pomocą objectId Przykład;

Stan początkowego obciążenia

condition = {limit:12, type:""};

Weź pierwszy i ostatni obiekt z danych odpowiedzi

Stan następnej strony

condition = {limit:12, type:"next", firstId:"57762a4c875adce3c38c662d", lastId:"57762a4c875adce3c38c6615"};

Stan następnej strony

condition = {limit:12, type:"next", firstId:"57762a4c875adce3c38c6645", lastId:"57762a4c875adce3c38c6675"};

W mangusty

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

Najlepszym podejściem (IMO) jest użycie pominięcia i ograniczenia ALE w ograniczonych kolekcjach lub dokumentach. 

Aby zapytanie zawierało ograniczone dokumenty, możemy użyć indeksu określonego indeksu w polu typu DATE. Zobacz poniżej

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)
2
Nazmul Hassan

Jest to przykładowa funkcja do uzyskania wyniku modelu umiejętności z opcją paginacji i limitu

 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

}

2
Venkatesh Somu

Najprostsza wtyczka do stronicowania.

https://www.npmjs.com/package/mongoose-paginate-v2

Dodaj wtyczkę do schematu, a następnie użyj metody paginacji modelu:

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
2
Arv Ot

Proste i wydajne rozwiązanie stronicowania

async getNextDocs(last_doc_id?: string) {
    let docs

    if (!last_doc_id) {
        // get first 5 docs
        docs = await MySchema.find().sort({ _id: -1 }).limit(5)
    }
    else {
        // get next 5 docs according to that last document id
        docs = await MySchema.find({ _id: { $lt: last_doc_id} }).sort({ _id: -1 }).limit(5)
    }
    return docs
}
  1. Jeśli nie podasz ostatniego identyfikatora dokumentu, otrzymasz 5 najnowszych dokumentów
  2. Jeśli podałeś ostatni identyfikator dokumentu, otrzymasz 5 kolejnych dokumentów.
1
WasiF

Powyższa odpowiedź jest dobra. 

Po prostu dodatek dla każdego, kto jest w async-await zamiast obiecuję !!

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

Możesz napisać zapytanie w ten sposób. 

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

strona: numer strony pochodzący od klienta jako parametry żądania.
na stronę: brak wyników wyświetlanych na stronie 

Jeśli korzystasz ze stosu MEAN, następujący post na blogu zawiera wiele informacji do tworzenia stronicowania z przodu przy użyciu paska startowego angular-UI i przy użyciu metody pomijania i ograniczania mangusty w zapleczu.

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

0
Manoj Sanjeewa
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});
            }
        });
});
0
jvitoroc

możesz również użyć następującej linii kodu

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

ten kod będzie działać w najnowszej wersji mongo

0
Apoorv

Jeśli używasz mangusty jako źródła spokojnego api, spójrz na ' restify-mongoose ' i jego zapytania. Ma dokładnie taką funkcjonalność. 

Każde zapytanie w kolekcji zawiera przydatne nagłówki 

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

Zasadniczo otrzymujesz ogólny serwer ze stosunkowo liniowym czasem ładowania dla zapytań do kolekcji. To jest niesamowite i coś do obejrzenia, jeśli chcesz przejść do własnej implementacji. 

Był w stanie osiągnąć wyniki również z asynchronizacją/oczekiwaniem.

Poniższy przykładowy kod wykorzystuje program asynchroniczny z hapi v17 i 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;
               }

            }
        }
0
csichar
**//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);
            });

    });
}
0
chetan

Możesz użyć skip () i limit (), ale jest to bardzo nieefektywne. Lepszym rozwiązaniem byłoby sortowanie na polu indeksowanym plus ograniczenie (). W Wunderflats opublikowaliśmy małą bibliotekę tutaj: https://github.com/wunderflats/goosepage Używa pierwszego sposobu.

0
Jan Hase