Если вам это сойдет с рук, то, вероятно, лучше «сложить» уменьшенные результаты в один документ, а затем $slice
два верхних и $sum
остальное:
Model.aggregate([
{ "$group": {
"_id": "$setting",
"counting": { "$sum": "$counting" }
}},
{ "$sort": { "counting": -1 } },
{ "$group": {
"_id": null,
"data": { "$push": "$$ROOT" }
}},
{ "$addFields": {
"data": {
"$let": {
"vars": { "top": { "$slice": ["$data", 0, 2 ] } },
"in": {
"$concatArrays": [
"$$top",
{ "$cond": {
"if": { "$gt": [{ "$size": "$data" }, 2] },
"then":
[{
"_id": "Useless",
"counting": {
"$sum": {
"$map": {
"input": {
"$filter": {
"input": "$data",
"cond": { "$not": { "$in": [ "$$this._id", "$$top._id" ] } }
}
},
"in": "$$this.counting"
}
}
}
}],
"else": []
}}
]
}
}
}
}},
{ "$unwind": "$data" },
{ "$replaceRoot": { "newRoot": "$data" } }
])
Если это потенциально очень «большой» результат, даже уменьшенный, то $limit
используйте $facet
для «отдыха»:
Model.aggregate([
{ "$facet": {
"top": [
{ "$group": {
"_id": "$setting",
"counting": { "$sum": "$counting" }
}},
{ "$sort": { "counting": -1 } },
{ "$limit": 2 }
],
"rest": [
{ "$group": {
"_id": "$setting",
"counting": { "$sum": "$counting" }
}},
{ "$sort": { "counting": -1 } },
{ "$skip": 2 },
{ "$group": {
"_id": "Useless",
"counting": { "$sum": "$counting" }
}}
]
}},
{ "$project": {
"data": {
"$concatArrays": [
"$top","$rest"
]
}
}},
{ "$unwind": "$data" },
{ "$replaceRoot": { "newRoot": "$data" } }
])
Или даже $lookup
с MongoDB 3.6:
Model.aggregate([
{ "$group": {
"_id": "$setting",
"counting": { "$sum": "$counting" }
}},
{ "$sort": { "counting": -1 } },
{ "$limit": 2 },
{ "$group": {
"_id": null,
"top": { "$push": "$$ROOT" }
}},
{ "$lookup": {
"from": "colllection",
"let": { "settings": "$top._id" },
"pipeline": [
{ "$match": {
"$expr": {
"$not": { "$in": [ "$setting", "$$settings" ] }
}
}},
{ "$group": {
"_id": "Useless",
"counting": { "$sum": "$counting" }
}}
],
"as": "rest"
}},
{ "$project": {
"data": {
"$concatArrays": [ "$top", "$rest" ]
}
}},
{ "$unwind": "$data" },
{ "$replaceRoot": { "newRoot": "$data" } }
])
Все в действительности одинаково и все возвращают один и тот же результат:
{ "_id" : "Contrast", "counting" : 2 }
{ "_id" : "Sharpness", "counting" : 2 }
{ "_id" : "Useless", "counting" : 3 }
Опционально $project
прямо в конце каждого вместо $replaceRoot
, если для вас действительно важен контроль над именами полей.Обычно я просто придерживаюсь $group
значений по умолчанию
В случае, если ваш MongoDB предшествует 3.4, а полученный остаток "Useless"
на самом деле слишком велик, чтобы использовать какой-либо вариантпервый подход, затем простое Promise
разрешение - это, в основном, ответ, один для aggregate
, а другой для базового счета и просто выполнить математику:
let [docs, count] = await Promise.all([
Model.aggregate([
{ "$group": {
"_id": "$setting",
"counting": { "$sum": "$counting" }
}},
{ "$sort": { "counting": -1 } },
{ "$limit": 2 },
]),
Model.count().exec()
]);
docs = [
...docs,
{
"_id": "Useless",
"counting": count - docs.reduce((o,e) => o + e.counting, 0)
}
];
или без async/await
:
Promise.all([
Model.aggregate([
{ "$group": {
"_id": "$setting",
"counting": { "$sum": "$counting" }
}},
{ "$sort": { "counting": -1 } },
{ "$limit": 2 },
]),
Model.count().exec()
]).then(([docs, count]) => ([
...docs,
{
"_id": "Useless",
"counting": count - docs.reduce((o,e) => o + e.counting, 0)
}
]).then( result => /* do something */ )
Это, в основном, разновидность устаревшего подхода "общее количество страниц" путем простого запуска отдельного запроса для подсчета элементов коллекции.
Выполнение отдельных запросов обычно является устаревшим способомделать это, и это часто работает лучше всего.Остальные решения в основном нацелены на «уловки агрегации», так как это было то, о чем вы просили, и это ответ, который вы получили, показав различные варианты одного и того же.
Один вариант помещает все результаты водин документ (где это возможно, из-за ограничения BSON, конечно) и другие, в основном, отличаются от «старого» подхода, снова выполняя запрос в другой форме.$facet
параллельно и $lookup
последовательно.