Jeśli musisz obliczyć coś takiego w czasie wykonywania, z „przefiltrowaną” zawartością z tablicy określającą porządek sortowania, najlepiej zrobić coś za pomocą .aggregate()
aby zmienić kształt i określić wartość sortowania w ten sposób:
db.collection.aggregate([
// Pre-filter the array elements
{ "$project": {
"tags": 1,
"score": {
"$setDifference": [
{ "$map": {
"input": "$tags",
"as": "tag",
"in": {
"$cond": [
{ "$eq": [ "$$el.id", "t1" ] },
"$$el.score",
false
]
}
}},
[false]
]
}
}},
// Unwind to denormalize
{ "$unwind": "$score" },
// Group back the "max" score
{ "$group": {
"_id": "$_id",
"tags": { "$first": "$tags" },
"score": { "$max": "$score" }
}},
// Sort descending by score
{ "$sort": { "score": -1 } }
])
Gdzie pierwsza część potoku jest używana do "wstępnego filtrowania" zawartości tablicy (jak również do zachowania oryginalnego pola) tylko do tych wartości "score", gdzie id jest równe "t1". Odbywa się to poprzez przetwarzanie $map
który stosuje warunek do każdego elementu przez $cond
aby określić, czy zwrócić „wynik” dla tego elementu, czy false
.
$setDifference
operacja wykonuje porównanie z tablicą jednoelementową [false]
który skutecznie usuwa wszystkie false
wartości zwrócone z $map
. Jako „zestaw” usuwa to również zduplikowane wpisy, ale dla celów sortowania jest to dobra rzecz.
Po zredukowaniu tablicy i zmianie jej kształtu na wartości, które przetwarzasz $unwind
gotowy do następnego etapu, aby zająć się wartościami jako indywidualnymi elementami. $group
etap zasadniczo dotyczy $max
na "wyniku", aby zwrócić najwyższą wartość zawartą w przefiltrowanych wynikach.
Wtedy wystarczy tylko zastosować $sort
od ustalonej wartości do zamówienia dokumentów. Oczywiście, jeśli chcesz to zrobić na odwrót, użyj $min
i zamiast tego sortuj w porządku rosnącym.
Oczywiście dodaj $match
etap do początku, jeśli wszystko, czego naprawdę potrzebujesz, to dokumenty, które faktycznie zawierają wartości "t1" dla id
w obrębie tagów. Ale ta część ma najmniejsze znaczenie dla sortowania według przefiltrowanych wyników, które chcesz osiągnąć.
Alternatywą do obliczania jest robienie tego wszystkiego podczas wpisywania wpisów do tablicy w dokumentach. Trochę bałagan, ale wygląda mniej więcej tak:
db.collection.update(
{ "_id": docId },
{
"$push": { "tags": { "id": "t1", "score": 60 } },
"$max": { "maxt1score": 60 },
"$min": { "mint1score": 60 }
}
)
Tutaj $max
operator aktualizacji ustawia wartość dla określonego pola tylko wtedy, gdy nowa wartość jest większa niż istniejąca wartość lub w przeciwnym razie nie istnieje jeszcze żadna właściwość. Odwrotny przypadek dotyczy $min
, gdzie tylko wtedy, gdy mniej niż zostanie zastąpione nową wartością.
To oczywiście skutkowałoby dodaniem różnych dodatkowych właściwości do dokumentów, ale końcowy wynik jest znacznie uproszczony sortowanie:
db.collection.find().sort({ "maxt1score": -1 })
I będzie działać znacznie szybciej niż obliczenia za pomocą potoku agregacji.
Zastanów się więc nad zasadami projektowania. Uporządkowane dane w tablicach, w których chcesz sortować wyniki filtrowane i sparowane, oznaczają obliczanie w czasie wykonywania w celu określenia, według której wartości należy sortować. Dodawanie dodatkowych właściwości do dokumentu w .update()
oznacza, że możesz po prostu odwołać się do tych właściwości w celu bezpośredniego sortowania wyników.