Друзья, очередной запуск курса «Базы данных» состоится уже завтра, поэтому мы провели традиционный открытый урок, запись которого вы можете посмотреть здесь. В этот раз поговорили о популярной БД 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:



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

Комментарии (6)


  1. x67
    31.10.2019 22:45

    Ответ очевиден — в монге, и вышеописанный пример хорошо это показывает

    А если ответ не очевиден, а аргументация непонятна, обновление монги до более новой версии не поможет?


  1. moscas
    01.11.2019 11:42

    Мы в DataGrip делаем поддержку Монги, которая выйдет в декабре. Будем рады, если вы попробуете и расскажете и своих впечатлениях, что можно улучшить и так далее.


    1. stgunholy
      01.11.2019 16:52

      А как плагин к идее можно использовать?


      1. moscas
        01.11.2019 16:56

        Конечно


        1. stgunholy
          01.11.2019 17:04

          Отлично! а где можно тогда о впечатлениях рассказать? :)


          1. moscas
            01.11.2019 17:09

            Напишите на datagrip@jetbrains.com. Спасибо!