MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Filtern Sie die Ergebnisse nach dem Wert des letzten Array-Eingabefelds

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.