Die Laufleistung kann davon abweichen, und es kann sich durchaus herausstellen, dass der von Ihnen verfolgte Prozess „derzeit“ zumindest als „am besten geeignet“ funktioniert. Aber wir können wahrscheinlich effizienter arbeiten.
Was Sie jetzt tun könnten
Vorausgesetzt, Ihre Arrays sind bereits mithilfe des Codes <"sortiert".>$sortieren
Modifikator mit $push
, dann können Sie wahrscheinlich Folgendes tun:
db.somedb.find(
{
"partn.is_partner": true,
"$where": function() {
return this.partn.slice(-1)[0].is_partner == true;
}
},
{ "partn": { "$slice": -1 } }
)
Also solange partn,is_partner
"indiziert" ist, ist dies immer noch ziemlich effizient, da diese anfängliche Abfragebedingung mit einem Index erfüllt werden kann. Der Teil, der das nicht kann, ist $where
Klausel hier, die JavaScript-Auswertung verwendet.
Aber was ist der zweite Teil im $where
Dabei wird einfach das letzte Element aus dem Array "geschnitten" und der Wert von is_partner
getestet Eigenschaft, um zu sehen, ob es wahr ist. Nur wenn auch diese Bedingung erfüllt ist, wird das Dokument zurückgegeben.
Es gibt auch den $slice
Projektionsoperator. Dies bewirkt dasselbe bei der Rückgabe des letzten Elements aus dem Array. Falsche Übereinstimmungen werden bereits gefiltert, also zeigt dies nur das letzte Element, wo es wahr ist.
In Kombination mit dem erwähnten Index sollte dies ziemlich schnell gehen, da die Dokumente bereits ausgewählt wurden und die JavaScript-Bedingung nur den Rest filtert. Beachten Sie, dass ohne ein weiteres Feld mit einer Standardabfragebedingung ein $where
übereinstimmen muss -Klausel kann keinen Index verwenden. Versuchen Sie also immer, "sparsam" mit anderen Abfragebedingungen zu verwenden.
Was Sie in Zukunft tun können
Als Nächstes steht $slice
an, obwohl es zum Zeitpunkt des Verfassens dieses Artikels noch nicht verfügbar ist, aber sicherlich in naher Zukunft Operator für das Aggregationsframework. Dies befindet sich derzeit im Entwicklungszweig, aber hier ist ein Blick darauf, wie es funktioniert:
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 ] }
}}
])
Kombinieren dieses $slice
innerhalb eines $redact
Hier können die Dokumente mit einer logischen Bedingung gefiltert werden, wodurch das Dokument getestet wird. In diesem Fall das $slice
erzeugt ein Einzelelement-Array, das an $ gesendet wird Karte
um nur den einzelnen is_partner
zu extrahieren value (immer noch als Array). Da dies bestenfalls immer noch ein Einzelelement-Array ist, lautet der andere Test $anyElementTrue
was dies zu einem singulären booleschen Ergebnis macht, geeignet für $cond
.
Der $redact
hier entscheidet das Ergebnis ob $$KEEP
oder $$PRUNE
das Dokument aus den Ergebnissen. Später verwenden wir $slice
erneut im Projekt, um nur das letzte Element des Arrays nach der Filterung zurückzugeben.
Das funktioniert ziemlich genau wie die JavaScript-Version, mit der Ausnahme, dass diese alle nativen codierten Operatoren verwendet und daher etwas schneller sein sollte als die JavaScript-Alternative.
Beide Formen geben Ihr erstes Dokument wie erwartet zurück:
{
"_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
}
]
}
Der große Haken bei beiden ist, dass Ihr Array bereits sortiert sein muss, damit das neueste Datum zuerst kommt. Ohne das benötigen Sie das Aggregationsframework für $sort
das Array, so wie Sie es jetzt tun.
Nicht wirklich effizient, deshalb sollten Sie Ihr Array "vorsortieren" und die Reihenfolge bei jedem Update beibehalten.
Als praktischer Trick ordnet dies tatsächlich alle Array-Elemente in allen Sammlungsdokumenten in einer einfachen Anweisung neu:
db.somedb.update(
{},
{ "$push": {
"partn": { "$each": [], "$sort": { "date": 1 } }
}},
{ "multi": true }
)
Selbst wenn Sie also kein neues Element in ein Array "schieben" und nur eine Eigenschaft aktualisieren, können Sie immer dieses grundlegende Konstrukt anwenden, um das Array so zu ordnen, wie Sie es möchten.
Eine Überlegung wert, da es die Dinge viel schneller machen sollte.