Vous avez besoin du .aggregate()
afin de "filtrer" tout contenu de tableau pour plus d'une correspondance singulière, et la correspondance de base est également beaucoup plus simple car MongoDB ne se soucie pas du fait que les données se trouvent dans des tableaux, tant que le chemin spécifié est correct :
db.collection.aggregate([
{ "$match": { "data.userid": 1 } },
{ "$project": {
"data": {
"$setDifference": [
{ "$map": {
"input": "$data",
"as": "el",
"in": {
"$cond": [
{ "$setIsSubset": [ [1], "$$el.userid" ] },
"$$el",
false
]
}
}},
[false]
]
}
}},
{ "$match": { "data.0": { "$exists": true } }}
])
Avec PHP, cela se traduit comme suit :
$collection->aggregate(array(
array( '$match' => array( "data.userid" => 1 )),
array(
'$project' => array(
'data' => array(
'$setDifference' => array(
array(
'$map' => array(
'input' => '$data',
'as' => 'el',
'in' => array(
'$cond' => array(
array( '$setIsSubset' => array(array(1),'$$el.userid') ),
'$$el',
FALSE
)
)
)
),
array(FALSE)
)
)
)
),
array( '$match' => array( 'data.0' => array( '$exists' => TRUE ) ) )
))
Le $map
permet l'inspection de chaque élément du tableau externe et transmet chaque élément au $cond
opération ternaire. Cela traite un $setIsSubset
opération sur le tableau "interne" pour voir s'il contient en fait l'une des valeurs de l'ensemble alternatif (dans ce cas [1]
) et où un true
l'évaluation est faite puis l'élément est retourné ou sinon false
.
Le point de $setDifference
est de supprimer ces false
valeurs du tableau modifié et ne renvoient que les éléments correspondants. Et enfin le $exists
test regarde pour voir que le tableau externe a en fait au moins un élément et n'est pas vide à la suite du filtrage.
Les documents renvoyés sont ceux avec la condition correspondante et uniquement les éléments du tableau qui correspondent également à la condition spécifiée.
Bien sûr, les opérateurs ici exigent que vous ayez au moins MongoDB 2.6 comme serveur (qui est une version assez ancienne maintenant et une mise à jour conseillée au moins) mais si vous avez toujours une version inférieure, vous avez besoin d'une approche traditionnelle avec $unwind
et $group
:
$collection->aggregate(array(
array( '$match' => array( "data.userid" => 1 )),
array( '$unwind' => '$data' ),
array( '$match' => array( 'data.userid' => 1 )),
array(
'$group' => array(
'_id' => '$_id',
'data' => array( '$push' => '$data' )
)
)
))