Ce que vous voulez faire, c'est construire l'ensemble du pipeline en fonction des options fournies. C'est juste une structure de données après tout.
Vous testez également de manière incorrecte un "tableau" et vous devriez utiliser instanceof
car typeof
renverrait en fait "object"
et non "array"
.
De plus, vous voulez vraiment cette condition dans le premier phase de pipeline pour sélectionner de manière optimale les documents également, en plus d'être ajoutés après $unwind
si nécessaire :
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){
})
Étant donné un req
objet avec quelque chose de présent dans status
vous obtenez :
// 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"
}
}
}
]
Avec un tableau :
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"
}
}
}
]
Et sans rien :
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"
}
}
}
]
Ainsi, vous pouvez voir où les pièces sont conditionnellement incluses en fonction des valeurs fournies.
Utiliser $filtre
Vous devriez vraiment utiliser $filter
ici à la place. C'est beaucoup plus efficace que $unwind
et vous ne regroupez vraiment rien. Tout ce que vous voulez vraiment, ce sont des tableaux filtrés. C'est donc tout ce que vous ajoutez conditionnellement :
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 ]
}
}
}
}}]
: []
)
];
Le choix est entre $in
et $eq
pour le test de comparaison, en fonction de ce qui est fourni. Vous pouvez éventuellement envelopper le tout dans $setUnion
si vous "voulez vraiment dire" utiliser un "ensemble" dans le résultat. Mais il semble généralement que vous souhaitiez simplement "filtrer" les valeurs du tableau.
Avec les mêmes attentes d'une seule valeur :
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"
]
}
}
}
}
}
]
Un tableau :
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"
]
]
}
}
}
}
}
]
Ou rien :
var req = {
query: {
nameSlug: "Bill",
//status: ["A","B"]
},
};
/* 1 */
[
{
"$match" : {
"shop.nameSlug" : "Bill"
}
}
]
Où si vous n'avez pas besoin de filtrer, vous supprimez simplement l'étape de pipeline supplémentaire.