Kilometerstand kan hierop variëren en het kan best zijn dat "momenteel" het proces dat u volgt op zijn minst "het meest geschikt" blijkt te zijn. Maar we kunnen waarschijnlijk efficiënter.
Wat u nu zou kunnen doen
Op voorwaarde dat uw arrays al zijn "gesorteerd" met behulp van de $sort
modifier met $push
, dan kunt u waarschijnlijk dit doen:
db.somedb.find(
{
"partn.is_partner": true,
"$where": function() {
return this.partn.slice(-1)[0].is_partner == true;
}
},
{ "partn": { "$slice": -1 } }
)
Dus zolang partn,is_partner
is "geïndexeerd", dit is nog steeds behoorlijk efficiënt omdat aan die initiële vraagvoorwaarde kan worden voldaan met behulp van een index. Het deel dat dat niet kan is de $where
clausule hier die JavaScript-evaluatie gebruikt.
Maar wat dat tweede deel in de $where
doet, is simpelweg het laatste element uit de array "snijden" en de waarde van de is_partner
testen eigendom om te zien of het waar is. Alleen als ook aan die voorwaarde is voldaan, wordt het document geretourneerd.
Er is ook de $slice
projectie-operator. Dit doet hetzelfde bij het retourneren van het laatste element uit de array. Valse overeenkomsten zijn al gefilterd, dus dit toont alleen het laatste element waar waar is.
Gecombineerd met de index zoals vermeld, zou dit vrij snel moeten zijn, aangezien de documenten al zijn geselecteerd en de JavaScript-voorwaarde alleen de rest filtert. Merk op dat zonder een ander veld met een standaard zoekvoorwaarde die overeenkomt, een $where
clausule kan geen index gebruiken. Probeer dus altijd "spaarzaam" te gebruiken met andere zoekvoorwaarden.
Wat u in de toekomst kunt doen
Next Up, hoewel niet beschikbaar op het moment van schrijven, maar zeker in de nabije toekomst zal de $slice
zijn operator voor het aggregatieraamwerk. Dit is momenteel in de ontwikkelbranche, maar hier is een kijkje in hoe het werkt:
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 ] }
}}
])
Die $slice
. combineren binnen een $redact
In de fase hier kunnen de documenten worden gefilterd met een logische voorwaarde, waardoor het document wordt getest. In dit geval de $slice
produceert een array met één element die wordt verzonden naar $map
om alleen de enkele is_partner
. te extraheren waarde ( nog steeds als een array ). Aangezien dit op zijn best nog steeds een array met één element is, is de andere test $anyElementTrue
waardoor dit een enkelvoudig booleaans resultaat is, geschikt voor $cond
.
De $redact
hier beslist over dat resultaat of $$KEEP
of $$PRUNE
het document uit de resultaten. Later gebruiken we $slice
opnieuw in het project om alleen het laatste element van de array na het filteren terug te geven.
Dat blijkt vrijwel precies te zijn wat de JavaScript-versie doet, behalve dat deze alle native gecodeerde operators gebruikt, en daarom een beetje sneller zou moeten zijn dan de JavaScript-alternatieven.
Beide formulieren retourneren uw eerste document zoals verwacht:
{
"_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
}
]
}
De grote vangst hier met beide is dat je array al moet zijn gesorteerd, dus de laatste datum is eerst. Zonder dat heb je het aggregatieraamwerk nodig om $sort
de array, net zoals je nu doet.
Niet echt efficiënt, daarom moet u uw array "voorsorteren" en de volgorde bij elke update behouden.
Als een handige truc, zal dit in feite alle array-elementen in alle collectiedocumenten opnieuw ordenen in één simpele instructie:
db.somedb.update(
{},
{ "$push": {
"partn": { "$each": [], "$sort": { "date": 1 } }
}},
{ "multi": true }
)
Dus zelfs als u geen nieuw element in een array "pusht" en alleen een eigenschap bijwerkt, kunt u altijd die basisconstructie toepassen om de array geordend te houden zoals u dat wilt.
Het overwegen waard, omdat het de zaken veel sneller zou moeten maken.