Først og fremmest, lad os vise dine data på en måde, som folk kan bruge dem og producere et ønsket resultat:
{ 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' } ],
}
Bemærk, at "when"
datoerne er faktisk forskellige, så der vil være en $max
værdi, og de er ikke bare ens. Nu kan vi gennemgå sagerne
Case 1 - Hent "singular" $max
værdi
Den grundlæggende sag her er at bruge $arrayElemAt
og $indexOfArray
operatører for at returnere den matchende $max
værdi:
db.items.aggregate([
{ "$match": {
"subcategory": "Programming Languages", "name": { "$exists": true }
}},
{ "$addFields": {
"change": {
"$arrayElemAt": [
"$change",
{ "$indexOfArray": [
"$change.when",
{ "$max": "$change.when" }
]}
]
}
}}
])
Returnerer:
{
"_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"
}
}
Grundlæggende er "$max":"$change.when"
returnerer den værdi, der er "maksimum" fra denne række af værdier. Du finder derefter det matchende "indeks" for det array af værdier via $indexOfArray
som returnerer den første matchende indeks fundet. Denne "indeks"-position (fra faktisk kun en matrix af "når"
værdier transponeret i samme rækkefølge ) bruges derefter med $arrayElemAt
for at udtrække "hele objektet" fra
Case 2 - Returner "multiple" $max
poster
Stort set det samme med $max
, bortset fra at vi denne gang $filter
for at returnere de mange "mulige" værdier, der matcher den $max
værdi:
db.items.aggregate([
{ "$match": {
"subcategory": "Programming Languages", "name": { "$exists": true }
}},
{ "$addFields": {
"change": {
"$filter": {
"input": "$change",
"cond": {
"$eq": [ "$$this.when", { "$max": "$change.when" } ]
}
}
}
}}
])
Returnerer:
{
"_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"
}
]
}
Så $max
er selvfølgelig den samme, men denne gang bruges den entalsværdi, der returneres af den pågældende operator i en $eq
sammenligning inden for $filter
. Dette inspicerer hvert array-element og ser på strømmen "når"
værdi ( "$$this.when"
). Hvor "lige" derefter returneres elementet.
Grundlæggende det samme som den første tilgang, men med den undtagelse at $filter
tillader "flere" elementer, der skal returneres. Derfor alt med det samme $max
værdi.
Case 3 - Forsortér matrixindholdet.
Nu kan du måske bemærke, at i de eksempeldata, jeg inkluderede (tilpasset fra din egen, men med en faktisk "max"-dato) er "max"-værdien faktisk den sidste værdi i arrayet. Dette kan helt naturligt ske som et resultat af, at $push
(som standard) "tilføjer" til slutningen af det eksisterende matrixindhold. Så "nyere" poster vil normalt være i slutningen af arrayet.
Dette er selvfølgelig standard adfærd, men der er gode grunde til, hvorfor du "må" ønsker at ændre på det. Kort sagt det bedste måde at få den "seneste" på array-indtastning er faktisk at returnere det første element fra arrayet.
Alt du faktisk skal gøre er at sikre dig den "nyeste" er faktisk tilføjet først i stedet for sidste . Der er to tilgange:
-
Brug
$position
for at "afvente" matrixelementer: Dette er en simpel modifikation til$push
ved hjælp af0
position for altid at tilføje til fronten :db.items.updateOne( { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") }, { "$push": { "change": { "$each": [{ "version": 3, "who": "ATL User", "when": new Date(), "what": "Another change" }], "$position": 0 } }} )
Dette ville ændre dokumentet til:
{ "_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" } ] }
Bemærk, at dette ville kræve, at du faktisk går og "vender" alle dine array-elementer på forhånd, så det "nyeste" allerede var foran, så rækkefølgen blev opretholdt. Heldigvis er dette noget dækket i den anden tilgang...
-
Brug
$sort
at ændre dokumenterne i rækkefølge på hver$push
: Og dette er den anden modifikator, som faktisk "omsorterer" atomisk på hver ny tilføjelse. Normal brug er stort set det samme med alle nye varer til$each
som ovenfor, eller endda bare et "tomt" array for at anvende$sort
kun til eksisterende data:db.items.updateOne( { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") }, { "$push": { "change": { "$each": [], "$sort": { "when": -1 } } }} )
Resultater i:
{ "_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" } ] }
Det kan tage et minut at forstå, hvorfor du ville
$push
for at$sort
et array som dette, men den generelle hensigt er, når der kan foretages ændringer af et array, som "ændrer" en egenskab som enDato
værdi bliver sorteret efter, og du ville bruge en sådan erklæring til at afspejle disse ændringer. Eller faktisk bare tilføje nye elementer med$sort
og lad det løse sig.
Så hvorfor "butik" arrayet bestilt sådan? Som tidligere nævnt vil du have den første element som det "seneste" , og derefter forespørgslen, der skal returneres, der blot bliver:
db.items.find(
{
"subcategory": "Programming Languages",
"name": { "$exists": true }
},
{ "change": { "$slice": 1 } }
)
Returnerer:
{
"_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"
}
]
}
Så $slice
kan derefter kun bruges til at udtrække array-elementer ved kendte indekser. Teknisk kan du bare bruge -1
der for at returnere den sidste element i arrayet alligevel, men omarrangeringen, hvor den seneste er først, giver mulighed for andre ting som at bekræfte, at den sidste ændring blev foretaget af en bestemt bruger og/eller andre forhold som en datointerval begrænsning. dvs.:
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 } }
)
Bemærker her, at noget som "change.-1.when"
er en ulovlig erklæring, hvilket grundlæggende er grunden til, at vi omarrangerer arrayet, så du kan bruge det lovlige 0
for først i stedet for -1
til sidste .
Konklusion
Så der er flere forskellige ting, du kan gøre, enten ved at bruge aggregeringstilgangen til at filtrere matrixindholdet eller via standardforespørgselsformularer efter at have foretaget nogle ændringer af, hvordan dataene faktisk lagres. Hvilken man skal bruge afhænger af dine egne forhold, men det skal bemærkes, at enhver af standardforespørgselsformularerne vil køre betydeligt hurtigere end nogen form for manipulation via aggregeringsrammen eller nogen beregnede operatører.