#mongodb #mongodb-query #aggregation-framework #grouping #limit
#mongodb #mongodb-запрос #aggregation-framework #группировка #ограничить
Вопрос:
Я не могу ограничить количество передаваемых элементов в групповой функции с конвейером агрегации. Возможно ли это? Небольшой пример:
Данные:
[
{
"submitted": date,
"loc": { "lng": 13.739251, "lat": 51.049893 },
"name": "first",
"preview": "my first"
},
{
"submitted": date,
"loc": { "lng": 13.639241, "lat": 51.149883 },
"name": "second",
"preview": "my second"
},
{
"submitted": date,
"loc": { "lng": 13.715422, "lat": 51.056384 },
"name": "nearpoint2",
"preview": "my nearpoint2"
}
]
Вот мой конвейер агрегации:
var pipeline = [
//I want to limit the data to a certain area
{ $match: {
loc: {
$geoWithin: {
$box: [
[locBottomLeft.lng, locBottomLeft.lat],
[locUpperRight.lng, locUpperRight.lat]
]
}
}
}},
// I just want to get the latest entries
{ $sort: { submitted: -1 } },
// I group by name
{
$group: {
_id: "$name",
// get name
submitted: { $max: "$submitted" },
// get the latest date
locs: { $push: "$loc" },
// push every loc into an array THIS SHOULD BE LIMITED TO AN AMOUNT 5 or 10
preview: { $first: "$preview" }
}
},
// Limit the query to at least 10 entries.
{ $limit: 10 }
];
Как я могу ограничить locs
массив 10
любым другим размером? Я пробовал что-то с $each
и $slice
, но, похоже, это не работает.
Ответ №1:
Предположим, что нижние левые координаты и верхние правые координаты являются соответственно [0, 0]
и [100, 100]
. Из MongoDB 3.2 вы можете использовать $slice
оператор для возврата подмножества массива, которое является тем, что вы хотите.
db.collection.aggregate([
{ "$match": {
"loc": {
"$geoWithin": {
"$box": [
[0, 0],
[100, 100]
]
}
}}
}},
{ "$group": {
"_id": "$name",
"submitted": { "$max": "$submitted" },
"preview": { "$first": "$preview" }
"locs": { "$push": "$loc" }
}},
{ "$project": {
"locs": { "$slice": [ "$locs", 5 ] },
"preview": 1,
"submitted": 1
}},
{ "$limit": 10 }
])
Комментарии:
1. Нет ли способа сделать это непосредственно при нажатии?
2. На данный момент это невозможно сделать при отправке @Nickpick
Ответ №2:
Начиная с Mongo 5.2
, это идеальный вариант использования для нового $topN
накопителя агрегации:
// { submitted: ISODate("2021-12-05"), group: "group1", value: "plop" }
// { submitted: ISODate("2021-12-07"), group: "group2", value: "smthg" }
// { submitted: ISODate("2021-12-06"), group: "group1", value: "world" }
// { submitted: ISODate("2021-12-12"), group: "group1", value: "hello" }
db.collection.aggregate([
{ $group: {
_id: "$group",
top: { $topN: { n: 2, sortBy: { submitted: -1 }, output: "$value" } }
}}
])
// { _id: "group1", top: [ "hello", "world" ] }
// { _id: "group2", top: [ "smthg" ] }
При этом применяется $topN
групповое накопление, которое:
- принимает для каждой группы 2 верхних (
n: 2
) элемента - топ-2, как определено
sortBy: { submitted: -1 }
(в обратном хронологическом порядке) - и для каждой сгруппированной записи извлекает поле
value
(output: "$value"
)
Ответ №3:
Я решил это, (1) разрешив передачу всех значений на групповом этапе, затем (2) добавив $ filter на последующем этапе $ project. В пределах $filter исключите все элементы массива с дисквалифицирующими значениями.
https://docs.mongodb.com/manual/reference/operator/aggregation/filter/
Ответ №4:
Вы можете достичь этого, передав $slice
operator непосредственно в $push
.
var pipeline = [{
//I want to limit the data to a certain area
$match: {
loc: {
$geoWithin: {
$box: [
[locBottomLeft.lng, locBottomLeft.lat],
[locUpperRight.lng, locUpperRight.lat]
]
}
}
}
},
// I just want to get the latest entries
{
$sort: {
submitted: -1
}
},
// I group by name
{
$group: {
_id: "$name",
< --get name
submitted: {
$max: "$submitted"
},
< --get the latest date
locs: {
$push: {
$slice: 10
}
},
< --push every loc into an array THIS SHOULD BE LIMITED TO AN AMOUNT 5 or 10
preview: {
$first: "$preview"
}
}
},
//Limit the query to at least 10 entries.
{
$limit: 10
}
];
Комментарии:
1. Это приводит к следующей ошибке:
MongoError: Expression $slice takes at least 2 arguments, and at most 3, but 1 were passed in