【问题标题】:Get distinct records with specified fields that match a value, paginated获取具有与值匹配的指定字段的不同记录,分页
【发布时间】:2016-03-20 15:03:57
【问题描述】:

我正在尝试获取我的 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 字段。

有什么想法吗?

【问题讨论】:

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


    【解决方案1】:

    你需要了解的关于聚合管道的事情通常是“管道”这个词,意思是每个阶段只接收前一个阶段按执行顺序发出的输入。在这里可以想到的最佳模拟是“unix pipe”|,其中一个命令的输出“通过管道”传送到另一个:

    ps aux | grep mongo | tee out.txt
    

    所以聚合管道的工作方式与此大致相同,其中要考虑的另一个主要问题是 $project$group 阶段仅在发出您要求的那些字段上运行,而没有其他字段。与 SQL 等声明式方法相比,这需要一点时间来适应,但稍加练习就会成为第二天性。

    其他要习惯的事情是像$match 这样的阶段比字段选择更重要的是放置在管道的开头。其主要原因是可能的索引选择和使用,这极大地加快了速度。此外,$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 的主要事情是除_id(这是分组键)之外的所有其他字段都需要使用accumulator 来选择,因为实际上总是存在多次出现分组键的值。

    在这种情况下,我们使用$first 作为累加器,它将从分组边界获取第一次出现。这通常在$sort 之后使用,但不必如此,只要您了解所选内容的行为即可。

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

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

    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) {}
    );
    

    这将获取文档中所有字段的副本并将它们放在命名键(在本例中为“文档”)下。这是一种更短的注释方式,但是生成的文档当然具有不同的结构,现在都作为子字段在一个键下。

    但只要你了解“管道”的基本原理,并且不将你想在后期使用的数据排除在前面的阶段之外,那么你通常应该没问题。

    【讨论】:

    • 超级有帮助的布雷克斯。也为解释 +1!
    猜你喜欢
    • 2020-09-13
    • 2020-08-19
    • 2015-06-09
    • 2013-08-23
    • 1970-01-01
    • 2017-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多