#mongodb #sorting #aggregation-framework #grouping
Вопрос:
Я произвел вывод ниже, используя агрегацию mongodb (включая конвейер $group внутри levelsCount
поля). :
{
"_id" : "1",
"name" : "First",
"levelsCount" : [
{ "_id" : "level_One", "levelNum" : 1, "count" : 1 },
{ "_id" : "level_Three", "levelNum" : 3, "count" : 1 },
{ "_id" : "level_Four", "levelNum" : 4, "count" : 8 }
]
}
{
"_id" : "2",
"name" : "Second",
"levelsCount" : [
{ "_id" : "level_One", "levelNum" : 1, "count" : 5 },
{ "_id" : "level_Two", "levelNum" : 2, "count" : 2 },
{ "_id" : "level_Three", "levelNum" : 3, "count" : 1 },
{ "_id" : "level_Four", "levelNum" : 4, "count" : 3 }
]
}
{
"_id" : "3",
"name" : "Third",
"levelsCount" : [
{ "_id" : "level_One", "levelNum" : 1, "count" : 1 },
{ "_id" : "level_Two", "levelNum" : 2, "count" : 3 },
{ "_id" : "level_Three", "levelNum" : 3, "count" : 2 },
{ "_id" : "level_Four", "levelNum" : 4, "count" : 3 }
]
}
Теперь мне нужно отсортировать эти документы на основе полей levelNum
и count
levelsCount
элементов массива. Т. е. если у обоих двух документов было количество 5 levelNum: 1 (level_One)
, то сортировка идет для сравнения количества levelNum: 2 (level_Two)
полей и так далее.
Я вижу, как конвейер $sort будет работать с несколькими полями (что-то вроде { $sort : { level_One : 1, level_Two: 1 } }
), но проблема в том, как получить доступ к этим значениям levelNum
каждого элемента массива и установить это значение в качестве имени поля для выполнения сортировки. (Я не мог справиться с этим даже после того, как $размотал levelsCount
массив).
P. s: Начальный порядок элементов levelsCount
массива может отличаться в каждом документе и не имеет значения.
Изменить: Ожидаемый результат вышеупомянутой структуры будет:
// Sorted result:
{
"_id" : "2",
"name" : "Second",
"levelsCount" : [
{ "_id" : "level_One", "levelNum" : 1, "count" : 5 }, // "level_One's count: 5" is greater than "level_One's count: 1" in two other documents, regardless of other level_* fields. Therefore this whole document with "name: Second" is ordered first.
{ "_id" : "level_Two", "levelNum" : 2, "count" : 2 },
{ "_id" : "level_Three", "levelNum" : 3, "count" : 1 },
{ "_id" : "level_Four", "levelNum" : 4, "count" : 3 }
]
}
{
"_id" : "3",
"name" : "Third",
"levelsCount" : [
{ "_id" : "level_One", "levelNum" : 1, "count" : 1 },
{ "_id" : "level_Two", "levelNum" : 2, "count" : 3 }, // "level_Two's count" in this document exists with value (3) while the "level_Two" doesn't exist in the below document which mean (0) value for count. So this document with "name: Third" is ordered higher than the below document.
{ "_id" : "level_Three", "levelNum" : 3, "count" : 2 },
{ "_id" : "level_Four", "levelNum" : 4, "count" : 3 }
]
}
{
"_id" : "1",
"name" : "First",
"levelsCount" : [
{ "_id" : "level_One", "levelNum" : 1, "count" : 1 },
{ "_id" : "level_Three", "levelNum" : 3, "count" : 1 },
{ "_id" : "level_Four", "levelNum" : 4, "count" : 8 }
]
}
Конечно, я бы предпочел иметь выходной документ в приведенном ниже формате, но первая проблема заключается в сортировке всех документов:
{
"_id" : "1",
"name" : "First",
"levelsCount" : [
{ "level_One" : 1 },
{ "level_Three" : 1 },
{ "level_Four" : 8 }
]
}
Комментарии:
1. Предоставьте, пожалуйста, ожидаемый результат в формате json
2. @Valijon К вопросу добавляется ожидаемый результат. Спасибо за продолжение
Ответ №1:
Вы можете сортировать levelNum
как по убыванию, так и count
по возрастанию,
db.collection.aggregate([
{
$sort: {
"levelsCount.levelNum": -1,
"levelsCount.count": 1
}
}
])
Для результата массива в формате ключ-значение levelsCount
,
$map
для повторения циклаlevelsCount
массива- подготовьте массив пар ключ-значение и преобразуйте его в объект с помощью
$arrayToObject
{
$addFields: {
levelsCount: {
$map: {
input: "$levelsCount",
in: {
$arrayToObject: [
[{ k: "$this._id", v: "$this.levelNum" }]
]
}
}
}
}
}
Комментарии:
1. Большое спасибо.Я столкнулся с 2 проблемами во время тестов: 1. Если в
levelsCount
массиве в документе нет документа со значениемlevel_Two
дляlevelsCount._id
поля; сортировка упорядочит этот документ выше, чем тот, который имеет фактическоеlevel_Two
значение для негоlevelsCount._id
. В то время как мне нужно, чтобы отсутствующий документ рассматривался как0
значение для егоcount
поля, что приведет к более низкому месту в порядке. 2. ЗначенияlevelsCount._id
на самом деле являются строками, их алфавитная сортировка будет концептуально неверной. Поэтому мне нужно было добавить дополнительное поле с соответствующим номером2. Я отредактировал входные документы в вопросе
3. я не думаю, что это возможно, возможно, потребуется
$unwind
деконструировать массив, но определенно это изменит порядок фактических элементов массиваlevelsCount
.4. Начальный порядок элементов в
levelsCount
списке на самом деле не имеет значения5. обновлено, я не понимаю, почему подсчет уровней. levelNum убывает , потому что он упорядочивается на большее число, когда мы направляем сортировку по полю, которое находится в массиве объекта.