Открытый вебинар «Основы MongoDB»
Друзья, очередной запуск курса «Базы данных» состоится уже завтра, поэтому мы провели традиционный открытый урок, запись которого вы можете посмотреть здесь. В этот раз поговорили о популярной БД MongoDB: изучили некоторые тонкости, рассмотрели основы работы, возможности и архитектуру. А также коснулись некоторых User Cases.
Вебинар провёл Иван Ремень, руководитель направления серверной разработки в «Ситимобил».
Особенности MongoDB
MongoDB — документоориентированная СУБД с открытым исходным кодом, не требующая описания схемы таблиц. Она классифицируется как NoSQL и использует BSON (бинарный JSON). Масштабируется из коробки, написана на языке C++ и поддерживает синтаксис JavaScript. Поддержка SQL отсутствует.
У MongoDB есть драйверы для многих популярных языков программирования (Си, C++, C#, Go, Java, JavaScript, Perl, PHP, Python, Ruby и др.). Также есть неофициальные и поддерживаемые сообществом драйверы для прочих языков программирования.
Что же, давайте рассмотрим основные команды, которые могут быть полезны.
Итак, чтобы развернуть MongoDB в Docker, пишем:
docker run -it --rm -p 127.0.0.1:27017:27017
--name mongo-exp-project mongo
docker exec -it mongo-exp-project mongo
Таким образом, происходит запуск клиента MongoDB:
А теперь напишем традиционный Hello World:
print ("Hello world!”)
После этого — запустим цикл:
Как вы заметили, перед нами обычный JS, а MongoDB — это полноценный интерпретатор JavaScript.
Когда применять MongoDB?
Есть байка о том, что средний стартапер в кремниевой долине — это человек, который неделю назад открыл книжку «HTML для чайников». Какой он выберет стек? Согласитесь, что ему очень удобно, когда у него в браузере по очевидным причинам находится JavaScript, на сервере крутится Node.js, а в базе данных тоже JavaScript. Это момент № 1.
Во-вторых, есть прекрасное выступление Петра Зайцева, одного из лучших специалистов по базам данных в России. В нём Пётр рассказывает о MySQL и MongoDB, уделяя особое внимание тому, когда и что лучше использовать.
В-третьих, хочется подчеркнуть, что MongoDB характеризуется хорошей масштабируемостью — и это одна из ключевых особенностей БД. Если вы заранее не знаете, какая будет нагрузка, MongoDB прекрасно подойдёт. К тому же, она поддерживает такие паттерны из коробки, как шардирование и репликация, и всё это сделано достаточно прозрачным, то есть работать очень удобно.
Что касается терминологии в MongoDB, то:
- базы — это базы (схемы, совокупность таблиц);
- в MongoDB есть такое понятие, как коллекция — это аналог таблицы и набор документов, которые по логике вещей должны быть связаны;
- документы — это аналог строки.
Создание базы данных и простые запросы
Чтобы создать базу данных, нужно просто начать её использовать:
use learn
Теперь сделаем небольшую вставочку документа. Пусть это будет, к примеру, единорог с именем Аврора:
db.unicorns.insert({name: 'Aurora', gender: 'f', weight: 450})
db — глобальный объект для доступа к БД, то есть, по сути, сама «монга». Для шардинга используется sh, для репликации — rs.
Какие команды есть у объекта db:
Итак, вернёмся к нашей команде, в результате применения которой консоль сообщит, что вставлена одна строка:
Слово unicorns
в команде db.unicorns.insert({name: 'Aurora', gender: 'f', weight: 450})
обозначает коллекцию. Здесь обратите внимание, что мы коллекцию не описывали и не создавали, а просто написали «unicorns», сделали insert, и у нас появилась коллекция.
А вот так мы сможем получить все наши коллекции:
db.getCollectionNames()
Ну и так далее. Можем вставить ещё одну коллекцию:
А теперь запросим полную коллекцию (напоминаем, что в нашем случае в базе данных уже находится информация о двух единорогах с одинаковым именем):
db.unicorns.find()
Обратите внимание, вот и наш JSON (есть имя, пол, вес, некий уникальный идентификатор объекта):
Теперь давайте вставим ещё парочку единорогов с одинаковыми именами:
db.unicorns.insert({name: 'Leto', gender: 'm',
home: 'Arrakeen', worm: false})
db.unicorns.insert({name: 'Leto', gender: 'm',
home: 'Arrakeen', worm: false})
И посмотрим, что получилось:
Как видите, у нас появились дополнительные поля: home и worm, которых нет у Авроры.
Добавим ещё несколько единорогов:
db.unicorns.insertMany([{name: 'Horny', dob: new Date(1992,2,13,7,47), loves: ['carrot','papaya'], weight: 600, gender: 'm', vampires: 63},
{name: 'Aurora', dob: new Date(1991, 0, 24, 13, 0), loves: ['carrot', 'grape'], weight: 450, gender: 'f', vampires: 43},
{name: 'Unicrom', dob: new Date(1973, 1, 9, 22, 10), loves: ['energon', 'redbull'], weight: 984, gender: 'm', vampires: 182},
{name: 'Roooooodles', dob: new Date(1979, 7, 18, 18, 44), loves: ['apple'], weight: 575, gender: 'm', vampires: 99}])
Итак, мы вставили с помощью JavaScript ещё четыре объекта:
Как вы думаете, в каких БД удобнее хранить паспортные данные: в реляционных БД или в монге?
Ответ очевиден — в монге, и вышеописанный пример хорошо это показывает. Не секрет, что КЛАДР — это боль в РФ. А монга очень хорошо ложится на адреса, ведь можно задать всё как массив, и будет гораздо проще жить. И это хороший User Case для MongoDB.
Добавим ещё единорогов:
db.unicorns.insert({name: 'Solnara', dob: new Date(1985, 6, 4, 2, 1), loves:['apple', 'carrot', 'chocolate'], weight:550, gender:'f', vampires:80});
db.unicorns.insert({name:'Ayna', dob: new Date(1998, 2, 7, 8, 30), loves: ['strawberry', 'lemon'], weight: 733, gender: 'f', vampires: 40});
db.unicorns.insert({name:'Kenny', dob: new Date(1997, 6, 1, 10, 42), loves: ['grape', 'lemon'], weight: 690, gender: 'm', vampires: 39});
db.unicorns.insert({name: 'Raleigh', dob: new Date(2005, 4, 3, 0, 57), loves: ['apple', 'sugar'], weight: 421, gender: 'm', vampires: 2});
db.unicorns.insert({name: 'Leia', dob: new Date(2001, 9, 8, 14, 53), loves: ['apple', 'watermelon'], weight: 601, gender: 'f', vampires: 33});
db.unicorns.insert({name: 'Pilot', dob: new Date(1997, 2, 1, 5, 3), loves: ['apple', 'watermelon'], weight: 650, gender: 'm', vampires: 54});
db.unicorns.insert({name: 'Nimue', dob: new Date(1999, 11, 20, 16, 15), loves: ['grape', 'carrot'], weight: 540, gender: 'f'});
db.unicorns.insert({name: 'Dunx', dob: new Date(1976, 6, 18, 18, 18), loves: ['grape', 'watermelon'], weight: 704, gender: 'm', vampires: 165});
А теперь обратите внимание на документы. В качестве dob у нас хранятся целые объекты. А ещё есть информация о том, что любит единорог, причём эти данные есть не у всех. Таким образом, внутри лежит полноценный массив.
Кстати, для более красивого вывода результатов, можно в конце команды поиска вызвать метод .pretty()
:
Если нужно получить информацию о последней ошибке, используем следующую команду:
db.getLastError()
Это можно делать после каждой вставки, либо настроить Write Concern. Лучше почитать об этом в официальной документации, которая, кстати, в монге весьма содержательная. Кстати, на хабре тоже есть неплохая статья по этому поводу.
Переходим к более сложным запросам
Запрос по точному значению поля:
db.unicorns.find({gender: 'm'})
Написав такой запрос, мы получим в выводе на консоли список всех единорогов-мужчин.
Также можно выполнить запрос сразу по нескольким полям: по полу и по весу:
Выше обратите внимание на специальный селектор $gt, который позволяет вывести всех единорогов мужского пола весом более 700.
Можно проверить, существует ли поле вообще:
db.unicorns.find({vampires: {$exists: false}})
Или так:
db.unicorns.find({'parents.father': {$exists: true}})
Следующая команда выведет единорогов, имена которых начинаются с букв А или а:
db.unicorns.find({name: {$regex: "^[Aa]"}})
Теперь рассмотрим поиск по массиву. Вопрос № 1: что выведет эта команда:
db.unicorns.find({loves:'apple'})
Правильно: всех, кто любит яблоки.
Следующая команда вернёт лишь те данные о единороге, в которых содержатся только яблоки и арбузы:
db.unicorns.find({loves:[ "apple", "watermelon" ]})
И ещё одна команда:
db.unicorns.find({loves:[ "watermelon", "apple" ]})
В нашем случае она ничего не вернёт, так как, когда мы передаём массив, сравнивается первый элемент с первым, второй со вторым и т. д. То есть массив должен совпадать ещё и по позициям этих значений.
А вот так выглядит поиск по массиву с использованием оператора «ИЛИ»:
Следующий пример продемонстрирует нам поиск с использованием оператора $all. И здесь уже последовательность непринципиальна:
Также мы можем искать и по размеру массива:
А что делать, если мы хотим найти массив, у которого размер больше единицы? Для этого существует оператор $where, с помощью которого можно писать более сложные вещи:
db.unicorns.find({$where: function() { return this.loves && (this.loves.length > 1) } })
Кстати, если хотите попрактиковаться, вот вам файлик с командами.
Особенности курсора
Немного отвлечёмся и скажем пару слов про особенности монги:
- find () и другие операции не возвращают данные — они возвращают так называемый «курсор»;
- то, что мы видим, как данные печатаются, есть работа интерпретатора.
Набрав db.unicorns.find без скобок, мы получим подсказку:
Продолжаем выполнять запросы
Есть ещё оператор $in:
db.unicorns.find({weight: {$in: [650, 704]}})
Теперь поговорим про update. Например, давайте изменим вес единорога Roooooodles:
db.unicorns.update({name: "Roooooodles"}, {weight: 2222})
В результате наших действий документ полностью обновится, а в нём останется только одно указанное поле:
То есть единственное, что останется у нашего объекта — это вес 2222 и, разумеется, id.
Исправить ситуацию можно с помощью $set:
db.unicorns.update({_id: ObjectId("5da6ea4d9703b8be0089e6db")}, {$set: { "name" : "Roooooodles", "dob" : ISODate("1979-08-18T18:44:00Z"), "loves" : [ "apple" ], "gender" : "m", "vampires" : 99}})
Также есть возможность инкрементировать значения:
А ещё есть upsert — комбинация update и insert:
А вот как осуществляется выборка полей:
Остаётся добавить пару слов про skip и limit:
Коллеги, на этом всё, если хотите узнать подробности, смотрите видео целиком. И не забывайте оставлять свои комментарии!