【问题标题】:How to implement pagination for mongodb in node.js using official mongodb client?如何使用官方mongodb客户端在node.js中实现mongodb的分页?
【发布时间】:2019-10-03 13:02:33
【问题描述】:

我想使用offical mongodb packagenode.js 环境中为mongodb 实现分页。我试图在互联网上找到,但都是基于mongoose 的链接。我不想使用猫鼬。

如何使用
http://mongodb.github.io/node-mongodb-native/3.1/api/提供的官方客户端 api 实现分页

【问题讨论】:

    标签: node.js mongodb pagination


    【解决方案1】:

    基于偏移量的方法有一个很大的缺陷:如果结果列表在调用 API 之间发生了变化,则索引会发生变化并导致项目被返回两次或被跳过并且永远不会返回

    这个问题在 https://www.sitepoint.com/paginating-real-time-data-cursor-based-pagination/

    基于时间的分页方法会好一点,因为不再跳过结果。如果您查询第一页,然后删除了一个新项目,它不会改变您第二页中的结果,一切都很好。但是,这种方法有一个重大缺陷:如果同时创建了多个项目怎么办?

    最好使用基于光标的分页
    可以使用集合中唯一、可排序和不可变的任何字段来实现。

    _id 满足所有唯一、可订购和不可变条件。基于该字段,我们可以将最后一个文档的_id作为后续请求的光标进行排序并返回页面结果。

    curl https://api.mixmax.com/items?limit=2

    const items = db.items.find({}).sort({
       _id: -1
    }).limit(2);
    
    const next = items[items.length - 1]._id
    res.json({ items, next })
    

    当用户想要获得第二个页面时,他们将光标(作为下一个)传递到 URL: curl https://api.mixmax.com/items?limit=2&next=590e9abd4abbf1165862d342

    const items = db.items.find({
      _id: { $lt: req.query.next }
    }).sort({
       _id: -1
    }).limit(2);
    
    const next = items[items.length - 1]._id
    res.json({ items, next })
    

    如果我们想以不同的顺序返回结果,例如项目的日期,那么我们将在查询字符串中添加sort=launchDatecurl https://api.mixmax.com/items?limit=2&sort=launchDate

    const items = db.items.find({}).sort({
       launchDate: -1
    }).limit(2);
    
    const next = items[items.length - 1].launchDate;
    res.json({ items, next })
    

    后续页面请求
    curl https://api.mixmax.com/items?limit=2&sort=launchDate&next=2017-09-11T00%3A44%3A54.036Z

    const items = db.items.find({
      launchDate: { $lt: req.query.next }
    }).sort({
       _id: -1
    }).limit(2);
    
    const next = items[items.length - 1].launchDate;
    res.json({ items, next });
    

    如果我们在同一天同一时间推出了一堆项目?现在我们的 launchDate 字段不再是唯一的,并且不满足 Unique、Orderable 和 Immutable。健康)状况。我们不能将它用作游标字段。但是我们可以使用两个字段来生成游标。既然我们知道 MongoDB 中的 _id 字段总是满足上述三个条件,我们知道如果我们将它与 launchDate 字段一起使用,这两个字段的组合将满足要求,可以一起用作游标字段。 curl https://api.mixmax.com/items?limit=2&sort=launchDate

    const items = db.items.find({}).sort({
       launchDate: -1,
      _id: -1 // secondary sort in case there are duplicate launchDate values
    }).limit(2);
    
    const lastItem = items[items.length - 1];
    // The cursor is a concatenation of the two cursor fields, since both are needed to satisfy the requirements of being a cursor field
    const next = `${lastItem.launchDate}_${lastItem._id}`;
    res.json({ items, next });
    

    后续页面请求
    curl https://api.mixmax.com/items?limit=2&sort=launchDate&next=2017-09-11T00%3A44%3A54.036Z_590e9abd4abbf1165862d342

    const [nextLaunchDate, nextId] = req.query.next.split(‘_’);
    const items = db.items.find({
      $or: [{
        launchDate: { $lt: nextLaunchDate }
      }, {
        // If the launchDate is an exact match, we need a tiebreaker, so we use the _id field from the cursor.
        launchDate: nextLaunchDate,
      _id: { $lt: nextId }
      }]
    }).sort({
       _id: -1
    }).limit(2);
    
    const lastItem = items[items.length - 1];
    // The cursor is a concatenation of the two cursor fields, since both are needed to satisfy the requirements of being a cursor field
    const next = `${lastItem.launchDate}_${lastItem._id}`;
    res.json({ items, next });
    

    参考:https://engineering.mixmax.com/blog/api-paging-built-the-right-way/

    【讨论】:

    • 这也称为键集分页,请在此处了解更多信息:use-the-index-luke.com/no-offset 它通常具有更高的性能和一致性,但应该说,它有一些限制,偏移分页方法没有' t,一个重要的存在,你不能导航到任意页面。
    【解决方案2】:

    使用推荐的分页方法与limit() 和skip() (see here):

    const MongoClient = require('mongodb').MongoClient;
    MongoClient.connect('http:localhost:27017').then((client) => {
        const db = client.db(mongo.db);
        db.collection('my-collection').find({}, {limit:10, skip:0}).then((documents) => {
            //First 10 documents
            console.log(documents);
        });
    
    
        db.collection('my-collection').find({}, {limit:10, skip:10}).then((documents) => {
            //Documents 11 to 20
            console.log(documents);
        });
    });
    

    这是一个分页功能:

    function studentsPerPage (pageNumber, nPerPage) {
        return db.collection('students').find({}, 
            {
                limit: nPerPage, 
                skip: pageNumber > 0 ? ( ( pageNumber - 1 ) * nPerPage ) : 0
            });
    }
    

    【讨论】:

    • 这是 mongo 文档中推荐的方法,这有点疯狂!在几乎任何数据库中,如果偏移量很大,skip 可能会有点危险,因为它需要扫描所有文档,直到您正在寻找的文档(值得称赞的是,文档确实提到了这一点)。我认为更好的模式可能是按 _id 字段排序,并传递您想要在之前获取文档的 _id -- {_id: {$lte: previousId}}
    • skip 方法虽然简单明了,但如果大多数人不打算翻阅大量文档,则可能不值得担心。
    【解决方案3】:

    我正在发送一个在 MongoDb 和 Nodejs 上的 API。

    module.exports.fetchLoans = function(req, res, next) {
        var perPage = 5;
        var page = req.body.page || 1;
        loans
          .find({ userId: req.user._id})
          .select("-emi")
          .skip(perPage * page - perPage)
          .limit(perPage)
          .sort({ timestamp: -1 })
          .exec(function(err, loan) {
            if (loan != null) {
              loans
                .find({ userId: req.user._id})
                .count()
                .exec(function(err, count) {
                  if (count != null) {
                    res.json({
                      success: true,
                      loans: loan,
                      currentpage: page,
                      totalpages: Math.ceil(count / perPage)
                    });
                  } else {
                    console.log("Milestone Error: ", err);
                    res.json({ success: false, error: "Internal Server Error. Please try again." });
                  }
                });
            } else {
              console.log("Milestone Error: ", err);
              res.json({ success: false, error: "Internal Server Error. Please try again." });
            }
          });
      };
    

    在此代码中,您必须在每次点击时提供页码。

    【讨论】:

      【解决方案4】:

      你可以使用skiplimit选项来实现分页

      module.exports = (data)=>{
      
        let page = parseInt(data.page);
        let limit = parseInt(data.limit);
        let skip = 0
      
        if(page>1){
         skip = (page * limit);
         }
      
      
      let mongoClient = require('mongodb').MongoClient;
          mongoClient.connect('mongodb://localhost:27017').then((client) => {
              let db = client.db('your-db');
              db.collection('your-collection').find({}, {limit:limit, skip:skip}).then((documents) => {
                  console.log(documents);
              });
      
          });
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-02-12
        • 1970-01-01
        • 2015-03-22
        相关资源
        最近更新 更多