javascript - 有条件地包括聚合管道阶段

标签 javascript node.js mongodb aggregation-framework

我有一个函数可以根据给定的参数给我一些订单。 但是,参数可以为空,在那种情况下我想单独留下 $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

最佳答案

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

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

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

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"
        }
    }
]

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

关于javascript - 有条件地包括聚合管道阶段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46362486/

相关文章:

javascript - mocha - 检索 promise 测试的结果

javascript - 垂直菜单扩展不适用于动态内容

javascript - 为什么当我移动鼠标时,视频上方会出现海报?

node.js - Bower 在 Azure 部署上失败

java - Mongolab 身份验证失败的 Java

名称中带有空格的字段的 MongoDB $project

node.js - 在 Node.js 中将二进制数据读/写到 MongoDB

javascript - Vue 属性未在模板实例中定义

javascript - 如何向 javascript RegExp 对象添加选项

node.js - 如何替换 package.json 文件中的 *