【问题标题】:Conditionally Include Aggregation Pipeline Stages有条件地包括聚合管道阶段
【发布时间】:2017-09-22 19:31:01
【问题描述】:

我有一个函数可以根据给定的参数给我一些订单。 但是,参数可以为空,在这种情况下,我想不理会$match

这是我目前拥有的代码:

if(req.query.status && typeof(req.query.status) == 'array'){
        var match = {
            $in: req.query.status
        };
    }else if(req.query.status){
        var match = req.query.status;
    }else{
        //when empty find all statuses
        var match = null;
    }


 Order.aggregate(
    {
        $match: {
            'shop.nameSlug' : req.query.nameSlug,
        }
    },
    {
        $unwind: "$status"
    },
    {
        $match: {
            "status.status" : match
        }
    },
    {
        $group: {
            _id: "$_id",
            status: {
                $addToSet: "$status"
            },
            number: {
                $first: "$number"
            },
            date: {
                $first: "$date"
            },
            comment: {
                $first: "$comment"
            }
        }
    }
).exec(function(err, orders){
})

match 为 null 时,不返回任何订单,因为 mongo 正在搜索等于 null 的字段。 match == null 时如何删除 $match

【问题讨论】:

    标签: javascript node.js mongodb aggregation-framework


    【解决方案1】:

    您的意思是根据提供的选项构建整个管道。毕竟它只是一个数据结构。

    您还错误地测试了“数组”,您应该使用instanceof,因为typeof 实际上会返回"object" 而不是"array"

    此外,您确实希望 first 管道阶段中的条件也能以最佳方式选择文档,除了在需要的地方添加到 $unwind 之后:

    var pipeline = [
      { $match: 
          Object.assign(
            { 'shop.nameSlug' : req.query.nameSlug },
            (req.query.status) 
              ? { "status.status": (req.query.status instanceof Array)
                ? { "$in": req.query.status } : req.query.status }
              : {}
          )
      },
      { $unwind: "$status" },
      ...(
        (req.query.status)
          ? [{ "$match": { 
              "status.status": (req.query.status instanceof Array)
               ? { "$in": req.query.status } : req.query.status
           }}]
          : []
        ),
        { $group: {
          _id: "$_id",
          status: { $addToSet: "$status" },
          number: { $first: "$number" },
          date: { $first: "$date" },
          comment: { $first: "$comment" }
        }}
    ];    
    
    
    Order.aggregate(pipeline).exec(function(err, orders){
    
    })
    

    给定一个req 对象,其中包含status 中的某些内容,您会得到:

    // Example stucture
    var req = {
      query: { 
       nameSlug: "Bill", 
       status: "A"
      },
    };
    
    // Pipeline output as:
    
    [
        {
            "$match" : {
                "shop.nameSlug" : "Bill",
                "status.status" : "A"
            }
        },
        {
            "$unwind" : "$status"
        },
        {
            "$match" : {
                "status.status" : "A"
            }
        },
        {
            "$group" : {
                "_id" : "$_id",
                "status" : {
                    "$addToSet" : "$status"
                },
                "number" : {
                    "$first" : "$number"
                },
                "date" : {
                    "$first" : "$date"
                },
                "comment" : {
                    "$first" : "$comment"
                }
            }
        }
    ]
    

    使用数组:

    var req = {
      query: { 
       nameSlug: "Bill", 
       status: ["A","B"]
      },
    };
    
    // Pipeline output as:
    [
        {
            "$match" : {
                "shop.nameSlug" : "Bill",
                "status.status" : {
                    "$in" : [ 
                        "A", 
                        "B"
                    ]
                }
            }
        },
        {
            "$unwind" : "$status"
        },
        {
            "$match" : {
                "status.status" : {
                    "$in" : [ 
                        "A", 
                        "B"
                    ]
                }
            }
        },
        {
            "$group" : {
                "_id" : "$_id",
                "status" : {
                    "$addToSet" : "$status"
                },
                "number" : {
                    "$first" : "$number"
                },
                "date" : {
                    "$first" : "$date"
                },
                "comment" : {
                    "$first" : "$comment"
                }
            }
        }
    ]
    

    一无所有:

    var req = {
      query: { 
       nameSlug: "Bill", 
       //status: ["A","B"]
      },
    };
    
    // Pipeline output as:
    [
        {
            "$match" : {
                "shop.nameSlug" : "Bill"
            }
        },
        {
            "$unwind" : "$status"
        },
        {
            "$group" : {
                "_id" : "$_id",
                "status" : {
                    "$addToSet" : "$status"
                },
                "number" : {
                    "$first" : "$number"
                },
                "date" : {
                    "$first" : "$date"
                },
                "comment" : {
                    "$first" : "$comment"
                }
            }
        }
    ]
    

    因此,您可以根据提供的值查看有条件地包含部件的位置。


    使用 $filter

    你真的应该在这里使用$filter。它比$unwind 高效得多,而且你真的没有对任何东西进行分组。您真正想要的只是过滤数组。这就是你有条件地添加的所有内容:

    var pipeline = [
      { $match: 
          Object.assign(
            { 'shop.nameSlug' : req.query.nameSlug },
            (req.query.status) 
              ? { "status.status": (req.query.status instanceof Array)
                ? { "$in": req.query.status } : req.query.status }
              : {}
          )
      },
      ...(
        (req.query.status)
          ? [{ "$addFields": { 
              "status": {
                "$filter": {
                  "input": "$status",
                  "cond": {
                    [(req.query.status instanceof Array) ? "$in" : "$eq"]:
                      [ "$$this.status", req.query.status ]
                  }
                }    
              }
           }}]
          : []
        )
    ];
    

    $in$eq 之间进行选择以进行比较测试,具体取决于所提供的内容。如果您“真的想”在结果中使用“集合”,则可以选择将整个内容包装在 $setUnion 中。但通常看起来您只是想从数组中“过滤”值。

    对单个值的期望相同:

    var req = {
      query: { 
       nameSlug: "Bill", 
       //status: ["A","B"]
       status: "A"
      },
    };
    
    /* 1 */
    [
        {
            "$match" : {
                "shop.nameSlug" : "Bill",
                "status.status" : "A"
            }
        },
        {
            "$addFields" : {
                "status" : {
                    "$filter" : {
                        "input" : "$status",
                        "cond" : {
                            "$eq" : [ 
                                "$$this.status", 
                                "A"
                            ]
                        }
                    }
                }
            }
        }
    ]
    

    一个数组:

    var req = {
      query: { 
       nameSlug: "Bill", 
       status: ["A","B"]
      },
    };
    
    /* 1 */
    [
        {
            "$match" : {
                "shop.nameSlug" : "Bill",
                "status.status" : {
                    "$in" : [ 
                        "A", 
                        "B"
                    ]
                }
            }
        },
        {
            "$addFields" : {
                "status" : {
                    "$filter" : {
                        "input" : "$status",
                        "cond" : {
                            "$in" : [ 
                                "$$this.status", 
                                [ 
                                    "A", 
                                    "B"
                                ]
                            ]
                        }
                    }
                }
            }
        }
    ]
    

    或者什么都没有:

    var req = {
      query: { 
       nameSlug: "Bill", 
       //status: ["A","B"]
      },
    };
    
    /* 1 */
    [
        {
            "$match" : {
                "shop.nameSlug" : "Bill"
            }
        }
    ]
    

    如果您不需要过滤,那么您只需删除额外的管道阶段。

    【讨论】:

    • w0w。 What.An.Answer!辉煌!我还不是 mongo / mongoose 的专家,所以$group 代码来自 SO 上的另一个答案 :) 我收到错误 MongoError: Unrecognized pipeline stage name: '$addFields' 我该如何解决?
    • @NVO 您需要 MongoDB 3.4 用于该管道阶段。但是您几乎可以简单地使用$project,只是您实际上需要明确命名所有您想要的字段,而不仅仅是“合并”的字段。就像您在$group 中所做的一样。但是你真的应该在任何高于 v3 的现代 MongoDB 版本中使用 $project$filter。你应该运行它,因为官方不再支持任何更少的东西。
    • 啊。我现在可以工作了。 $unwind 的原因是一个订单可以有多个状态,我只想检查最后一个,而不是第一个匹配,因为所有订单都是在发送前支付的,这导致当我想过滤时发送订单'付费”。
    • 聪明的展开运算符!我从不喜欢为聚合管道初始化特定变量
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-03
    • 1970-01-01
    • 2020-05-29
    • 2019-03-03
    • 2021-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多