Po pierwsze, pokażmy Twoje dane w sposób, w jaki ludzie mogą je wykorzystać i uzyskać pożądany rezultat:
{ 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' } ],
}
Zauważ, że "kiedy"
daty są w rzeczywistości różne, więc będzie $max
wartość i nie są po prostu takie same. Teraz możemy przejrzeć sprawy
Przypadek 1 – Pobierz „liczbę pojedynczą” $max
wartość
Podstawowym przypadkiem jest użycie $arrayElemAt
i $indexOfArray
operatorów, aby zwrócić pasujące $max
wartość:
db.items.aggregate([
{ "$match": {
"subcategory": "Programming Languages", "name": { "$exists": true }
}},
{ "$addFields": {
"change": {
"$arrayElemAt": [
"$change",
{ "$indexOfArray": [
"$change.when",
{ "$max": "$change.when" }
]}
]
}
}}
])
Zwroty:
{
"_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"
}
}
Zasadniczo "$max":"$change.when"
zwraca wartość, która jest „maksimum” z tej tablicy wartości. Następnie znajdziesz pasujący „indeks” tej tablicy wartości za pomocą $indexOfArray
który zwraca pierwszy znaleziono pasujący indeks. Ta pozycja "indeks" (w rzeczywistości tylko z tablicy "kiedy"
wartości transponowane w tej samej kolejności ) są następnie używane z $arrayElemAt
aby wyodrębnić „cały obiekt” z „zmiany”
tablica w określonej pozycji indeksu.
Przypadek 2 – Zwróć „wiele” $max
wpisy
Prawie to samo z $max
, ale tym razem $filter
aby zwrócić wiele "możliwych" wartości pasujące do $max
wartość:
db.items.aggregate([
{ "$match": {
"subcategory": "Programming Languages", "name": { "$exists": true }
}},
{ "$addFields": {
"change": {
"$filter": {
"input": "$change",
"cond": {
"$eq": [ "$$this.when", { "$max": "$change.when" } ]
}
}
}
}}
])
Zwroty:
{
"_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 więc $max
jest oczywiście taka sama, ale tym razem pojedyncza wartość zwracana przez ten operator jest używana w $eq
porównanie w ramach $filter
. Sprawdza każdy element tablicy i sprawdza bieżący "kiedy"
wartość ( "$$this.kiedy"
). Gdzie "równe" wtedy element jest zwracany.
Zasadniczo to samo co pierwsze podejście, ale z tą różnicą, że $filter
umożliwia „wiele” elementy do zwrócenia. Dlatego wszystko z tym samym $max
wartość.
Przypadek 3 – Wstępne sortowanie zawartości tablicy.
Teraz możesz zauważyć, że w przykładowych danych, które zawarłem (zaadaptowane z twoich własnych, ale z rzeczywistą datą „max”), wartość „max” jest w rzeczywistości ostatnią wartość w tablicy. Może się to po prostu naturalnie zdarzyć, ponieważ $push
( domyślnie ) "dołącza" do końca istniejącej zawartości tablicy. A więc „nowsze” wpisy będą zwykle znajdować się na końcu tablicy.
Jest to oczywiście domyślne zachowanie, ale istnieją dobre powody, dla których „możesz” chcę to zmienić. W skrócie najlepsze sposób na uzyskanie „najnowszych” wpis tablicy ma w rzeczywistości zwrócić pierwszy element z tablicy.
Wszystko, co musisz zrobić, to upewnić się, że "najnowsze" jest faktycznie dodany pierwszy zamiast ostatni . Istnieją dwa podejścia:
-
Użyj
$position
aby „założyć” elementy tablicy: Jest to prosty modyfikator$push
używając0
pozycja aby zawsze dodawać do przódu :db.items.updateOne( { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") }, { "$push": { "change": { "$each": [{ "version": 3, "who": "ATL User", "when": new Date(), "what": "Another change" }], "$position": 0 } }} )
Zmieniłoby to dokument 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" } ] }
Zauważ, że wymagałoby to wcześniejszego „odwrócenia” wszystkich elementów tablicy, tak aby „najnowszy” był już na początku, aby zachować kolejność. Na szczęście jest to nieco omówione w drugim podejściu...
-
Użyj
$sort
modyfikować dokumenty w kolejności na każdym$push
: I to jest inny modyfikator, który faktycznie „przesortowuje” atomowo przy każdym dodaniu nowego przedmiotu. Normalne użycie jest w zasadzie takie samo z każdym nowym elementem$each
jak powyżej, lub nawet po prostu „pustą” tablicę w celu zastosowania$sortuj
tylko do istniejących danych:db.items.updateOne( { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") }, { "$push": { "change": { "$each": [], "$sort": { "when": -1 } } }} )
Wyniki w:
{ "_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" } ] }
Może minąć minuta, aby zrozumieć, dlaczego
$push
w celu$sort
tablica taka jak ta, ale ogólną intencją jest to, że można dokonać modyfikacji tablicy, która "zmienia" właściwość, taką jakDate
sortowanej wartości i użyjesz takiego stwierdzenia, aby odzwierciedlić te zmiany. Lub po prostu dodaj nowe elementy za pomocą$sort
i niech to zadziała.
Dlaczego więc „sklep” tablica uporządkowana w ten sposób? Jak wspomniano wcześniej, chcesz pierwszego jako „najnowszy” , a następnie zapytanie do zwrócenia staje się po prostu:
db.items.find(
{
"subcategory": "Programming Languages",
"name": { "$exists": true }
},
{ "change": { "$slice": 1 } }
)
Zwroty:
{
"_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 więc $slice
może być następnie użyty tylko do wyodrębnienia elementów tablicy według znanych indeksów. Technicznie możesz po prostu użyć -1
tam, aby zwrócić ostatni w każdym razie element tablicy, ale zmiana kolejności, w której najnowsza jest pierwsza, pozwala na inne rzeczy, takie jak potwierdzenie ostatniej modyfikacji dokonanej przez określonego użytkownika i/lub inne warunki, takie jak ograniczenie zakresu dat. czyli:
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 } }
)
Zauważając, że coś takiego jak "zmień.-1.kiedy"
jest niedozwolonym stwierdzeniem, dlatego w zasadzie zmieniamy kolejność tablicy, aby można było użyć legal 0
na pierwszy zamiast -1
na ostatni .
Wniosek
Jest więc kilka różnych rzeczy, które możesz zrobić, używając podejścia agregacyjnego do filtrowania zawartości tablicy lub za pomocą standardowych formularzy zapytań po dokonaniu pewnych modyfikacji w sposobie przechowywania danych. To, którego użyć, zależy od Twoich okoliczności, ale należy zauważyć, że każdy ze standardowych formularzy zapytań będzie działać znacznie szybciej niż jakakolwiek manipulacja za pośrednictwem struktury agregacji lub dowolnych operatorów obliczeniowych.