Základním konceptem je, že potřebujete agregační rámec, abyste mohli použít podmínky pro "filtrování" prvků pole na podmínky. V závislosti na dostupné verzi existují různé techniky, které lze použít.
Ve všech případech je to výsledek:
{
"_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 a vyšší
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
]
}
}
}
}
}}
])
Toto je nejúčinnější způsob, který využívá $replaceRoot
což nám umožňuje deklarovat proměnné k použití ve struktuře "nahrazeno" pomocí $let
. Hlavní výhodou je, že to vyžaduje pouze „dvě“ fáze potrubí.
Aby se obsah pole shodoval, používáte $filter
kde použijete $eq
logická operace pro testování hodnoty "odesílatel"
. Pokud se podmínka shoduje, vrátí se pouze odpovídající položky pole.
Pomocí stejného $filter
aby byly brány v úvahu pouze odpovídající položky „odesílatele“, pak chceme použít $max
přes "filtrovaný" seznam na hodnoty v "datetime"
. $max
]5
hodnota je "nejnovější" datum podle podmínek.
Chceme tuto hodnotu, abychom mohli později porovnat vrácené výsledky z "filtrovaného" pole s tímto "maxDate". Což se děje uvnitř "in"
blok $let
kde se dvě „proměnné“ deklarované dříve pro filtrovaný obsah a „maxDate“ opět použijí na $filter
aby bylo možné vrátit to, co by mělo být jedinou hodnotou, která splnila obě podmínky a měla také "nejpozději datum".
Protože chcete pouze „jeden“ výsledek, používáme $arrayElemAt
použít hodnotu spíše než pole.
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
]
}
}}
])
Toto je v podstatě stejný proces, jak je popsán, ale bez $ nahradit kořen
fázi pipeline, musíme podat žádost ve dvou $project
etapy. Důvodem je, že potřebujeme "vypočítanou hodnotu" z "maxDate", abychom mohli provést poslední $filter
a není k dispozici ve složeném příkazu, takže místo toho rozdělíme potrubí. To má malý dopad na celkové náklady na operaci.
V MongoDB 2.6 až 3.0 zde můžeme použít většinu techniky kromě $arrayElemAt
a buď přijměte výsledek „pole“ jediným záznamem, nebo vložte $unwind
fázi zabývat se tím, co by nyní mělo být jediným záznamem.
Starší verze MongoDB
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" }
}}
])
I když to vypadá krátce, jde o zdaleka nejnákladnější operaci. Zde musíte použít $unwind
za účelem aplikování podmínek na prvky pole. Jedná se o velmi nákladný proces, protože vytváří kopii každého dokumentu pro každou položku pole a je v podstatě nahrazen moderními operátory, které se tomu vyhýbají v případě „filtrování“.
Druhý $match
stage zde zahodí všechny prvky (nyní „dokumenty“), které neodpovídají podmínce „odesílatel“. Poté použijeme $sort
aby se "nejnovější" datum u každého dokumentu umístilo na začátek pomocí _id
, tedy dvě klávesy "třídění".
Nakonec použijeme $group
abyste mohli pouze odkazovat na původní dokument pomocí $first
jako akumulátor pro získání prvku, který je „navrchu“.