2017年10月16日 星期一

query pagination data with mongoose


目前透過 mongoose 查詢 pagination 資料覺得很麻煩,
先取得 total count 再取 page data,
目前使用的方式如下:

    user
        .find(where)
        .count()
        .then((totalCount) => {
            if (totalCount && totalCount > 0) {
                return user.find(where)
                    .select('field1 field2 field3 filed4')
                    .sort({ createdOn: -1 })
                    .skip(skip)
                    .limit(limit)
                    .exec()
                    .then((rows) => {
                        return {
                          rows:rows
                          totalCount: totalCount
                    });
            } else {
                 return res.status(200)
                        .json({
                         rows: [],
                         skip: skip,
                         limit: limit,
                         totalCount: 0
                        })
                        .end();
            }
        })
        .then((result) => {
                 return res.status(200)
                        .json({
                         rows: result.rows,
                         skip: skip,
                         limit: limit,
                         totalCount: result.totalCount
                        })
                        .end();
        })
        .catch((err) => {
            next(err);
        });

寫得落落長,太麻煩了,花了一些時間查查,有沒有什麼方便的方式
原來 mongoose 有 pagination plugin 可以用,真的是佛心來的,
使用方式如下:

Installation:
npm install mongoose-paginate

Usage:
var mongoose         = require('mongoose');
var mongoosePaginate = require('mongoose-paginate');
 
var schema = new mongoose.Schema({ /* schema definition */ });
schema.plugin(mongoosePaginate);
 
var Model = mongoose.model('Model',  schema); // Model.paginate() 

Model.paginate([query], [options], [callback])

所以上面的範例可以改成:
var where   = {};
var options = {
    select:   'field1 field2 field3 filed4',
    sort:     { createdOn: -1 },
    lean:     true,
    offset:   20, 
    limit:    10
};
user.paginate(where, options).then(function(result) {
    return res.status(200)
                        .json({
                         rows: result.docs,
                         skip: skip,
                         limit: limit,
                         totalCount: result.total
                        })
                        .end()
});

太好了,是不是簡單很多!

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

Avoiding Callback Hell in Node.js - Promise


寫 nodejs 就會用到非同步的 callback
就會碰到所謂的「callback hell」的囧境
什麼是 callback hell? 如下所示:

doSomeAsyncFunc(function () {
  doSomeAsyncFunc(function () {
    doSomeAsyncFunc(function () {
      doSomeAsyncFunc(function () {
        doSomeAsyncFunc(function () {
          doSomeAsyncFunc(function () {
            doSomeAsyncFunc(function () {
              doSomeAsyncFunc(function () {
                doSomeAsyncFunc(function () {
                  // 我到底在第幾層地獄啊!?
              })
            })
          })
        })
      })
    })
  })
})



這樣光要看懂就很辛苦了,還要怎麼維護程式碼呢?

有幾種解決方式:

  1. async.waterfall
  2. promise
  3. async/await

上一篇介紹了 async.waterfall,現在來看看 Promise 如何使用

1. 先將 doSomeAsyncFunc 包裝成 promise 物件
doSomeAsyncFunc(function () {
  return new Promise(function(resolve, reject) {
    // do something, possibly async
    if (/* everythings fine */) {
      resolve(result);
    } else {
      reject(err);
    }
  });
}

2. 使用 primise.then 即可攤平 callback hell

var promise = doSomeAsyncFunc();
promise.then((result) => {
  // get result
  return '2';
}).then((result2) => {
  // get result2 ('2')
  return '3';
}).then((result3) => {
  // get result3 ('3')
}).catch((err) => {
  // error handling
});

3. 所以一開始的範例就可以改成:

var promise = doSomeAsyncFunc();
promise.then((result) => {
  // do something
  return doSomeAsyncFunc();
}).then((result) => {
  // do something
  return doSomeAsyncFunc();
}).then((result) => {
  // do something
  return doSomeAsyncFunc();
}).then((result) => {
  // do something
  return doSomeAsyncFunc();
}).then((result) => {
  // do something
  return doSomeAsyncFunc();
}).catch((err) => {
  // error handling
});

這樣就可以攤平可怕的 callback hell,回到地球表面囉!