OTUS. Онлайн-образование corporate blog
MongoDB
Database Administration
31 October 2019

Открытый вебинар «Основы 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:



Коллеги, на этом всё, если хотите узнать подробности, смотрите видео целиком. И не забывайте оставлять свои комментарии!

+11
3.1k 70
Comments 6