如何使用MongoDB聚合进行分页?

时间:2018-01-17 16:21:31

标签: mongodb pagination aggregation-framework

我想执行一个执行基本分页的聚合查询:

  1. 查找属于某个company_id
  2. 的所有订单
  3. order_number
  4. 对订单排序
  5. 计算文件总数
  6. 跳到例如文件编号100并传递其余文件
  7. 将文件数限制为例如2并将其传递给
  8. 通过从文档中返回计数和选定的几个字段来完成
  9. 以下是查询的细分:

    db.Order.collection.aggregate([
    

    找到所有匹配的文件:

      { '$match'    : { "company_id" : ObjectId("54c0...") } },
    

    对文件进行排序:

      { '$sort'     : { 'order_number' : -1 } },
    

    这会对文件进行计数并传递未经修改的文档,但我确定这样做是错误的,因为事情变得奇怪了:

      {
        '$group' : {
          '_id'     : null,
          'count'   : { '$sum' : 1 },
          'entries' : { '$push' : "$$ROOT" }
        }
      },
    

    这似乎跳过了一些文件:

      { "$skip"     : 100 },
    

    这应该限制文档,但它不会:

      { "$limit"    : 2 },
    

    这会返回计数,但它不会返回数组中的文档,而是返回包含每个字段的数组:

      { '$project'  : {
          'count'     : 1,
          'entries'   : {'_id' : "$entries._id", 'order_number' : "$entries.order_number"}
        }
      }
    ])
    

    结果如下:

    [
      { "_id" : null,
        "count" : 300,
        "entries" : [
          {
            "_id" : [ObjectId('5a5c...'), ObjectId('5a5c...')],
            "order_number" : ["4346", "4345"]
          },
          {
            "_id" : [ObjectId('5a5c...'), ObjectId('5a5c...')],
            "order_number" : ["4346", "4345"]
          },
          ...
        ]
      }
    ]
    

    我在哪里弄错了?

1 个答案:

答案 0 :(得分:10)

要计算总计返回子集,您需要对同一数据集应用分组和跳过/限制。为此,您可以使用facets

例如,要显示第3页,每页10个文档:

db.Order.aggregate([
    { '$match'    : { "company_id" : ObjectId("54c0...") } },
    { '$sort'     : { 'order_number' : -1 } },
    { '$facet'    : {
        metadata: [ { $count: "total" }, { $addFields: { page: NumberInt(3) } } ],
        data: [ { $skip: 20 }, { $limit: 10 } ] // add projection here wish you re-shape the docs
    } }
] )

它将返回包含2个字段的单个文档:

{
    "metadata" : [ 
        {
            "total" : 300,
            "page" : 3
        }
    ],
    "data" : [ 
        {
            ... original document ...
        }, 
        {
            ... another document ...
        }, 
        {
            ... etc up to 10 docs ...
        }
    ]
}