Laten we eerst uw gegevens laten zien op een manier waarop mensen ze kunnen gebruiken en een gewenst resultaat opleveren:
{ 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' } ],
}
Merk op dat de "wanneer"
datums zijn in feite anders, dus er zal een $max
waarde en ze zijn niet alleen hetzelfde. Nu kunnen we de cases doornemen
Geval 1 - Haal de "enkelvoud" $max op
waarde
Het basisgeval hier is het gebruik van de $arrayElemAt
en $indexOfArray
operators om de overeenkomende $max
te retourneren
waarde:
db.items.aggregate([
{ "$match": {
"subcategory": "Programming Languages", "name": { "$exists": true }
}},
{ "$addFields": {
"change": {
"$arrayElemAt": [
"$change",
{ "$indexOfArray": [
"$change.when",
{ "$max": "$change.when" }
]}
]
}
}}
])
Retourneren:
{
"_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"
}
}
In feite de "$max":"$change.when"
retourneert de waarde die het "maximum" is binnen die reeks waarden. U vindt dan de overeenkomende "index" van die reeks waarden via $indexOfArray
die de eerste . retourneert overeenkomende index gevonden. Die "index"-positie (van eigenlijk slechts een array van "wanneer"
waarden getransponeerd in dezelfde volgorde ) wordt vervolgens gebruikt met $arrayElemAt
om het "hele object" te extraheren uit de "wijziging"
array op de opgegeven indexpositie.
Geval 2 - Retourneer de "multiple" $max
inzendingen
Vrijwel hetzelfde met $max
, behalve deze keer hebben we $filter
om de meervoudige "mogelijke" . terug te geven waarden die overeenkomen met die $max
waarde:
db.items.aggregate([
{ "$match": {
"subcategory": "Programming Languages", "name": { "$exists": true }
}},
{ "$addFields": {
"change": {
"$filter": {
"input": "$change",
"cond": {
"$eq": [ "$$this.when", { "$max": "$change.when" } ]
}
}
}
}}
])
Retourneren:
{
"_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"
}
]
}
Dus de $max
is natuurlijk hetzelfde, maar deze keer wordt de enkelvoudige waarde die door die operator wordt geretourneerd, gebruikt in een $eq
vergelijking binnen $filter
. Dit inspecteert elk array-element en kijkt naar de huidige "wanneer"
waarde ( "$$this.when"
). Waar "gelijk" dan wordt het element geretourneerd.
In principe hetzelfde als de eerste benadering, maar met de uitzondering dat $filter
staat "meerdere" toe elementen terug te sturen. Daarom alles met de dezelfde $max
waarde.
Geval 3 - Sorteer de array-inhoud vooraf.
Nu zou u kunnen opmerken dat in de voorbeeldgegevens die ik heb opgenomen (aangepast van uw eigen maar met een werkelijke "max"-datum) de "max" -waarde in feite de laatste is waarde in de array. Dit kan natuurlijk gebeuren als gevolg dat $push
( standaard ) "voegt toe" aan het einde van de bestaande array-inhoud. Dus "nieuwere" inzendingen staan meestal aan het einde van de array.
Dit is natuurlijk de standaard gedrag, maar er zijn goede redenen waarom u 'mag' wil daar verandering in brengen. Kortom de beste manier om de "meest recente" . te krijgen array-invoer is om in feite het eerste element te retourneren uit de array.
Het enige dat u hoeft te doen, is ervoor zorgen dat de "meest recente" wordt feitelijk eerst toegevoegd in plaats van laatste . Er zijn twee benaderingen:
-
Gebruik
$position
om array-items "vooraf te plaatsen": Dit is een eenvoudige wijziging van$push
met behulp van de0
positie om altijd toe te voegen aan de voorkant :db.items.updateOne( { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") }, { "$push": { "change": { "$each": [{ "version": 3, "who": "ATL User", "when": new Date(), "what": "Another change" }], "$position": 0 } }} )
Dit zou het document veranderen in:
{ "_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" } ] }
Houd er rekening mee dat je hiervoor al je array-elementen van tevoren moet "omkeren", zodat de "nieuwste" al vooraan staat, zodat de volgorde behouden blijft. Gelukkig wordt dit enigszins behandeld in de tweede benadering...
-
Gebruik
$sort
om de documenten in volgorde aan te passen op elke$push
: En dit is de andere modifier die in feite atomair "opnieuw sorteert" bij elke toevoeging van een nieuw item. Normaal gebruik is in principe hetzelfde met alle nieuwe items voor$each
zoals hierboven, of zelfs gewoon een "lege" array om de toe te passen$sort
alleen naar bestaande gegevens:db.items.updateOne( { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") }, { "$push": { "change": { "$each": [], "$sort": { "when": -1 } } }} )
Resultaten in:
{ "_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" } ] }
Het kan even duren voordat u begrijpt waarom u
$push
om$sort
een array als deze, maar de algemene bedoeling is wanneer er wijzigingen kunnen worden aangebracht in een array die een eigenschap zoals eenDatum
"veranderen" waarde waarop wordt gesorteerd en u zou een dergelijke verklaring gebruiken om die wijzigingen weer te geven. Of voeg gewoon nieuwe items toe met de$sort
en laat het werken.
Dus waarom "winkel" de array zo geordend? Zoals eerder vermeld, wil je de eerste item als de "meest recente" , en dan de vraag om terug te keren die eenvoudig wordt:
db.items.find(
{
"subcategory": "Programming Languages",
"name": { "$exists": true }
},
{ "change": { "$slice": 1 } }
)
Retourneren:
{
"_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"
}
]
}
Dus de $slice
kan dan alleen worden gebruikt om array-items op basis van bekende indexen te extraheren. Technisch gezien kun je gewoon -1
. gebruiken daar om de laatste . terug te sturen item van de array sowieso, maar de herschikking waarbij de meest recente eerst is, maakt andere dingen mogelijk, zoals bevestigen dat de laatste wijziging door een bepaalde gebruiker is aangebracht, en/of andere voorwaarden zoals een beperking van een datumbereik. dat wil zeggen:
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 } }
)
Hierbij opmerkend dat zoiets als "change.-1.when"
is een illegale verklaring, wat in feite de reden is waarom we de array opnieuw ordenen, zodat u de legale . kunt gebruiken 0
voor eerste in plaats van -1
voor laatste .
Conclusie
Er zijn dus verschillende dingen die u kunt doen, hetzij door de aggregatiebenadering te gebruiken om de array-inhoud te filteren, hetzij via standaardqueryformulieren nadat u enige wijziging heeft aangebracht in de manier waarop de gegevens feitelijk worden opgeslagen. Welke u moet gebruiken, hangt af van uw eigen omstandigheden, maar het moet worden opgemerkt dat elk van de standaardvragenformulieren zal aanzienlijk sneller werken dan enige manipulatie via het aggregatieraamwerk of enige berekende operator.