Il chilometraggio può variare su questo e potrebbe benissimo risultare che "attualmente" il processo che stai seguendo risulta essere almeno "più adatto". Ma probabilmente possiamo fare in modo più efficiente.
Cosa potresti fare ora
A condizione che i tuoi array siano già "ordinati" tramite $sort
modificatore con $push
, allora probabilmente puoi farlo:
db.somedb.find(
{
"partn.is_partner": true,
"$where": function() {
return this.partn.slice(-1)[0].is_partner == true;
}
},
{ "partn": { "$slice": -1 } }
)
Quindi, purché partn,is_partner
è "indicizzato" questo è ancora abbastanza efficiente in quanto la condizione di query iniziale può essere soddisfatta utilizzando un indice. La parte che non può essere utilizzata è $where
clausola qui che utilizza la valutazione JavaScript.
Ma cos'è quella seconda parte nel $where
sta facendo semplicemente "tagliare" l'ultimo elemento dall'array e testarne il valore di is_partner
proprietà per vedere se è vero. Solo se viene soddisfatta anche tale condizione, il documento viene restituito.
C'è anche il $slice
operatore di proiezione. Questo fa la stessa cosa nel restituire l'ultimo elemento dall'array. Le false corrispondenze sono già filtrate, quindi questo mostra solo l'ultimo elemento dove è vero.
Combinato con l'indice come menzionato, questo dovrebbe essere abbastanza veloce dato che i documenti sono già stati selezionati e la condizione JavaScript filtra solo il resto. Nota che senza un altro campo con una condizione di query standard da corrispondere, un $where
la clausola non può utilizzare un indice. Quindi cerca sempre di usare "con parsimonia" con altre condizioni di query in atto.
Cosa puoi fare in futuro
Next Up, anche se non disponibile al momento della scrittura, ma sicuramente nel prossimo futuro sarà il $slice
operatore per il framework di aggregazione. Questo è attualmente nel ramo di sviluppo, ma ecco una sbirciatina a come funziona:
db.somedb.aggregate([
{ "$match": { "partn.is_partner": true } },
{ "$redact": {
"$cond": {
"if": {
"$anyElementTrue": {
"$map": {
"input": { "$slice": ["$partn",-1] },
"as": "el",
"in": "$$el.is_partner"
}
}
},
"then": "$$KEEP",
"else": "$$PRUNE"
}
}},
{ "$project": {
"partn": { "$slice": [ "$partn",-1 ] }
}}
])
Combinando quel $slice
all'interno di un $redact
la fase qui consente di filtrare i documenti con una condizione logica, testando il documento. In questo caso il $slice
produce un singolo elemento array che viene inviato a $map
per estrarre solo il singolo is_partner
value (sempre come array). Poiché nella migliore delle ipotesi si tratta ancora di un array a elemento singolo, l'altro test è $anyElementTrue
il che rende questo un risultato booleano singolare, adatto per $cond
.
Il $redact
qui decide su quel risultato se $$KEEP
o $$PRUNE
il documento dai risultati. Successivamente utilizziamo $slice
di nuovo nel progetto per restituire solo l'ultimo elemento dell'array dopo il filtraggio.
Risulta essere più o meno esattamente ciò che fa la versione JavaScript, con l'eccezione che utilizza tutti gli operatori codificati nativi e quindi dovrebbe essere un po' più veloce dell'alternativa JavaScript.
Entrambi i moduli restituiscono il tuo primo documento come previsto:
{
"_id" : 0,
"partn" : [
{
"date" : ISODate("2015-07-28T00:59:14.963Z"),
"is_partner" : true
},
{
"date" : ISODate("2015-07-28T01:00:32.771Z"),
"is_partner" : false
},
{
"date" : ISODate("2015-07-28T01:15:29.916Z"),
"is_partner" : true
},
{
"date" : ISODate("2015-08-05T13:48:07.035Z"),
"is_partner" : false
},
{
"date" : ISODate("2015-08-05T13:50:56.482Z"),
"is_partner" : true
}
]
}
Il grosso problema qui con entrambi è che il tuo array deve essere già ordinato, quindi l'ultima data è prima. Senza quello, allora hai bisogno del framework di aggregazione per $sort
l'array, proprio come stai facendo ora.
Non molto efficiente, ecco perché dovresti "preordinare" il tuo array e mantenere l'ordine ad ogni aggiornamento.
Come trucco pratico, questo riordinerà effettivamente tutti gli elementi dell'array in tutti i documenti di raccolta in una semplice istruzione:
db.somedb.update(
{},
{ "$push": {
"partn": { "$each": [], "$sort": { "date": 1 } }
}},
{ "multi": true }
)
Quindi, anche se non stai "spingendo" un nuovo elemento in un array e stai semplicemente aggiornando una proprietà, puoi sempre applicare quel costrutto di base per mantenere l'array ordinato come lo desideri.
Vale la pena considerare perché dovrebbe rendere le cose molto più veloci.