Открытый вебинар «Основы MongoDB»

Друзья, очередной запуск курса «Базы данных» состоится уже завтра, поэтому мы провели традиционный открытый урок, запись которого вы можете посмотреть здесь. В этот раз поговорили о популярной БД MongoDB: изучили некоторые тонкости, рассмотрели основы работы, возможности и архитектуру. А также коснулись некоторых User Cases.

sx2-grmgvwy-o08yp2jovyirre4.jpeg

Вебинар провёл Иван Ремень, руководитель направления серверной разработки в «Ситимобил».



Особенности 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:

jztomgzelwv3sg-nux-dc423lja.jpeg

А теперь напишем традиционный Hello World:

print ("Hello world!”)


jo7tvnxidl2lfb5bzcn-zu9wqyo.jpeg

После этого — запустим цикл:

-7jweaubyu6romt4twjtipbi5wa.jpeg

Как вы заметили, перед нами обычный JS, а MongoDB — это полноценный интерпретатор JavaScript.

Когда применять MongoDB?


Есть байка о том, что средний стартапер в кремниевой долине — это человек, который неделю назад открыл книжку «HTML для чайников». Какой он выберет стек? Согласитесь, что ему очень удобно, когда у него в браузере по очевидным причинам находится JavaScript, на сервере крутится Node.js, а в базе данных тоже JavaScript. Это момент № 1.

Во-вторых, есть прекрасное выступление Петра Зайцева, одного из лучших специалистов по базам данных в России. В нём Пётр рассказывает о MySQL и MongoDB, уделяя особое внимание тому, когда и что лучше использовать.

В-третьих, хочется подчеркнуть, что MongoDB характеризуется хорошей масштабируемостью — и это одна из ключевых особенностей БД. Если вы заранее не знаете, какая будет нагрузка, MongoDB прекрасно подойдёт. К тому же, она поддерживает такие паттерны из коробки, как шардирование и репликация, и всё это сделано достаточно прозрачным, то есть работать очень удобно.

Что касается терминологии в MongoDB, то:

  • базы — это базы (схемы, совокупность таблиц);
  • в MongoDB есть такое понятие, как коллекция — это аналог таблицы и набор документов, которые по логике вещей должны быть связаны;
  • документы — это аналог строки.


Создание базы данных и простые запросы


Чтобы создать базу данных, нужно просто начать её использовать:

use learn


tdhy90yakxehjtfbjegccw27lfc.jpeg

Теперь сделаем небольшую вставочку документа. Пусть это будет, к примеру, единорог с именем Аврора:

db.unicorns.insert({name: 'Aurora', gender: 'f', weight: 450})


db — глобальный объект для доступа к БД, то есть, по сути, сама «монга». Для шардинга используется sh, для репликации — rs.

Какие команды есть у объекта db:

_9kho_1yywo-7uqvglreimpwd_8.jpeg

Итак, вернёмся к нашей команде, в результате применения которой консоль сообщит, что вставлена одна строка:

udkeqsk3bfngttzs2p_nyw4a6ju.jpeg

Слово unicorns в команде db.unicorns.insert({name: 'Aurora', gender: 'f', weight: 450}) обозначает коллекцию. Здесь обратите внимание, что мы коллекцию не описывали и не создавали, а просто написали «unicorns», сделали insert, и у нас появилась коллекция.

А вот так мы сможем получить все наши коллекции:

db.getCollectionNames()


Ну и так далее. Можем вставить ещё одну коллекцию:

pdzxqnt0hgfowtjxd20iydadd3u.jpeg

А теперь запросим полную коллекцию (напоминаем, что в нашем случае в базе данных уже находится информация о двух единорогах с одинаковым именем):

db.unicorns.find()


Обратите внимание, вот и наш JSON (есть имя, пол, вес, некий уникальный идентификатор объекта):

54ow75y40hhp-ticbi6l1bcssw8.jpeg

Теперь давайте вставим ещё парочку единорогов с одинаковыми именами:

db.unicorns.insert({name: 'Leto', gender: 'm', 
home: 'Arrakeen', worm: false}) 
db.unicorns.insert({name: 'Leto', gender: 'm', 
home: 'Arrakeen', worm: false})


И посмотрим, что получилось:

zqkuvlfg21n3_pmigl7m6trfud0.jpeg

Как видите, у нас появились дополнительные поля: 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 ещё четыре объекта:

niuqwlicbep2agmmqgkxlsl2obg.jpeg

Как вы думаете, в каких БД удобнее хранить паспортные данные: в реляционных БД или в монге?

Ответ очевиден — в монге, и вышеописанный пример хорошо это показывает. Не секрет, что КЛАДР — это боль в РФ. А монга очень хорошо ложится на адреса, ведь можно задать всё как массив, и будет гораздо проще жить. И это хороший 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});


