Ако трябва да изчислите нещо подобно по време на изпълнение, с "филтрирано" съдържание от масива, определящо реда на сортиране, тогава най-добре направете нещо с .aggregate()
за преоформяне и определяне на стойност за сортиране като тази:
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 } }
])
Където първата част от конвейера се използва за „предварително филтриране“ на съдържанието на масива (както и запазване на оригиналното поле) само до тези стойности на „резултат“, където идентификаторът е равен на „t1“. Това става чрез обработка на $map
който прилага условие към всеки елемент чрез $cond
за да определи дали да върне "резултат" за този елемент или false
.
$setDifference
извършва сравнение с масив от един елемент [false]
което ефективно премахва всички false
стойности, върнати от $map
. Като „набор“ това също премахва дублиращи се записи, но за целта на сортирането тук това е добро нещо.
С намаления и преформатиран масив до стойности, вие обработвате $unwindкод>
готови за следващия етап на работа с ценностите като отделни елементи. $group
етап по същество се прилага $max
на "резултат", за да върне най-високата стойност, съдържаща се във филтрираните резултати.
Тогава става въпрос само за прилагане на $sortкод>
върху определената стойност да наредите документите. Естествено, ако искате това обратното, използвайте $min
и вместо това сортирайте във възходящ ред.
Разбира се, добавете $match
етап до началото, ако всичко, което наистина искате, са документи, които всъщност съдържат стойности "t1" за id
в рамките на етикетите. Но тази част е от най-малко значение за сортирането на филтрирани резултати, които искате да постигнете.
Алтернативата на изчисляването е да направите всичко, докато пишете записи в масива в документите. Някак объркано, но става нещо подобно:
db.collection.update(
{ "_id": docId },
{
"$push": { "tags": { "id": "t1", "score": 60 } },
"$max": { "maxt1score": 60 },
"$min": { "mint1score": 60 }
}
)
Тук $max
Операторът за актуализиране задава стойността за посоченото поле само ако новата стойност е по-голяма от съществуващата стойност или в противен случай все още не съществува свойство. Обратният случай е верен за $min
, където само ако е по-малко от него ще бъде заменено с новата стойност.
Това разбира се би имало ефекта на добавяне на различни допълнителни свойства към документите, но крайният резултат е, че сортирането е значително опростено:
db.collection.find().sort({ "maxt1score": -1 })
И ще работи много по-бързо от изчисляването с тръбопровод за агрегиране.
Така че помислете за принципите на дизайна. Структурираните данни в масиви, където искате филтрирани и сдвоени резултати за сортиране, означават изчисляване по време на изпълнение, за да се определи по коя стойност да се сортира. Добавяне на допълнителни свойства към документа на .update()
означава, че можете просто да посочите тези свойства, за да сортирате директно резултатите.