Nejprve ukažme vaše data tak, aby je lidé mohli použít a přinést požadovaný výsledek:
{ value: 1,
_id: ObjectId('5cb9ea0c75c61525e0176f96'),
name: 'Test',
category: 'Development',
subcategory: 'Programming Languages',
status: 'Supported',
description: 'Test',
change:
[ { version: 1,
who: 'ATL User',
when: new Date('2019-04-19T15:30:39.912Z'),
what: 'Item Creation' },
{ version: 2,
who: 'ATL Other User',
when: new Date('2019-04-19T15:31:39.912Z'),
what: 'Name Change' } ],
}
Všimněte si, že "kdy"
data se ve skutečnosti liší, takže bude existovat $max
hodnotu a nejsou jen stejné. Nyní můžeme projít případy
Případ 1 – Získejte „singulární“ $max
hodnotu
Základním případem je použití $arrayElemAt
a $indexOfArray
operátory vrátí odpovídající $max
hodnota:
db.items.aggregate([
{ "$match": {
"subcategory": "Programming Languages", "name": { "$exists": true }
}},
{ "$addFields": {
"change": {
"$arrayElemAt": [
"$change",
{ "$indexOfArray": [
"$change.when",
{ "$max": "$change.when" }
]}
]
}
}}
])
Vrátí:
{
"_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
"value" : 1,
"name" : "Test",
"category" : "Development",
"subcategory" : "Programming Languages",
"status" : "Supported",
"description" : "Test",
"change" : {
"version" : 2,
"who" : "ATL Other User",
"when" : ISODate("2019-04-19T15:31:39.912Z"),
"what" : "Name Change"
}
}
V podstatě "$max":"$change.when"
vrátí hodnotu, která je "maximum" z tohoto pole hodnot. Poté najdete odpovídající "index" tohoto pole hodnot pomocí $indexOfArray
který vrátí první nalezen odpovídající index. Tato pozice "indexu" (ve skutečnosti pouze z pole "kdy"
hodnoty transponované ve stejném pořadí ) se pak použije s $arrayElemAt
extrahovat "celý objekt" z "change"
pole na zadané pozici indexu.
Případ 2 – Vraťte „multiple“ $max
záznamy
V podstatě to samé s $max
, kromě této doby jsme $filter
vrátíte vícenásobné "možné" odpovídající hodnoty $max
hodnota:
db.items.aggregate([
{ "$match": {
"subcategory": "Programming Languages", "name": { "$exists": true }
}},
{ "$addFields": {
"change": {
"$filter": {
"input": "$change",
"cond": {
"$eq": [ "$$this.when", { "$max": "$change.when" } ]
}
}
}
}}
])
Vrátí:
{
"_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
"value" : 1,
"name" : "Test",
"category" : "Development",
"subcategory" : "Programming Languages",
"status" : "Supported",
"description" : "Test",
"change" : [
{
"version" : 2,
"who" : "ATL Other User",
"when" : ISODate("2019-04-19T15:31:39.912Z"),
"what" : "Name Change"
}
]
}
Takže $max
je samozřejmě stejné, ale tentokrát je singulární hodnota vrácená tímto operátorem použita v $eq
srovnání v rámci $filter
. To zkontroluje každý prvek pole a podívá se na aktuální "kdy"
hodnota ( "$$this.when"
). Kde "rovná se" pak je prvek vrácen.
V podstatě stejný jako první přístup, ale s tou výjimkou, že $filter
umožňuje „více“ prvky, které mají být vráceny. Proto vše se stejným $max
hodnotu.
Případ 3 – Předřazení obsahu pole.
Nyní si můžete všimnout, že v příkladech, které jsem zahrnul (upraveno z vašich vlastních, ale se skutečným „maximálním“ datem ), je hodnota „max“ ve skutečnosti poslední hodnotu v poli. K tomu může přirozeně dojít v důsledku toho, že $push
( ve výchozím nastavení ) "připojí" na konec obsahu existujícího pole. Tedy "novější" položky budou mít tendenci být na konci pole.
Toto je samozřejmě výchozí chování, ale existují dobré důvody, proč "můžete" chtít to změnit. Zkrátka nejlepší způsob, jak získat „nejnovější“ položka pole má ve skutečnosti vrátit první prvek z pole.
Vše, co ve skutečnosti musíte udělat, je zajistit „nejnovější“ je ve skutečnosti přidáno nejprve spíše než poslední . Existují dva přístupy:
-
Použijte
$position
k "pre-pend" položek pole: Toto je jednoduchý modifikátor$push
pomocí0
pozice abyste vždy přidávali přední :db.items.updateOne( { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") }, { "$push": { "change": { "$each": [{ "version": 3, "who": "ATL User", "when": new Date(), "what": "Another change" }], "$position": 0 } }} )
Tím by se dokument změnil na:
{ "_id" : ObjectId("5cb9ea0c75c61525e0176f96"), "value" : 1, "name" : "Test", "category" : "Development", "subcategory" : "Programming Languages", "status" : "Supported", "description" : "Test", "change" : [ { "version" : 3, "who" : "ATL User", "when" : ISODate("2019-04-20T02:40:30.024Z"), "what" : "Another change" }, { "version" : 1, "who" : "ATL User", "when" : ISODate("2019-04-19T15:30:39.912Z"), "what" : "Item Creation" }, { "version" : 2, "who" : "ATL Other User", "when" : ISODate("2019-04-19T15:31:39.912Z"), "what" : "Name Change" } ] }
Všimněte si, že by to vyžadovalo, abyste skutečně šli a "obrátili" všechny prvky pole předem, takže "nejnovější" už byl vpředu, takže pořadí bylo zachováno. Naštěstí je to trochu pokryto druhým přístupem...
-
Použít
$sort
upravit dokumenty v pořadí na každém$push
: A to je další modifikátor, který ve skutečnosti atomicky "přetřídí" každý nový prvek. Normální použití je v zásadě stejné u všech nových položek do$each
jako výše, nebo dokonce jen "prázdné" pole za účelem použití$sort
pouze na existující data:db.items.updateOne( { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") }, { "$push": { "change": { "$each": [], "$sort": { "when": -1 } } }} )
Výsledky v:
{ "_id" : ObjectId("5cb9ea0c75c61525e0176f96"), "value" : 1, "name" : "Test", "category" : "Development", "subcategory" : "Programming Languages", "status" : "Supported", "description" : "Test", "change" : [ { "version" : 3, "who" : "ATL User", "when" : ISODate("2019-04-20T02:40:30.024Z"), "what" : "Another change" }, { "version" : 2, "who" : "ATL Other User", "when" : ISODate("2019-04-19T15:31:39.912Z"), "what" : "Name Change" }, { "version" : 1, "who" : "ATL User", "when" : ISODate("2019-04-19T15:30:39.912Z"), "what" : "Item Creation" } ] }
Může chvíli trvat, než pochopíte, proč byste
$push
za účelem$sort
pole, jako je toto, ale obecným záměrem je, když mohou být provedeny úpravy pole, které "změní" vlastnost, jako jeDatum
podle které se třídí hodnota a takový příkaz byste použili k vyjádření těchto změn. Nebo stačí přidat nové položky pomocí$sort
a ať to vyjde.
Proč tedy "ukládat" takto uspořádané pole? Jak již bylo zmíněno, chcete první položku jako „nejnovější“ a pak dotaz, který se má vrátit, jednoduše zní:
db.items.find(
{
"subcategory": "Programming Languages",
"name": { "$exists": true }
},
{ "change": { "$slice": 1 } }
)
Vrátí:
{
"_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
"value" : 1,
"name" : "Test",
"category" : "Development",
"subcategory" : "Programming Languages",
"status" : "Supported",
"description" : "Test",
"change" : [
{
"version" : 3,
"who" : "ATL User",
"when" : ISODate("2019-04-20T02:40:30.024Z"),
"what" : "Another change"
}
]
}
Takže $slice
lze pak použít pouze k extrahování položek pole podle známých indexů. Technicky stačí použít -1
tam, abyste vrátili poslední položka pole tak jako tak, ale změna pořadí, kde je nejnovější jako první, umožňuje další věci, jako je potvrzení poslední úpravy, kterou provedl určitý uživatel, a/nebo další podmínky, jako je omezení časového rozsahu. tj.:
db.items.find(
{
"subcategory": "Programming Languages",
"name": { "$exists": true },
"change.0.who": "ATL User",
"change.0.when": { "$gt": new Date("2018-04-01") }
},
{ "change": { "$slice": 1 } }
)
Zde je třeba poznamenat, že něco jako "change.-1.when"
je nezákonné prohlášení, což je v podstatě důvod, proč měníme pořadí pole, abyste mohli použít legální 0
pro první místo -1
na poslední .
Závěr
Existuje tedy několik různých věcí, které můžete udělat, buď pomocí agregačního přístupu k filtrování obsahu pole, nebo pomocí standardních formulářů dotazů po provedení určité úpravy toho, jak jsou data skutečně uložena. Který z nich použít, závisí na vašich vlastních okolnostech, ale je třeba poznamenat, že jakýkoli ze standardních formulářů dotazů poběží výrazně rychleji než jakákoli manipulace prostřednictvím agregačního rámce nebo jakýchkoli výpočetních operátorů.