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
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.