Подключение к Mongo-серверу осуществляется с помощью утилиты Mongo (в докер-контейнере она уже установлена)
python3 upstart.py -s mongo
Результат
MongoDB shell version v4.0.0
connecting to: mongodb://mongo_host:27017/
MongoDB server version: 4.0.0
Welcome to the MongoDB shell.
For interactive help, type "help".
---
>
В консоли существует множество команд
stats - статистика MongoDB
db.stats()
Результат
{
"db" : "test",
"collections" : 0,
"views" : 0,
"objects" : 0,
"avgObjSize" : 0,
"dataSize" : 0,
"storageSize" : 0,
"numExtents" : 0,
"indexes" : 0,
"indexSize" : 0,
"fileSize" : 0,
"fsUsedSize" : 0,
"fsTotalSize" : 0,
"ok" : 1
}
Переключимся в схему данных test_db
use test_db
Результат
switched to db test_db
Добавим в коллекцию документов test_collection
один документ
db.test_collection.insert({name: 'Pepe', gender: 'm', weight: 40})
Результат
WriteResult({ "nInserted" : 1 })
Снова посмотрим статистику по БД - мы увидим одну созданную коллекцию:
db.stats()
Результат
{
"db" : "test_db",
"collections" : 1,
"views" : 0,
"objects" : 1,
"avgObjSize" : 67,
"dataSize" : 67,
"storageSize" : 4096,
"numExtents" : 0,
"indexes" : 1,
"indexSize" : 4096,
"fsUsedSize" : 42377572352,
"fsTotalSize" : 244529655808,
"ok" : 1
}
Как видно, у нас в БД появилась новая коллекция и один объект. В любой момент мы можем проверить, какие объекты хранятся в БД:
db.getCollectionNames()
Результат
[ "test_collection" ]
Для фильтрации записей используем find (аналог WHERE из SQL)
db.test_collection.find({'name': 'Pepe'})
Результат
{ "_id" : ObjectId("5b56a66a64669cd544d5fd73"), "name" : "Pepe", "gender" : "m", "weight" : 40 }
JSON, который передaётся в функцию find называется "селектор". Селектор формирует набор документов из коллекции, которые отвечают уcловиям, перечисленным в селекторе.
Добавим новый документ в коллекцию с отличающимся набором полей:
db.test_collection.insert({name: 'Lolo', gender: 'f', home: 'Moscow', student: false})
Результат
WriteResult({ "nInserted" : 1 })
Воспользуемся командой find()
, которая является аналогом для SELECT в стандарте SQL.
db.test_collection.find()
Результат
{ "_id" : ObjectId("5b56a66a64669cd544d5fd73"), "name" : "Pepe", "gender" : "m", "weight" : 40 }
{ "_id" : ObjectId("5b56b32e64669cd544d5fd74"), "name" : "Lolo", "gender" : "f", "home" : "Moscow", "student" : false }
Мы добавили в нашу коллекцию новый документ с другим набором полей. Такое добавление было бы невозможно в реляционной БД, где набор полей фиксируется в момент создания таблицы.
Для удаления записей используется функция .remove()
, в которую нужно передать селектор
db.test_collection.remove({home: "Moscow"})
Результат
WriteResult({ "nRemoved" : 1 })
Проверим, какие элементы остались в коллекции
db.test_collection.find()
Результат
{ "_id" : ObjectId("5b56a66a64669cd544d5fd73"), "name" : "Pepe", "gender" : "m", "weight" : 40 }
В селекторы можно передавать специальные операторы, которые эквивалентны условиям в WHERE запросов SQL:
db.test_collection.find({gender: 'm', weight: {$gt: 700}})
Тут ничего не нашли - это ожидаемо. Поправим условие в селекторе:
db.test_collection.find({gender: 'm', weight: {$lt: 700}})
Результат
{ "_id" : ObjectId("5b56a66a64669cd544d5fd73"), "name" : "Pepe", "gender" : "m", "weight" : 40 }
Так, например, оператор $exists проверяет наличие у объекта того или иного поля. Внутри селектора условия можно объединять с помощью оператора $or
db.test_collection.find({$or: [{gender: 'm'}, {home: 'Moscow'}]})
Результат
{ "_id" : ObjectId("5b56a66a64669cd544d5fd73"), "name" : "Pepe", "gender" : "m", "weight" : 40 }
{ "_id" : ObjectId("5b56b78664669cd544d5fd75"), "name" : "Lolo", "gender" : "f", "home" : "Moscow", "student" : false }
Обновление производится функцией update, попробуем её применить
db.test_collection.update({home: 'Moscow'},{student: true})
Результат
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
Проверим, что новый документ появился в коллекции
db.test_collection.find({home: 'Moscow'})
Ничего не нашли, что случилось? Выведем все документы коллекции
db.test_collection.find()
Результат
{ "_id" : ObjectId("5b56a66a64669cd544d5fd73"), "name" : "Pepe", "gender" : "m", "weight" : 40 }
{ "_id" : ObjectId("5b56b78664669cd544d5fd75"), "student" : true }
Функция update не просто изменила это поля, а переписала весь документ. Чтобы такого не произошло, нужно использовать модификатор $set
Вернём нашу запись на место
db.test_collection.insert({name: 'Lolo', gender: 'f', home: 'Moscow', student: false})
Результат
WriteResult({ "nInserted" : 1 })
Проведём правильный update
db.test_collection.update({home: 'Moscow'}, {$set: {student: true}})
Результат
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
Проверим, что всё успешно
db.test_collection.find()
Результат
{ "_id" : ObjectId("5b571bf081d67789509607f1"), "name" : "Lolo", "gender" : "f", "home" : "Moscow", "student" : true }
Третий параметр в методе update позволяет создать запись, если она отсутствует - т.н. upsert (UPDATE + INSERT)
Попробуем выполнить update записи, которой нет
db.test_collection.update({home: 'Perm'}, {$set: {student: false}})
Результат
WriteResult({ "nMatched" : 0, "nUpserted" : 0, "nModified" : 0 })
Как изменилась коллекция? Никак =[
db.test_collection.find()
Результат
{ "_id" : ObjectId("5b571bf081d67789509607f1"), "name" : "Lolo", "gender" : "f", "home" : "Moscow", "student" : true }
Попробуем функционал UPSERT:
db.test_collection.update({home: 'Perm'}, {$set: {student: false}}, true)
Результат
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("5b57207bbff183a0a45dcfb3")
})
Проверим, как изменилась коллекция
db.test_collection.find()
Результат
{ "_id" : ObjectId("5b571bf081d67789509607f1"), "name" : "Lolo", "gender" : "f", "home" : "Moscow", "student" : true }
{ "_id" : ObjectId("5b57207bbff183a0a45dcfb3"), "home" : "Perm", "student" : false }
По умолчанию будет обновлён один из документов, попадающих под условия селектора. Четвёртый параметр команды позволяет осуществлять массовую вставку (обновление) документов.
Есть другие модификаторы, например $inc (увеличивает скаляр) или $push (добавляет в массив) - о них можно почитать в документации по Mongo
Отсутствуют джойны. Чтобы как-то моделировать джойны предлагается особенным образом формировать документы - чтобы сохранять связи между различными коллекциями
db.posts.insert({day: 'Wed', author: ObjectId("5b571bf081d67789509607f1")})
db.posts.insert({day: 'Sat', author: ObjectId("5b571bf081d67789509607f1")})
db.posts.insert({day: 'Sat', author: ObjectId("5b57207bbff183a0a45dcfb3")})
Проверим, что записалось в БД
db.posts.find()
Результат
{ "_id" : ObjectId("5b57392b81d67789509607f2"), "day" : "Wed", "author" : ObjectId("5b571bf081d67789509607f1") }
{ "_id" : ObjectId("5b57393481d67789509607f3"), "day" : "Sat", "author" : ObjectId("5b571bf081d67789509607f1") }
{ "_id" : ObjectId("5b57394381d67789509607f4"), "day" : "Sat", "author" : ObjectId("5b57207bbff183a0a45dcfb3") }
Вместо джойна используем find по полю ObjectId
db.posts.find({author: ObjectId("5b571bf081d67789509607f1")})
Результат
{ "_id" : ObjectId("5b57392b81d67789509607f2"), "day" : "Wed", "author" : ObjectId("5b571bf081d67789509607f1") }
{ "_id" : ObjectId("5b57393481d67789509607f3"), "day" : "Sat", "author" : ObjectId("5b571bf081d67789509607f1") }
Пo умолчанию возвращаются все поля документа. Это поведение можно изменить:
db.posts.find({author: ObjectId("5b571bf081d67789509607f1")}, {day: 1})
Результат
{ "_id" : ObjectId("5b57392b81d67789509607f2"), "day" : "Wed" }
{ "_id" : ObjectId("5b57393481d67789509607f3"), "day" : "Sat" }
Служебное поле _id возвращается всегда - его можно выключить в явном виде
db.posts.find({author: ObjectId("5b571bf081d67789509607f1")}, {day: 1, _id: 0})
Результат
{ "day" : "Wed" }
{ "day" : "Sat" }
В остальных случаях включение и исключение полей нельзя смешивать.
Порядок выдачи изменяется командой .sort():
db.posts.find({author: ObjectId("5b571bf081d67789509607f1")}, {day: 1, _id: 0}).sort({day: 1})
Результат
{ "day" : "Sat" }
{ "day" : "Wed" }
или в обратном порядке
db.posts.find({author: ObjectId("5b571bf081d67789509607f1")}, {day: 1, _id: 0}).sort({day: -1})
Результат
{ "day" : "Wed" }
{ "day" : "Sat" }
Так же можно управлять количеством записей в выдаче и смещением от начала списка
Полная выдача:
db.posts.find({author: ObjectId("5b571bf081d67789509607f1")}, {day: 1, _id: 0}).sort({day: -1})
Результат
{ "day" : "Wed" }
{ "day" : "Sat" }
Задаём смещение
db.posts.find({author: ObjectId("5b571bf081d67789509607f1")}, {day: 1, _id: 0}).sort({day: -1}).limit(2).skip(1)
Результат
{ "day" : "Sat" }
Гибкость курсора позволяет управлять нагрузкой на Mongo, так как курсор умеет делать смещения без непосредственного чтения записей
Для загрузки JSON выполним скрипт формирования JSON - это будут случайные картинки собачек, получаемых по API
rm -f test.json; for i in $(seq 100 $END); do curl "https://dog.ceo/api/breeds/image/random">>test.json; done;
На выходе получаем JSON-файл
head test.json;
Результат
{"status":"success","message":"https:\/\/images.dog.ceo\/breeds\/weimaraner\/n02092339_1013.jpg"},
{"status":"success","message":"https:\/\/images.dog.ceo\/breeds\/basenji\/n02110806_5971.jpg"},
{"status":"success","message":"https:\/\/images.dog.ceo\/breeds\/dingo\/n02115641_5815.jpg"},
{"status":"success","message":"https:\/\/images.dog.ceo\/breeds\/corgi-cardigan\/n02113186_10475.jpg"},
{"status":"success","message":"https:\/\/images.dog.ceo\/breeds\/boxer\/n02108089_1357.jpg"},
/ #
ВНИМАНИЕ Генерировать файл не надо, он уже есть в исходном наборе
Подключаемся в bash
терминал контейнера
python3 upstart.py -s bash
Загрузим файл в Mongo с помощью утилиты mongoimport:
/usr/bin/mongoimport --host ${APP_MONGO_HOST} --port ${APP_MONGO_PORT} --db pets --collection dogs --file /usr/share/data_store/raw_data/dogs.json
Результат выполнения команды
2019-10-27T15:50:25.965+0000 connected to: mongo_host:27017
2019-10-27T15:50:25.999+0000 imported 100 documents
Проверим, что документы успешно загружены, подключившись к Mongo командой /usr/bin/mongo ${APP_MONGO_HOST}:${APP_MONGO_PORT}/pets
:
db.stats()
Результат
{
"db" : "pets",
"collections" : 1,
"views" : 0,
"objects" : 100,
"avgObjSize" : 115.46,
"dataSize" : 11546,
"storageSize" : 16384,
"numExtents" : 0,
"indexes" : 1,
"indexSize" : 16384,
"fsUsedSize" : 43341520896,
"fsTotalSize" : 244529655808,
"ok" : 1
}
Посмотрим на записи, которые появились в таблице
db.dogs.find().limit(3)
Результат
{ "_id" : ObjectId("5b5aad7b54c17bb03ac25e64"), "status" : "success", "message" : "https://images.dog.ceo/breeds/greyhound-italian/n02091032_9131.jpg" }
{ "_id" : ObjectId("5b5aad7b54c17bb03ac25e65"), "status" : "success", "message" : "https://images.dog.ceo/breeds/hound-afghan/n02088094_1534.jpg" }
{ "_id" : ObjectId("5b5aad7b54c17bb03ac25e66"), "status" : "success", "message" : "https://images.dog.ceo/breeds/chihuahua/n02085620_5093.jpg" }
Для группировки данных существует модификатор group - аналог GROUP BY в стандарте SQL. Его синтаксис:
{ $group: { _id: <expression>, <field1>: { <accumulator1> : <expression1> }, ... } }
Этот модификатор оборачивается в конструкцию db.dogs.aggregate([])
Подключаемся в Mongo
/usr/bin/mongo ${APP_MONGO_HOST}:${APP_MONGO_PORT}/pets
Применим к нашей игрушечной таблице aggregate()
:
db.dogs.aggregate([{$group: {_id: "$status"}}])
Это минимально допустимая конструкция, которая не делает ничего полезного, т.к. мы не указали агрегирующую функцию. Усложним пример, добавив простой счётчик
db.dogs.aggregate([{$group: {_id: "$status", dog_count: { $sum: 1 }}}])
Результат
{ "_id" : "success", "dog_count" : 200 }
Допустим, для предыдущего примера мы хотим посчитать не просто количество документов, а сумму длин поля $message.
Для этой задачи потребуется добавить к коллекции новое поле - message_len.
Строки в Mongo - объекты Javascript, то есть у них есть атрибут length
. Таким образом нам нужно применить функцию length каждому полю message всех документов коллекции.
Для этого мы используем метод курсора forEach и вызовем метод update для каждого документа:
db.dogs.find().forEach(function(doc){db.dogs.update({_id:doc._id}, {$set: {message_len: doc.message.length}})})
Теперь можно посчитать ещё один агрегат - сумму длин строк:
db.dogs.aggregate([{$group: {_id: "$status", tag_count: { $sum: 1 }, descr_len: {$sum: "$message_len"}}}])
{ "_id" : "success", "tag_count" : 200, "descr_len" : 11845 }