So che questa domanda è vecchia, ma l'ho trovata su Google dopo aver risposto a una nuova domanda simile . Quindi ho pensato che questo meritasse lo stesso trattamento.
Puoi evitare il calo delle prestazioni di $where utilizzando aggregato invece:
db.example.aggregate([
// Use an index, which $where cannot to narrow down
{$match: { "comments.by": "Abe" }},
// De-normalize the Array
{$unwind: "$comments"},
// The order of the array is maintained, so just look for the $last by _id
{$group: { _id: "$_id", comments: {$last: "$comment"} }},
// Match only where that $last comment by `by.Abe`
{$match: { "comments.by": "Abe" }},
// Retain the original _id order
{$sort: { _id: 1 }}
])
E questo dovrebbe girare intorno a $dove dal momento che siamo stati in grado di restringere i documenti che aveva un commento di "Abe" in primo luogo. Come avvertito, $dove testerà tutti i documenti della raccolta e non utilizzerà mai un indice anche se è lì per essere utilizzato.
Naturalmente, puoi anche mantenere il documento originale utilizzando la tecnica descritta qui
inoltre, quindi tutto funzionerebbe proprio come un find()
.
Solo spunto di riflessione per chiunque lo trovi.
Aggiornamento per le versioni Modern MongoDB
Le versioni moderne hanno aggiunto $redact
espressione della pipeline nonché $arrayElemAt
(quest'ultimo a partire dalla 3.2, quindi sarebbe la versione minima qui) che in combinazione consentirebbe a un'espressione logica di ispezionare l'ultimo elemento di un array senza elaborare un $unwind
fase:
db.example.aggregate([
{ "$match": { "comments.by": "Abe" }},
{ "$redact": {
"$cond": {
"if": {
"$eq": [
{ "$arrayElemAt": [ "$comments.by", -1 ] },
"Abe"
]
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}}
])
La logica qui viene eseguita rispetto a $arrayElemAt
sta ottenendo l'ultimo indice dell'array -1
, che viene trasformato in un semplice array di valori nel "by"
proprietà tramite $map
. Ciò consente il confronto del singolo valore con il parametro richiesto, "Abe"
.
O anche un po' più moderno usando $expr
per MongoDB 3.6 e versioni successive:
db.example.find({
"comments.by": "Abe",
"$expr": {
"$eq": [
{ "$arrayElemAt": [ "$comments.by", -1 ] },
"Abe"
]
}
})
Questa sarebbe di gran lunga la soluzione più efficace per abbinare l'ultimo elemento all'interno di un array e in realtà dovrebbe sostituire l'uso di $where
nella maggior parte dei casi e soprattutto qui.