С помощью этого функционала мы можем групировать по неким полям наши результаты запросов, при этом проводить некие исчисления над полями одной групы для получения общего результата для группы; преобразовывать; фильтровать; сортировать; обрезать; а также строить пайпланы для обработки уже обработанных результатов.
$project - reshape - 1 : 1
$match - filter - n : 1
$group - aggregate - n : 1
$sort - sort - 1 : 1
$skip - skips - n : 1
$limit - limits - n : 1
$unwind - normalize - 1 : n
$out - output - 1 : 1 (направить результат в другую колекции из базы)
$redact - на сколько я понял, какие-то ограничения по пользователю, то есть эта директива позволяет проверять поля на значения и включать их или нет в ответ, а к пользователю приложение привязывает некое значение полей и на основании этих полей происходит фильтрация.
$geoNear - поиск относительно места расположения, в результате возвращается сначала самое близкое место и в конце самое отдаленное.
Как работает агрегация:
Групировака по нескольким полям(в ввиде сложенной айдишки для документов результата):
И чтобы суммировать какое-то поле у всех документах колекции или стадии агрегейшин пайплайн, мы используем тоже группировку, но группируем по null
Теперь каждый документ с массивом тегов превращется в N документов(где N размер массива). Все поля повторяются, а tags становится полем со значением из елемента массива.
Нужно изучать отдельной темой:
http://docs.mongodb.org/manual/reference/sql-aggregation-comparison/
2. Если мы хотим вернуть результат одним документом, то всегда нужно помнить о ограничении в 16 Мб на такой документ, чтобы не сталкиваться с таким ограничением - пользуемся cursor.
3. Если вызывать на шардированной системе group by или sort, у нас общий результат со всех шардов будет возвращен на первый шард(праймери шард) и там будет пытаться в его оперативки сделаться групировка и/или сортировка. Это конечно же может завалить этот шард, эта проблема решается например Hadoop-ом.
Aggregation Pipeline
Collection -> $project -> $match -> $group -> $sort -> Result$project - reshape - 1 : 1
$match - filter - n : 1
$group - aggregate - n : 1
$sort - sort - 1 : 1
$skip - skips - n : 1
$limit - limits - n : 1
$unwind - normalize - 1 : n
$out - output - 1 : 1 (направить результат в другую колекции из базы)
$redact - на сколько я понял, какие-то ограничения по пользователю, то есть эта директива позволяет проверять поля на значения и включать их или нет в ответ, а к пользователю приложение привязывает некое значение полей и на основании этих полей происходит фильтрация.
$geoNear - поиск относительно места расположения, в результате возвращается сначала самое близкое место и в конце самое отдаленное.
Как работает агрегация:
db.products.aggregate([
{$group:
{
_id: "$oneOfFieldsFromProductsByWhichWeAreGrouping",
weCreateThisFiledInResultByNextLogic: {$sum: 1} // this is as counting quantity of documents which is in each group
}
}
])
Групировака по нескольким полям(в ввиде сложенной айдишки для документов результата):
db.products.aggregate([
{$group:
{
_id: {
maker: "$manufacturer",
category: "$category"
},
num_products: {$sum: 1}
}
}
])
Кстати мы так можем и создавать документы в колекциях, а не позволять генерить монго автоматические ключи.
И чтобы суммировать какое-то поле у всех документах колекции или стадии агрегейшин пайплайн, мы используем тоже группировку, но группируем по null
db.products.aggregate([
{$group:
{
_id: null,
toBuyEverithingInShopYouNeed: {$sum: "$price"}
}
}
])
Aggregation Expressions for Grouping Stage
| $sum | Можно каунтить docs или сумировать значения указанного поля | db.zips.aggregate([{$group: {_id: "$state", population: {$sum: "$pop"}}}]) |
| $avg | ||
| $min | ||
| $max | ||
| $push | Собирать указанное поле в массив для групы | |
| $addToSet | Собирать указанное поле в множество(в виде моссива без дубликатов) для групы | db.zips.aggregate([{$group: {_id: "$city", postal_codes: {$addToSet: "$_id"}}}]) Часть результата:
{
"_id" : "CENTREVILLE",
"postal_codes" : [
"22020",
"49032",
"39631",
"21617",
"35042"
]
},
|
| $first | Должно работать вместе с $sort потому что в противном случае не имеет смысла | |
| $last | Должно работать вместе с $sort потому что в противном случае не имеет смысла |
Групировка групировки
db.grades.aggregate([
{$group: {_id:{class_id: "$class_id", student_id: "$student_id"}, 'average':{$avg: "$score"}}},
{$group: {_id:"$_id.class_id", "average":{$avg: "$average"}}}
])
Преобразование
db.zips.aggregate([
{$project: {
_id: 0,//don't include in result
city: {$toLower: "$city"},
pop: 1,//set as it is
state: 1,//set as it is
zip: "$_id"
}}
])
Результат:
{
"city" : "acmar",
"pop" : 6055,
"state" : "AL",
"zip" : "35004"
}
Фильтрация
db.zips.aggregate([
{$match:
{pop: {$gt: 100000}
},
{$group: {...}}
])
Сортировка
db.zips.aggregate([
{$match:
{state: "NY"
},
{$group:
{
_id: "$city",
population: {$sum: "$pop"}
}
},
{$project:
{
_id: 0,
city: "$_id",
population: 1
}
},
{$sort:
{
population: -1
}
}
])
Пропуск и ограничение
Будет пустым если в пайплайне нет перед ним сортировки.
db.zips.aggregate([
{$match:
{
state:"NY"
}
},
{$group:
{
_id: "$city",
population: {$sum:"$pop"},
}
},
{$project:
{
_id: 0,
city: "$_id",
population: 1,
}
},
{$sort:
{
population:-1
}
},
{$limit: 5},
{$skip: 10}
])
Но предыдущий пример также будет пустым, потому что мы сначала отрезаем 5 с начала, а потом из этих 5-ти пытаемся пропустить 10 - как итог получаем сдвиг за пределы массива результата.
Первый и последний
Будет пустым если в пайплайне нет перед ним сортировки.db.fun.aggregate([
{$match:{a:0}},
{$sort:{c:-1}},
{$group:{_id:"$a", c:{$first:"$c"}}}
])
Дробление-нормализация
Имеем документ в posts:{
id: ...,
author: "Some Name",
tags: ["tag1", "tag2", "tag3"],
...
}
db.posts.aggregate([
{$unwind: "$tags"},
{$group:
{
_id: "$tags",
count: {$sum:1},
}
},
...
])
Теперь каждый документ с массивом тегов превращется в N документов(где N размер массива). Все поля повторяются, а tags становится полем со значением из елемента массива.
Нужно изучать отдельной темой:
http://docs.mongodb.org/manual/reference/sql-aggregation-comparison/
Ограничения в Aggregation
1. Мы имеем ограничение 100 Мб оперируемых данных в pipeline stages. Эта проблема решается allowDiskUse директивой, но у нас падает продуктивность/запроса.2. Если мы хотим вернуть результат одним документом, то всегда нужно помнить о ограничении в 16 Мб на такой документ, чтобы не сталкиваться с таким ограничением - пользуемся cursor.
3. Если вызывать на шардированной системе group by или sort, у нас общий результат со всех шардов будет возвращен на первый шард(праймери шард) и там будет пытаться в его оперативки сделаться групировка и/или сортировка. Это конечно же может завалить этот шард, эта проблема решается например Hadoop-ом.
Комментариев нет:
Отправить комментарий