Hai bisogno di .aggregate()
metodo per "filtrare" qualsiasi contenuto dell'array per più di una singola corrispondenza, e anche la corrispondenza di base è molto più semplice poiché a MongoDB non importa che i dati si trovino all'interno degli array, purché il percorso specificato sia corretto:
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 } }}
])
Con PHP questo annota come segue:
$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 ) ) )
))
Il $map
l'operatore consente l'ispezione di ogni elemento dell'array esterno e passa ogni elemento a $cond
operazione ternaria. Questo elabora un $setIsSubset
operazione sull'array "inner" per vedere se contiene effettivamente uno dei valori nell'insieme alternativo (in questo caso [1]
) e dove un true
viene effettuata la valutazione, quindi l'elemento viene restituito o altrimenti false
.
Il punto di $setDifference
è rimuovere quei false
valori dall'array modificato e restituiscono solo elementi corrispondenti. E infine il $exists
test cerca di vedere che l'array esterno ha effettivamente almeno un elemento e non è vuoto come risultato del filtro.
I documenti restituiti sono quelli con la condizione di corrispondenza e solo gli elementi dell'array che soddisfano anche la condizione specificata.
Ovviamente gli operatori qui richiedono che tu abbia almeno MongoDB 2.6 come server (che ora è una versione piuttosto vecchia e almeno un aggiornamento consigliato) ma se hai ancora una versione inferiore, allora hai bisogno di un approccio tradizionale con $unwind
e $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' )
)
)
))