Практическое применение
MongoDB Aggregation
Framework
Hi! I’m Max Dadzerkin.
MongoDB?
W
H
Y
Базовые понятия MongoDB
*Документо-ориентированная БД общего
назначения;
*Структурные единицы:
• базы данных (databases);
• коллекции (collections);
• документы (documents);
• поля (fields).
• Документы хранятся в формате BSON (Binary JSON);
MongoDB
• Поддерживаемые типы данных:
числа, строки, даты, регулярные выражения, массивы, вложенные документы, Objectld, бинарные данные;
• Отсутствует предопределенная схема документа.
{
"_id": ObjectId("4b2d75476cc613d5ee930164"),
"type": "",
"active": "true",
"created": ISODate("2013-10-10T18:20:25.583Z"),
"likes": 7,
"text": "Post text",
"tags": ["tag1", "tag2"],
"author": {
"name": "author name",
"email": "[email protected]"
}
}
{
"_id": ObjectId("4b2d75476cc613d5ee95474d"),
"type": "comment",
"created": ISODate("2013-10-10T18:20:25.583Z"),
"moderated": false,
"text": "Comment text",
"author": {
"name": "author name",
"email": "[email protected]"
}
}
SCALABLE
горизонтальное
масштабирование
с помощью шардинга (sharding)
НАДЕЖНОСТЬ
Replica sets
СКОРОСТЬ
• Денормализация данных;
• Индексы;
• Атомарные операции.
Aggregation Framework
(1) Был добавлен в MongoDB 2.2 для выполнения
простых операций агрегации;
(2) Содержит набор операций, которые могут
быть объединены в цепочки (результат
предыдущей операции является входными
данными для последующей).
Aggregation Framework
КАК ЭТО РАБОТАЕТ?
(1) Определяем коллекцию, которую будем
трансформировать;
(2) Определяем цепочку операторов (pipeline);
(3) Коллекция проходит через цепочку;
(4) Результаты возвращаются как документ.
SQL Terms and Functions MongoDB Aggregation
Operators
WHERE $match
GROUP BY $group
HAVING $match
SELECT $project
ORDER BY $sort
Соответствие SQL и Aggregation операций
Простой пример
Документ
{
"name": "employee 1",
"position": "Junior",
"salary": 400
}
Вывести количество работников с группировкой по должности, у
которых зарплата не менее заданного значения
SQL запрос
SELECT position, count(*)
FROM employees
WHERE salary >= 500
GROUP BY position
Aggregation запрос
db.employees.aggregate([
{"$match": {"salary": {"$gte": 500}}},
{"$group": {"_id": "$position", "count": {"$sum": 1}}}
])
ЗАДАЧА
{
"result": [
{ "_id": "Junior", "count": 1 },
{ "_id": "Senior", "count": 50 },
...
],
"ok": 1
}
РЕЗУЛЬТАТ ЗАПРОСА
Пример реального
doc
{
"_id" : "16530152929",
"authorName" : "edcg",
"averageRating" : 3.625,
"comments" : [
{
"_id" : "443219543",
"moderationStatus" : "STATUS_NOT_MODERATED",
"authorName" : "pd14_98",
…
"commentType" : "NORMAL",
"commentBody" : "This comment is from someone",
}
],
"make" : "fiat",
"model" : "500",
"year" : "2012",
"moderationStatus" : "STATUS_PASSED",
"text" : "I've been waiting for this car to come to the US...",
"title" : "Fun, Cute but No Power“
…
}
фильтрует документы, используя существующий синтаксис для запросов
{ $match : { averageRating : { $gt : 4.0} } }
{
_id : 2,
averageRating : 4.025,
make : "fiat",
model : "500",
year : 2012
}
{
_id : 3,
averageRating : 3.5,
make : "fiat",
model : "500",
year : 2012
}
{
_id : 2,
averageRating : 4.025,
make : "fiat",
model : "500",
year : 2012
}
$match
(1) Меняет структуру документа;
(2) Добавляет, исключает или
переименовывает поля;
(3) Позволяет добавлять вычисляемые поля;
(4) Создает субдокументы.
$project
{
"_id" : "16530152929",
"authorName" : "edcg",
"make" : "fiat",
"model" : "500",
"year" : "2012",
"targetId" : "64851612",
"moderationStatus" :
"STATUS_PASSED",
"text" : "I've been waiting for this
car to come to the US...",
"title" : "Fun, Cute but No Power"
}
Включение/исключение полей/cоздание вложенного документа
$project: { _id: 0, title: 1, text: 1,
vehicle: { make: "$make", model: "$model", year: "$year" }
}
{
title : "Fun, Cute but No Power“,
text : "I've been waiting for this car
to come to the US...“,
vehicle: {
make: “fiat”,
model: “500“,
year: “2012”
}
}
(1) Группирует документы по ID: Поле, объект, константа
(2) Другие поля (кроме ID) вычисляются:
• Операторы группировки (group operators): $first, $last,
$max, $min, $avg, $sum…
• Операторы с логическими значениями (boolean
operators): $and, $or, $not
• Операторы сравнения (comparison operators): $eq, $gt,
$gte, $lt, $lte, $ne
• Условные выражения (conditional expressions): $cond,
$ifNull
…
$group
(1) Операция над полями типа массив;
(2) Возвращает новый документ для каждого элемента массива:
• Массив заменяется значением элемента;
• Если поле отсутствует или пустое, ничего не
возвращается;
• В случае, если поле имеет другой тип (не массив),
возвращается ошибка.
$unwind
{
"_id" : "16530152929",
"comments" : {
"type" : "NORMAL",
"body" : "comment"
}
"make" : "fiat",
"model" : "500",
"year" : "2012",
"title" : "Fun, Cute but No Power"
}
{
"_id": "16530152929",
"comments": [
{
"type": "NORMAL",
"body": "comment"
},
{
"type": "IMPORTANT",
"body": "important comment"
}
],
"make": "fiat",
"model": "500",
"year": "2012",
"title": "Fun, Cute but No Power"
}
{
"_id": "16530152929",
"comments": {
"type": "IMPORTANT",
"body": "important comment"
},
"make": "fiat",
"model": "500",
"year": "2012",
"title": "Fun, Cute but No Power"
}
$unwind { $unwind: "$comments" }
(1) Сортирует документы по одному или нескольким полям; (2) Позволяют реализовывать постраничный вывод.
$sort, $limit, $skip
ПРИМЕРЫ
Статистика по отзывам в определенном статусе с группировкой по make
{ "$match" : { "moderationStatus" : "STATUS_PASSED"} }, // возвращает все отзывы
в заданном статусе
{ "$group": { "_id": "$make", "counts": { "$sum": 1 } } } // суммирует количество
отзывов с группировкой по полю «make»
{
"result": [
…...
{
"_id": "chevrolet",
"counts": 25374
}
…...
],
"ok": 1
}
{ "$match" : { "comments.moderationStatus" : "STATUS_PASSED"} } , // возвращает
все отзывы, у которых есть комментарии в заданном статусе
{ "$unwind" : "$comments"} , // разворачивает массив комментариев
{ "$match" : { "comments.moderationStatus" : "STATUS_PASSED"} }, // фильтрует
документы по статусу комментария
{ "$group" : { "_id" : "$make" , "counts" : { "$sum" : 1} } } // суммирует количество
комментариев с группировкой по полю «make»
Количество утвержденных комментариев для каждого производителя (весь запрос)
{ "$match" : { "comments.moderationStatus" : "STATUS_PASSED"} }
{
"make": "bmw",
"comments": [
{
"body": "comment21",
"moderationStatus": "STATUS_REJECTED"
}
]
}
{
"make": "acura",
"comments": [
{
"body": "comment11",
"moderationStatus": "STATUS_PASSED"
},
{
"body": "comment 12",
"moderationStatus": "STATUS_REJECTED"
}
]
}
{
"make": "bmw",
"comments": [
{
"body": "comment21",
"moderationStatus": "STATUS_PASSED"
}
]
}
Количество утвержденных комментариев для каждого производителя (оператор 1)
{
"make": "bmw",
"comments": [
{
"body": "comment21",
"moderationStatus": "STATUS_PASSED"
}
]
}
{
"make": "acura",
"comments": [
{
"body": "comment11",
"moderationStatus": "STATUS_PASSED"
},
{
"body": "comment 12",
"moderationStatus": "STATUS_REJECTED"
}
]
}
{ "$unwind" : "$comments"}
{
"make": "acura",
"comments": [
{
"body": "comment11",
"moderationStatus": "STATUS_PASSED"
},
{
"body": "comment 12",
"moderationStatus": "STATUS_REJECTED"
}
]
}
{
"make": "bmw",
"comments": [
{
"body": "comment21",
"moderationStatus": "STATUS_PASSED"
}
]
}
{
"make": "acura",
"comments": {
"body": "comment11",
"moderationStatus": "STATUS_PASSED"
}
}
{
"make": "acura",
"comments": {
"body": "comment12",
"moderationStatus": "STATUS_REJECTED"
}
}
{
"make": "bmw",
"comments": {
"body": "comment21",
"moderationStatus": "STATUS_PASSED"
}
}
Количество утвержденных комментариев для каждого производителя (оператор 2)
{ "$match" : { "comments.moderationStatus" : "STATUS_PASSED "} }
{
"make": "acura",
"comments": {
"body": "comment11",
"moderationStatus": "STATUS_PASSED"
}
}
{
"make": "acura",
"comments": {
"body": "comment12",
"moderationStatus": "STATUS_REJECTED"
}
}
{
"make": "bmw",
"comments": {
"body": "comment21",
"moderationStatus": "STATUS_PASSED"
}
}
{
"make": "acura",
"comments": {
"body": "comment11",
"moderationStatus": "STATUS_PASSED"
}
}
{
"make": "bmw",
"comments": {
"body": "comment21",
"moderationStatus": "STATUS_PASSED"
}
}
Количество утвержденных комментариев для каждого производителя (оператор 3)
{ "$group" : { "_id" : "$make" , "counts" : { "$sum" : 1} } }
{
"make": "acura",
"comments": {
"body": "comment11",
"moderationStatus": "STATUS_PASSED"
}
}
{
"make": "bmw",
"comments": {
"body": "comment21",
"moderationStatus": "STATUS_PASSED"
}
}
{
"result": [
{
"_id": "acura",
"counts": 1
},
{
"_id": "bmw",
"counts": 1
}
],
"ok": 1
}
Количество утвержденных комментариев для каждого производителя (оператор 4)
Q&A
СПАСИБО.
Запрос со *
{
"$match": { "make": "audi", "model": "a4", "year": "2010", "moderationStatus": "STATUS_PASSED" }
} ,
{ "$sort": { "created": -1 } } ,
{ "$limit": 20 } ,
{ "$skip": 10 } ,
{ "$project":
{ "title": 1, "text": 1, "created": 1, "averageRating": 1, "targetId": 1, "comments": "$comments" }
} ,
{ "$unwind": "$comments" } ,
{
"$group": {
"_id": {
"_id": "$_id", "title": "$title", "targetId": "$targetId", "text": "$text", "created": "$created",
"averageRating": "$averageRating"
},
"commentsCount": { "$sum": 1 }
}
} ,
{
"$sort": {
"_id.created": -1
}
}
Постраничный вывод отзывов с подсчетом количества комментариев
{
"$match": { "make": "audi", "model": "a4", "year": "2010", "moderationStatus": "STATUS_PASSED" }
} ,
{
"$sort": { "created": -1 }
} ,
{ "$limit": 20 } ,
{ "$skip": 10 } ,
{
"$project": { "title": 1, "text": 1, "created": 1, "averageRating": 1, "targetId": 1,
"comments": {
"$ifNull": [ "$comments" , [ { "moderationStatus": "STATUS_REJECTED" } ] ]
}
}
} ,
{ "$unwind": "$comments" } ,
{ "$group": { "_id": { "_id": "$_id", "title": "$title", "targetId": "$targetId", "text": "$text",
"created": "$created", "averageRating": "$averageRating" },
"commentsCount": {
"$sum": { "$cond": [ { "$eq": [ "$comments.moderationStatus" , "STATUS_REJECTED" ] } , 0 , 1 ] }
}
}
} ,
{ "$sort": { "_id.created": -1 } }
Постраничный вывод отзывов с подсчетом количества комментариев (исправленная версия)
{
"result": [
{
"_id": {
"_id": "206121807",
"title": "Fantastic",
"targetId": "101186697",
"text": "My third Audi A4 and my favorite so far.",
"created": ISODate("2010-08-31T16:23:25Z"),
"averageRating": 5
},
"commentsCount": 5
},
{
"_id": {
"_id": "206121808",
"title": "Awesome",
"targetId": "101186697",
"text": "Audi A4 is my favorite car ever",
"created": ISODate("2010-09-14T13:16:21Z"),
"averageRating": 5
},
"commentsCount": 0
}
……...
],
"ok": 1
}
Постраничный вывод отзывов с подсчетом количества комментариев (результат запроса)