获取具有匹配值的指定字段的不同记录,并进行分页

时间:2015-12-14 23:59:01

标签: mongodb mongoose pagination mongodb-query aggregation-framework

我试图获取MongoDB集合中的所有文档

  • 由不同的客户ID(custID)
  • 其中状态代码== 200
  • 分页(跳过并限制)
  • 返回指定字段

var Order = mongoose.model(' Order',orderSchema());

我原来的想法是使用mongoose db query,但您不能将distinctskip and limit一起用作Distinct is a method that returns an "array", and therefore you cannot modify something that is not a "Cursor"

    Order
        .distinct('request.headers.custID')
        .where('response.status.code').equals(200)
        .limit(limit)
        .skip(skip)
        .exec(function (err, orders) {      
            callback({
                data: orders
            });
        });

然后我想使用Aggregate,使用$group获取不同的customerID记录,$match返回状态代码为200的所有唯一customerID记录,$project包括我想要的字段:

    Order.aggregate(
        [
            {
                "$project" :
                {
                    'request.headers.custID' : 1,
                    //other fields to include
                }
            },
            {
                "$match" :
                {
                    "response.status.code" : 200
                }
            },
            {
                "$group": {
                    "_id": "$request.headers.custID"
                }
            },
            {
                "$skip": skip
            },
            {
                "$limit": limit
            }
        ],
        function (err, order) {}
    );

这会返回一个空数组。如果我删除project,则只返回$request.headers.custID字段,而实际上我需要更多字段。

有什么想法吗?

1 个答案:

答案 0 :(得分:3)

关于聚合管道,您需要了解的事情通常是"管道"表示每个阶段仅按执行顺序接收前一阶段发出的输入。这里想到的最好的模拟是" unix pipe" $(".pov_icon_small").on('click',function(){ $(this).parent().siblings().children('div').each(function(value){ if($(this).hasClass('icon')) $(this).addClass('pov_icon_small').removeClass('pov_icon_large'); else if($(this).hasClass('title')) $(this).addClass('pov_title_small').removeClass('pov_title_large'); }); $(this).addClass('pov_icon_large').removeClass('pov_icon_small'); $(this).siblings('.title').addClass('pov_title_large').removeClass('pov_title_small'); }); ,其中一个命令的输出为"管道输出"对方:

|

因此聚合管道的工作方式与此类似,其中要考虑的另一个主要问题是$project$group阶段仅在发出您要求的字段时运行,而不是其他字段。与SQL等声明性方法相比,这需要一点点习惯,但通过一些实践,它就变成了第二天性。

其他需要考虑的事项是像$match这样的阶段比在字段选择中放置在管道的开头更重要。这样做的主要原因是可能的索引选择和使用,这极大地加快了速度。此外,ps aux | grep mongo | tee out.txt 后跟$project的字段选择有点多余,因为无论如何都基本上都是选择字段,并且通常最适合组合。

因此,最优秀的是:

$group

此处要记住的关于Order.aggregate( [ { "$match" : { "response.status.code" : 200 }}, { "$group": { "_id": "$request.headers.custID", // the grouping key "otherField": { "$first": "$otherField" }, // and so on for each field to select }}, { "$skip": skip }, { "$limit": limit } ], function (err, order) {} ); 的主要内容是除了$group之外的所有其他字段(这是分组键)需要使用accumulator来选择,因为有_id实际上,分组键的值总是多次出现。

在这种情况下,我们使用$first作为累加器,它将首先从分组边界出现。通常这是在$sort之后使用,但不一定是这样,只要你理解所选内容的行为。

$max之类的其他累加器只是从分组键内的值中取出字段的最大值,因此独立于"当前记录/文档"与$first$last不同。所以这一切都取决于你的需求。

当然,您可以使用$$ROOT变量在MongoDB 2.6之后缩小现代MongoDB版本中的选择:

Order.aggregate(
    [
        { "$match" : {
            "response.status.code" : 200
        }},
        { "$group": {
           "_id": "$request.headers.custID",  // the grouping key
           "document": { "$first": "$$ROOT" }
        }},
        { "$skip": skip },
        { "$limit": limit }
    ],
    function (err, order) {}
);

这将获取文档中所有字段的副本并将它们放在命名键下(在这种情况下为"文档")。这是一种较短的标记方式,但当然结果文档具有不同的结构,现在所有文档都在一个键下作为子字段。

但只要您了解管道的基本原则"并且不要在之前的阶段排除您想要在以后阶段使用的数据,那么您通常应该没问题。