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

Übereinstimmungsbedingungen und letztes Datum aus dem Array

Das Grundkonzept hier ist, dass Sie das Aggregationsframework benötigen, um Bedingungen anzuwenden, um die Array-Elemente auf die Bedingungen zu "filtern". Je nach verfügbarer Version können verschiedene Techniken angewendet werden.

In allen Fällen ist dies das Ergebnis:

{
    "_id" : ObjectId("593921425ccc8150f35e7664"),
    "user1" : 1,
    "user2" : 4,
    "messages" : {
            "sender" : 1,
            "datetime" : ISODate("2017-06-09T10:04:50Z"),
            "body" : "hiii 1"
    }
}
{
    "_id" : ObjectId("593921425ccc8150f35e7663"),
    "user1" : 1,
    "user2" : 3,
    "messages" : {
            "sender" : 1,
            "datetime" : ISODate("2017-06-10T10:04:50Z"),
            "body" : "hiii 2"
    }
}
{
    "_id" : ObjectId("593921425ccc8150f35e7662"),
    "user1" : 1,
    "user2" : 2,
    "messages" : {
            "sender" : 1,
            "datetime" : ISODate("2017-06-08T10:04:50Z"),
            "body" : "hiii 0"
    }
}

MongoDB 3.4 und höher

db.chat.aggregate([
  { "$match": { "messages.sender": 1 } },
  { "$replaceRoot": {
    "newRoot": {
      "$let": {
        "vars": {
          "messages": {
            "$filter": {
              "input": "$messages",
              "as": "m",
              "cond": { "$eq": [ "$$m.sender", 1 ] }
            }
          },
          "maxDate": {
            "$max": {
              "$map": {
                "input": {
                  "$filter": {
                    "input": "$messages",
                    "as": "m",
                    "cond": { "$eq": [ "$$m.sender", 1 ] }
                  }
                },
                "as": "m",
                "in": "$$m.datetime"
              }
            }
          }
        },
        "in": {
          "_id": "$_id",
          "user1": "$user1",
          "user2": "$user2",
          "messages": {
            "$arrayElemAt": [
              { "$filter": {
                "input": "$$messages",
                "as": "m",
                "cond": { "$eq": [ "$$m.datetime", "$$maxDate" ] }
              }},
              0
            ]
          }    
        }
      }
    }
  }}
])

Dies ist der effizienteste Weg, der $replaceRoot was es uns ermöglicht, Variablen zur Verwendung in der „ersetzten“ Struktur mit $let . Der Hauptvorteil hier ist, dass dies nur "zwei" Pipeline-Stufen erfordert.

Um den Inhalt des Arrays abzugleichen, verwenden Sie $filter wo Sie den $eq anwenden logische Operation, um den Wert von "sender" zu testen . Wenn die Bedingung zutrifft, werden nur die passenden Array-Einträge zurückgegeben.

Verwenden desselben $filter damit nur die passenden „sender“-Einträge berücksichtigt werden, wollen wir dann $max über die "gefilterte" Liste zu den Werten in "datetime" . Der $max ]5 Wert ist das "späteste" Datum laut Bedingungen.

Wir wollen diesen Wert, damit wir später die zurückgegebenen Ergebnisse aus dem "gefilterten" Array mit diesem "maxDate" vergleichen können. Was innerhalb des "in" passiert Block von $let wobei die beiden zuvor für den gefilterten Inhalt deklarierten „Variablen“ und das „maxDate“ erneut auf $filter um den einzigen Wert zurückzugeben, der beide Bedingungen erfüllt und auch das "neueste Datum" hat.

Da Sie nur „ein“ Ergebnis wollen, verwenden wir $arrayElemAt um den Wert anstelle des Arrays zu verwenden.

MongoDB 3.2

db.chat.aggregate([
  { "$match": { "messages.sender": 1 } },
  { "$project": {
    "user1": 1,
    "user2": 1,
    "messages": {
      "$filter": {
        "input": "$messages",
        "as": "m",
        "cond": { "$eq": [ "$$m.sender", 1 ] }
      }
    },
    "maxDate": {
      "$max": {
        "$map": {
          "input": {
            "$filter": {
              "input": "$messages",
              "as": "m",
              "cond": { "$eq": [ "$$m.sender", 1 ] }
            }
          },
          "as": "m",
          "in": "$$m.datetime"
        }
      }
    }         
  }},
  { "$project": {
    "user1": 1,
    "user2": 1,
    "messages": {
      "$arrayElemAt":[
       { "$filter": {
         "input": "$messages",
          "as": "m",
          "cond": { "$eq": [ "$$m.datetime", "$maxDate" ] }
       }},
       0
      ]
    }
  }}
])

Dies ist im Grunde der gleiche Vorgang wie beschrieben, jedoch ohne den $ root ersetzen Pipeline-Phase müssen wir uns in zwei $project Stufen. Der Grund dafür ist, dass wir den "berechneten Wert" von "maxDate" benötigen, um diesen letzten $filter , und es ist in einer zusammengesetzten Anweisung nicht möglich, also teilen wir stattdessen die Pipelines auf. Dies wirkt sich geringfügig auf die Gesamtkosten des Vorgangs aus.

In MongoDB 2.6 bis 3.0 können wir die meisten Techniken hier verwenden, mit Ausnahme von $arrayElemAt und akzeptieren Sie entweder das „Array“-Ergebnis mit einem einzigen Eintrag oder fügen Sie ein ein $unwind Phase, um mit dem fertig zu werden, was jetzt ein einziger Eintrag sein sollte.

Frühere MongoDB-Versionen

db.chat.aggregate([
  { "$match": { "messages.sender": 1 } },
  { "$unwind": "$messages" },
  { "$match": { "messages.sender": 1 } },
  { "$sort": { "_id": 1, "messages.datetime": -1 } },
  { "$group": {
    "_id": "$_id",
    "user1": { "$first": "$user1" },
    "user2": { "$first": "$user2" },
    "messages": { "$first": "$messages" }
  }}
])

Obwohl es kurz aussieht, ist dies bei weitem die kostspieligste Operation. Hier müssen Sie $unwind verwenden um die Bedingungen auf die Array-Elemente anzuwenden. Dies ist ein sehr kostspieliger Prozess, da er eine Kopie jedes Dokuments für jeden Array-Eintrag erstellt und im Wesentlichen durch die modernen Operatoren ersetzt wird, die dies im Fall von "Filtern" vermeiden.

Der zweite $match Stufe verwirft hier alle Elemente ( jetzt "Dokumente"), die nicht der Bedingung "Absender" entsprachen. Dann wenden wir einen $sort an um bei jedem Dokument durch die _id das "neueste" Datum oben zu setzen , daher die beiden "Sortieren"-Tasten.

Schließlich wenden wir $group an um einfach auf das Originaldokument zu verweisen, verwenden Sie $first als Akkumulator, um das Element zu erhalten, das "oben" liegt.