检索包含与查询匹配的文档子集的文档以及匹配的总数

时间:2014-07-03 03:47:15

标签: javascript node.js mongodb mongoose aggregation-framework

如何查询集合以检索与查询匹配的有限数量的文档以及同时匹配所述查询的文档数量。

我正在寻找像这样的结果:

{
    result : [
        {
            _id:null,
            total:734, //there are 734 documents matching the query
            page:[ //limited to 3
                {_id:"...", someValue:30000}
                {_id:"...", someValue:30400}
                {_id:"...", someValue:31900}
            ]
        }
    ]
}

我正试图将其用于寻呼目的。

1 个答案:

答案 0 :(得分:2)

为了进一步解释,您似乎要尝试做的是使用聚合框架实现具有总结果计数的“分页”。这不是一个好主意,所以我会告诉你原因:

db.collection.aggregate([
    // Some query to match results
    { "$match": { /* some query */ } },

    // Then group to get the count -- OOPS! 
    { "$group": {
        "_id": null,
        "page": { "$push": /* the documents */ }, // essentially the problem
        "total": { "$sum": 1 }
    }},

    // Then unwind -- oh dear!
    { "$unwind": "$page" },

    // Then skip and limit
    { "$skip": 100 },
    { "$limit": 25 }

    // And group back again :-<
    { "$group": {
        "_id": null,
        "total": { "$first": "$total" },
        "page": { "$push": "$page" }
    }}
])

除了所有管道处理之外,真正可怕的是你将每个结果“推”到一个数组上。你必须得到总数。

然后,聚合框架中的数组也没有“切片”的真实概念。您需要做的就是坚持$skip$limit管道阶段,以获得结果的“当前页面”。

没有其他方法可以做到这一点,当然除了意识到聚合框架本身不是这个的工具。您想要执行一个查询来计算总结果,而另一个查询用于当前结果页面。这就是我们多年来一直使用SQL数据库的方式。

使用mongoose时需要注意一些事项:

// Avoiding the indentation creep

var queryDoc = {};
async.waterfall(
    [
        function(callback) {
            Model.count(queryDoc,function(err,count) {
                callback(err,count);
            }
        },
        function(count,callback) {
            var query = Model.find(queryDoc);
            query.skip(100).limit(25).exec(function(err,page) {
                callback(err,{ "total": count, "page": page });
            });
        }
    ],
    function(err, result) { 
        if (err) throw err; // or handle
        console.log( result ); // or res.send or whatever
    }
);

理想情况下,您确实应该查看范围查询,而不是使用“跳过”和“限制”,或者通常只是查找最后“看到”值为“大于”仅用于前向分页。

如果必须为查询结果使用聚合框架,那么很好,但是以相同的方式执行。试图获得总计数然后“分页”结果是非常可怕的。