J'ai une collection que je fais une agrégation et je l'ai essentiellement obtenu jusqu'à
{array:[1,2,3], value: 1},
{array:[1,2,3], value: 4}
Comment effectuer une correspondance d'agrégation pour vérifier si la valeur est dans le tableau? J'ai essayé d'utiliser {$match: {"array: {$in: ["$value"]}}}
mais il ne trouve rien.
Je voudrais que la sortie (si vous utilisez l'exemple ci-dessus) soit:
{array:[1,2,3], value:1}
Une légère variation basée sur la réponse de @ chridam:
db.test.aggregate([
{ "$unwind": "$array" },
{ "$group": {
_id: { "_id": "$_id", "value": "$value" },
array: { $Push: "$array" },
mcount: { $sum: {$cond: [{$eq: ["$value","$array"]},1,0]}}
}
},
{ $match: {mcount: {$gt: 0}}},
{ "$project": { "value": "$_id.value", "array": 1, "_id": 0 }}
])
L'idée est de $unwind
et $group
remonter le tableau, en comptant dans mcount
le nombre d'éléments correspondant à la valeur. Après cela, un simple $match
sur mcount > 0
éliminera les documents indésirables.
Comme indiqué, $where
est une bonne option lorsque vous n'avez pas besoin de poursuivre la logique dans le pipeline d'agrégation.
Mais si vous le faites ensuite, utilisez $redact
, avec $map
pour transformer la "valeur" en un tableau et utilisez $setIsSubSet
pour comparer. C’est le moyen le plus rapide de le faire car il n’est pas nécessaire de dupliquer les documents avec $unwind
:
db.collection.aggregate([
{ "$redact": {
"$cond": {
"if": { "$setIsSubset": [
{ "$map": {
"input": { "$literal": ["A"] },
"as": "a",
"in": "$value"
}},
"$array"
]},
"then": "$$KEEP",
"else": "$$Prune"
}
}}
])
L'opérateur de pipeline $redact
permet de traiter une condition logique entre $cond
et utilise les opérations spéciales $$KEEP
pour "conserver" le document contenant la condition logique. true ou $$Prune
pour "supprimer" le document où la condition était fausse.
Cela lui permet de fonctionner comme $project
avec un $match
ultérieur, mais dans une seule étape de pipeline qui est plus efficace.
Étant donné qu'il s'agit d'opérateurs natifs et non de JavaScript, il s'agit probablement du "moyen" le plus rapide d'effectuer votre correspondance. Donc, à condition que vous utilisiez une version de MongoDB 2.6 ou une version ultérieure, procédez comme suit pour comparer ces éléments dans votre document.
Un peu tard pour répondre mais cela présente une autre solution:
En utilisant addFields et en faisant une correspondance séparée, cela donne plus de flexibilité que la rédaction Vous pouvez exposer plusieurs champs, puis utiliser ensemble une autre logique de correspondance basée sur les résultats.
db.applications.aggregate([
{$addFields: {"containsValueInArray": {$cond:[{$setIsSubset: [["valueToMatch"], "$arrayToMatchIn"]},true,false]}}},
{$match: {"containsValueInArray":true}}
]);
Une approche plus efficace impliquerait un seul pipeline utilisant l'opérateur $redact
comme suit:
db.collection.aggregate([
{
"$redact": {
"$cond": [
{
"$setIsSubset": [
["$value"],
"$array"
]
},
"$$KEEP",
"$$Prune"
]
}
}
])
Pour les versions antérieures de MongoDB qui ne prennent pas en charge $redact
(versions <2.6), envisagez ce pipeline d'agrégation qui utilise l'opérateur $unwind
:
db.collection.aggregate([
{ "$unwind": "$array" },
{
"$project": {
"isInArray": {
"$cond": [
{ "$eq": [ "$array", "$value" ] },
1,
0
]
},
"value": 1,
"array": 1
}
},
{ "$sort": { "isInArray": -1 } },
{
"$group": {
"_id": {
"_id": "$_id",
"value": "$value"
},
"array": { "$Push": "$array" },
"isInArray": { "$first": "$isInArray" }
}
},
{ "$match": { "isInArray": 1 } },
{ "$project": { "value": "$_id.value", "array": 1, "_id": 0 } }
])
Vous pouvez utiliser une expression d'agrégation dans une requête standard dans la version 3.6.
db.collection_name.find({"$expr": {"$in": ["$value", "$array"]}})
Utilisation de l'agrégation:
Vous pouvez utiliser $match + $expr
dans la version actuelle de 3.6
.
db.collection_name.aggregate({"$match": {"$expr": {"$in": ["$value", "$array"]}}})
Vous pouvez essayer l'expression $redact + $in
dans la version 3.4
.
db.collection_name.aggregate({
"$redact": {
"$cond": [
{
"$in": [
"$value",
"$array"
]
},
"$$KEEP",
"$$Prune"
]
}
})
Vous pouvez utiliser $where
si l'agrégation n'est pas required
db.collection.find({ $where: function(){
return (this.array.indexOf(this.value) !== -1)}
})
Essayez la combinaison de $ eq et $ setIntersection
{$group :{
_id: "$id",
yourName : { $sum:
{ $cond :[
{$and : [
{$eq:[{$setIntersection : ["$someArrayField", ["$value"]] },["$value"]]}
]
},1,0]
}
} }