xpca_noi3_eewunygplsma3a6qk.jpeg

А теперь обратите внимание на документы. В качестве dob у нас хранятся целые объекты. А ещё есть информация о том, что любит единорог, причём эти данные есть не у всех. Таким образом, внутри лежит полноценный массив.

Кстати, для более красивого вывода результатов, можно в конце команды поиска вызвать метод .pretty():

qc86rgjozt225q6gwk3nln4kaew.jpeg

Если нужно получить информацию о последней ошибке, используем следующую команду:

db.getLastError()


Это можно делать после каждой вставки, либо настроить Write Concern. Лучше почитать об этом в официальной документации, которая, кстати, в монге весьма содержательная. Кстати, на хабре тоже есть неплохая статья по этому поводу.

Переходим к более сложным запросам


Запрос по точному значению поля:

db.unicorns.find({gender: 'm'})


Написав такой запрос, мы получим в выводе на консоли список всех единорогов-мужчин.

Также можно выполнить запрос сразу по нескольким полям: по полу и по весу:

5mfvcuvnge7bfck_iuslb3nchda.jpeg

Выше обратите внимание на специальный селектор $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" ]})


В нашем случае она ничего не вернёт, так как, когда мы передаём массив, сравнивается первый элемент с первым, второй со вторым и т. д. То есть массив должен совпадать ещё и по позициям этих значений.

А вот так выглядит поиск по массиву с использованием оператора «ИЛИ»:

bq7wjjlkqc6ginsf3h3xful1fp8.jpeg

Следующий пример продемонстрирует нам поиск с использованием оператора $all. И здесь уже последовательность непринципиальна:

1efl3te8tdjvnmhigrrodnp9ces.jpeg

Также мы можем искать и по размеру массива:

dhiarjanv17ixwbwrzjdqyjfdgw.jpeg

А что делать, если мы хотим найти массив, у которого размер больше единицы? Для этого существует оператор $where, с помощью которого можно писать более сложные вещи:

db.unicorns.find({$where: function() { return this.loves && (this.loves.length > 1) } })


Кстати, если хотите попрактиковаться, вот вам файлик с командами.

Особенности курсора


Немного отвлечёмся и скажем пару слов про особенности монги:

  • find () и другие операции не возвращают данные — они возвращают так называемый «курсор»;
  • то, что мы видим, как данные печатаются, есть работа интерпретатора.


Набрав db.unicorns.find без скобок, мы получим подсказку:

0sariyefxtiuz19de2rieo8zt00.jpeg

Продолжаем выполнять запросы


Есть ещё оператор $in:

db.unicorns.find({weight: {$in: [650, 704]}})


zl72zu58hlfixg8bjl4grd-osxq.jpeg

Теперь поговорим про update. Например, давайте изменим вес единорога Roooooodles:

db.unicorns.update({name: "Roooooodles"}, {weight: 2222})


В результате наших действий документ полностью обновится, а в нём останется только одно указанное поле:

zk-ip7aduwxpw_nhqme-ladyy8s.jpeg

То есть единственное, что останется у нашего объекта — это вес 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}})

adhqjczlthw_wu81_2jqb9gxnro.jpeg

Также есть возможность инкрементировать значения:

fbw9pjxmzfr_vfls6jshqhhda0i.jpeg

А ещё есть upsert — комбинация update и insert:

d_mfyrplzfsf9_hs5izjshl2vmw.jpeg

А вот как осуществляется выборка полей:

tvwhgr6sd8puyxqur66dloianna.jpeg

zrstc599ywafkk1t5p2chunhtao.jpeg

Остаётся добавить пару слов про skip и limit:

n5ihiygbx7gnhmpndif_nxlv3ls.jpeg

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

© Habrahabr.ru