有条件地包含聚合stream水线阶段

我有一个函数,根据给定的参数给我一些命令。 但是,参数可以是空的,在这种情况下,我想单独离开$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正在search等于null的字段。 如何在match == null时删除$match

你要做的是根据提供的选项来build立整个pipe道。 它毕竟只是一个数据结构。

你也错误地testing了一个“数组”,你应该使用instanceof因为typeof实际上会返回"object"而不是"array"

此外,你真的希望在第一个stream水线阶段的条件,以最佳方式select文件,除了在$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 ] } } } }}] : [] ) ]; 

根据所提供的内容,比较testing的select在$in$eq之间。 你可以有select地把整个东西包装在$setUnion如果你真的想要在结果中使用“set”。 但是通常看起来就像你只是想将数据过滤掉。

与单一价值的期望相同:

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

如果你不需要过滤的话,那么你只需要删除额外的stream水线阶段。