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

Übereinstimmungsbedingung für aggregierte Anzahl von Array-Mitgliedern

Der Fehler liegt daran, dass es kein Array mehr ist, nachdem Sie $unwind und daher kein gültiges Argument mehr für $size .

Sie scheinen zu versuchen, einige vorhandene Antworten "zusammenzuführen", ohne zu verstehen, was sie tun. Was Sie hier wirklich wollen, ist $filter und $size

db.collection.aggregate([
  { "$project": {
    "total": {
      "$size": {
        "$filter": {
          "input": "$Array",
          "cond": { "$eq": [ "$$this.field1", "a" ] }
        }
      }
    }
  }}
])

Oder „erfinden Sie das Rad neu“ mit $reduce :

db.collection.aggregate([
  { "$project": {
    "total": {
      "$reduce": {
        "input": "$Array",
        "initialValue": 0,
        "in": {
          "$sum": [
            "$$value", 
            { "$cond": [{ "$eq": [ "$$this.field1", "a" ] }, 1, 0] }
        }
      }
    }
  }}
])

Oder für das, was Sie mit $unwind/ machen wollten. Code> , Sie tatsächlich $group erneut, um zu "zählen", wie viele Übereinstimmungen es gab:

db.collection.aggregate([
  { "$unwind": "$Array" },
  { "$match": { "Array.field1": "a" } },
  { "$group": {
    "_id": "$_id",
    "total": { "$sum": 1 }
  }}
])

Die ersten beiden Formen sind die "optimalen" für moderne MongoDB-Umgebungen. Die endgültige Form mit $unwind und $group ist ein "Legacy"-Konstrukt, das für diese Art von Operation seit MongoDB 2.6 wirklich nicht mehr notwendig war, wenn auch mit einigen leicht anderen Operatoren.

In den ersten beiden vergleichen wir im Wesentlichen das field1 Wert jedes Array-Elements, solange es noch ein Array ist. Beide $filter und $reduce sind moderne Operatoren, die dafür ausgelegt sind, mit einem vorhandenen Array zu arbeiten. Derselbe Vergleich wird bei jedem unter Verwendung der Aggregation $eq durchgeführt -Operator, der einen booleschen Wert zurückgibt, je nachdem, ob die angegebenen Argumente "gleich" sind oder nicht. In diesem Fall auf jedem Arraymitglied auf den erwarteten Wert von "a" .

Im Fall von $filter , bleibt das Array tatsächlich intakt, mit Ausnahme von Elementen, die die angegebene Bedingung in "cond" nicht erfüllt haben werden aus dem Array entfernt. Da wir immer noch ein "Array" als Ausgabe haben, können wir dann den $size -Operator, um die Anzahl der Array-Elemente zu messen, die nach Verarbeitung dieser Filterbedingung übrig geblieben sind.

Der $reduce arbeitet dagegen die Array-Elemente durch und liefert über jedes Element einen Ausdruck und einen gespeicherten "Akkumulator"-Wert, den wir mit "initialValue" initialisiert haben . In diesem Fall dasselbe $eq Der Test wird innerhalb von $cond angewendet Operator. Dies ist ein "ternäres" oder if/then/else Bedingungsoperator, der es einem getesteten Ausdruck ermöglicht, der einen booleschen Wert zurückgibt, um den then zurückzugeben Wert, wenn true oder das else Wert, wenn false .

In diesem Ausdruck geben wir 1 zurück oder 0 und liefern das Gesamtergebnis der Addition dieses zurückgegebenen Werts und des aktuellen "Akkumulators" "$$value" mit dem $sum Operator, um diese zu addieren.

Das endgültige Formular verwendete $unwind auf dem Array. Was dies tatsächlich tut, ist die Array-Mitglieder zu dekonstruieren, um ein "neues Dokument" für jedes Array-Mitglied und seine zugehörigen übergeordneten Felder im Originaldokument zu erstellen. Dies "kopiert" effektiv das Hauptdokument für jedes Arraymitglied.

Sobald Sie $unwind die Struktur der Dokumente wird in eine "flachere" Form geändert. Aus diesem Grund können Sie dann den nachfolgenden $match Pipeline-Phase, um die nicht abgeglichenen Dokumente zu entfernen.

Dies bringt uns zu $group die angewendet wird, um alle Informationen, die sich auf einen gemeinsamen Schlüssel beziehen, "wieder zusammenzubringen". In diesem Fall ist es die _id -Feld des Originaldokuments, das natürlich in jedes vom $unwind . Wenn wir zu diesem "gemeinsamen Schlüssel" als einzelnes Dokument zurückkehren, können wir die verbleibenden "Dokumente" "zählen", die aus dem Array extrahiert wurden, indem wir $sum Akku.

Wenn wir das verbleibende „Array“ zurückhaben wollten, können Sie $push und bauen Sie das Array nur mit den verbleibenden Mitgliedern neu auf:

  { "$group": {
    "_id": "$_id",
    "Array": { "$push": "$Array" },
    "total": { "$sum": 1 }
  }}

Aber natürlich anstatt $size zu verwenden in einer anderen Pipeline-Phase können wir einfach noch „zählen“, wie wir es bereits mit dem $summe