MongoDb for developers. Неделя 1

    Вечер добрый, хабр. На прошлой неделе стартовал курс «MongoDb for developers» от 10gen, о котором уже писали на хабре. Если вы смотрели уроки, то можете смело проходить мимо. Остальным — добро пожаловать.

    В этой статье будет изложен основной материал первой недели обучения. Если аудитория проявит интерес — то подобные посты будут выходить в конце каждой недели.

    Мы вкратце рассмотрим, что представляет собой MongoDB, сравним разницу в структурах данных между монго и реляционными базами для простого веб-приложения, поиграемся с шеллом, и немножко покодим на пхп и питоне.

    Зачем эта статья? Предвижу подобный вопрос. Не все успели записаться на курсы, не у всех есть достаточно свободного времени, не у всех хорошо обстоят дела с восприятием устной английской речи. Ну и для гуглящих подобный материал не помешает.


    MongoDb — не реляционная база данных, предназначенная для хранения JSON-документов. Рассмотрим некий абстрактный экземпляр коллекции монго
    {a:3
    b:7
    fruit:["apple","banana","peach"]}

    Мы видим, что данный документ имеет целочисленные ключи a и b и ключ fruit который является массивом. В реляционных базах подобное поведение невозможно. Стоит отметить, что подобное представление данных гораздо ближе к коду, чем то с которым мы имеем дело работая с реляционными базами данных.

    Коллекции в MongoDb не привязаны к заранее определенной схеме (schemaless). Например следующие документы, могут быть элементами одной коллекции
    {a:1,b2}
    {a:2,b:4,c:10}

    Вспомните, сколько раз вам приходилось скрипя зубами делать ALTER TABLE, добавляя новое поле, которое то и использовать будут далеко не все элементы? С MongoDb подобное осталось в прошлом.

    Если посмотреть на криво-нарисованную схемку ниже, видно что в мире хранения данных, есть два противоположных полюса. Шустрые, но бедные функциональностью хранилища ключ-значения (такие как memcached) и крайне функциональные реляционные базы данных, имеющие впрочем проблемы с производительностью и масштабируемостью


    Одна из основополагающих идей MongoDb — предоставлять широкий набор возможностей, при сохранении высокой производительности. Конечно чем-то приходится жертвовать. Так например Монго не имеет аналога join: нельзя объединить элементы двух разных коллекций. Вторым важным отличием от реляционных баз данных является отсутствие транзакций. Последнее звучит пугающе, но дело в том, что в монго вам не понадобятся транзакции, в тех ситуациях где они были бы необходимы при использовании реляционной базы.

    От слов к делу
    Для продолжения нам потребуется установить MongoDb. Вы скорее всего найдете свежую версию в репозитории своего дистрибутива, в случае отсутствия таковой- исходники доступны на официальном сайте. Там же есть бинарники для win и mac. Стоит отметить, что 32х-битная версия имеет существенные ограничения по объему хранимых данных, так что ее можно использовать только для разработки. Сейчас для нас это не принципиально.
    Устанавливаем, запускаем и заходим в шелл.

    dirtyhack@dirtyhack-ThinkPad-Edge-E125:~$ mongo
    MongoDB shell version: 2.0.4
    connecting to: test
    > 
    


    Пока у нас нет никаких данных, монго создал для нас базу test, с которой мы и будем работать. Положим в нее наш первый элемент

    > db.users.save({name:"Woody",age:23});
    > db.users.find().pretty()
    {
    	"_id" : ObjectId("508ea7f33cc5578ed9ecbf46"),
    	"name" : "Woody",
    	"age" : 23
    }
    > 
    

    Тут сразу много интересного. Командой db.users.save() мы отправили запрос на сохранение документа в коллекцию users текущей базы данных. Ранее этой коллекции не существовало, она была автоматически создана при запросе.

    > show collections
    system.indexes
    users
    >
    

    Мы сохранили в коллекции users простой json-документ, с ключами name и age. О назначении команды find, догадаться несложно — стоит отметить, что в случае когда нас интересует только один документ стоит пользоваться командой findOne(). Команда pretty() не влияет на логику- она просто выводит результат в удобном для чтения виде — без нее, мы бы получили строку, что не удобно при работе со сложными объектами.

    Пришло время добавить в коллекцию элемент посложнее, и наглядно продемонстрировать безсхемность монго.

    > db.users.save({name:"Bazz",age:23,place_of_birth: {country:"unknown",region:"unknown"},interests:["flying","fighting with evil"]});
    > db.users.find({name:"Bazz"}).pretty()
    {
    	"_id" : ObjectId("508eab333cc5578ed9ecbf47"),
    	"name" : "Bazz",
    	"age" : 23,
    	"place_of_birth" : {
    		"country" : "unknown",
    		"region" : "unknown"
    	},
    	"interests" : [
    		"flying",
    		"fighting with evil"
    	]
    }
    > 
    


    Второй добавленный нами документ уже более похож на реальные данные. Обратите внимание, что значениями элементов place_of_birth и interests являются вложенные документы- словарь (JSONObject) и массив (JSONArray) соответственно. Иерархическая вложенность — ограничивается здравым смыслом и лимитом в 16 мегабайт на элемент.

    Кстати, мы можем исgользовать встроенный интерпретатор javascript
    > var j=db.users.findOne({name:"Woody"})
    > j.age=25
    25
    > db.users.save(j)
    > db.users.find({name:"Woody"}).pretty()
    {
    	"_id" : ObjectId("508ea7f33cc5578ed9ecbf46"),
    	"name" : "Woody",
    	"age" : 25
    }
    > 
    


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

    Полученную коллекцию мы используем в небольшом приложении, в завершении статьи.

    Проектирование
    Рассмотрим как будет выглядеть структура данных простого веб-приложения в Монго и реляционной базе данных.

    Представим, что у нас есть блог с базовым функционалом. Авторы могут публиковать посты, прикрепляя к ним теги, пользователи могут искать по тегам и оставлять к постам комментарии.

    При использовании реляционной базы данных нам понадобятся таблицы для хранения данных об авторах, постах и комментариях. Так же нам, скорее всего, понадобятся дополнительные таблицы для хранения связей между таблицами. Конечно сообразно своим реалиям, вы можете денормализовать базу блога, но в базовом виде, она будет выглядеть как-то так.


    В MongoDb для хранения аналогичных данных, нам будет достаточно двух коллекций. Надеюсь почерк мистера Эрликсона, на картинке достаточно разборчив.

    Если вы обратили внимание — в коллекции авторов в качестве уникального _id предлагается использовать логин. Да, идентификатор можно задавать (соблюдая уникальность конечно), в случае если он не задан — система сделает это за вас.
    Как вы видите структура данных в монго заметно проще, и как я уже отмечал выше — значительно ближе к представлению данных в коде.
    Вы наверное уже устали от моих излияний, поэтому еще одно замечание об идентификаторах, и покодим наконец.
    Задание идентификатора — является операцией с высоким приоритетом. Он задается до фактического сохранения данных. По умолчанию драйвер монго не дожидается ответа от базы о сохранении данных, получает идентификатор и возвращает ответ пользователю. Если вас не устраивает такое поведение — вы можете использовать безопасный режим, но будьте готовы к некоторой потери производительности.

    Пиши код, твою мать.

    В уроках первой недели большую часть кода представили базовые занятия по питону и фреймворку bottle. В следующих занятиях мы поработаем с курсовым проектом блога на питоне и пхп, а пока создадим простенькое приложение, демонстрирующее работу с MongoDb из кода.

    Ввиду элементарности действий, мы не будем создавать никаких абстракций и сделаем все в лоб (коллеги наверное подумают, что я заболел). Думаю писать дао-прослойки вы прекрасно умеете сами.

    Мы создадим простое веб-приложение выводящее на экран имена и возраст пользователей из коллекции users и позволяющее добавлять в коллекцию новые элементы.

    Для запуска примера на питоне вам потребуется драйвер pymongo и фреймворк bottle. Для запуска примера на php, вам так же потребуется драйвер, его можно взять из pear(незабудьте добавить mongo.so в список расширений).

    Итак примеры
    Python
    index.py
    import bottle
    import pymongo
    import sys
    # Открываем безопасное соединение с локальной базой
    # При работе в безопасном режиме, в случае ошибки будет выброшено исключение, но их обработку мы рассмотрим позже
    # там все просто
    connection=pymongo.Connection("mongodb://localhost",safe=True)
    # будем работать с нашей тестовой базой
    db=connection.test
    
    @bottle.route('/')
    def home_page():
        #Получаем  все элементы коллекции
        users=db.users.find();
        return bottle.template('index',{"users":users})
    
    @bottle.post('/submit')
    def submit_page():
        # подготавливаем элемент из данных формы
        user={'name':bottle.request.forms.get("name"),'age':bottle.request.forms.get("age")}
        #Сохраняем
        db.users.insert(user)
        bottle.redirect('/')
    
    bottle.debug()
    bottle.run(host="localhost",port=8080)
    


    views/index.tpl
    <!DOCTYPE html>
    <html>
    <head>
        <title>Hello world</title>
    </head>
    <body>
    <p>
       Список пользователей
    </p><ul>
        %for user in users:
        <li>Имя:{{user['name']}} Возраст:{{user['age']}}</li>
        %end
    </ul>
    <form action="/submit" method="POST">
       Добавить нового пользователя<br>
        <input type="text" name="name" size=40 value=""><br>
        <input type="text" name="age" size=40 value=""><br>
        <input type="submit" value="submit">
    
    </form>
    
    </body>
    </html>
    


    PHP
    Пример на пхп отличается редкой незатейливостью

    <?  
        $mongo = new Mongo('localhost');
        $database = $mongo -> test;     
        if ($_POST)
    	    $database -> users -> insert($_POST);
    	
        $users = $database -> users -> find();
    ?>
    <!DOCTYPE html>
    <html>
    <head>
        <title>Hello world</title>
    </head>
    <body>
    <?var_dump($users)?>
    <p>
       Список пользователей
    </p><ul>
        <?foreach($users as $user){?>
        <li>Имя:<?=$user['name']?> Возраст:<?=$user['age']?></li>
    <?}?>
    </ul>
    <form action="/" method="POST">
       Добавить нового пользователя<br>
        <input type="text" name="name" size=40 value=""><br>
        <input type="text" name="age" size=40 value=""><br>
        <input type="submit" value="submit">
    
    </form>
    </body>
    </html>
    


    Важный вопрос к тем, кто заинтересован в примерах на php. В дальнейшем код будет посложнее, можно использовать Yii или ZF, либо самописный микрофреймворк — жду ваших пожеланий.

    На этом все, надеюсь не зря старался. Если статья понравится хабражителям — на след неделе, опубликую отчет по второй неделе, на которой будут рассматриваьтся CRUD операции.
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 46
    • +6
      О! Все же опубликовали этот пост. Так держать! =)
      Ждем продолжения.
      • +2
        Не проще ли перейти по ссылке в начале топика и пройти все лично?
      • +2
        Тогда уж просьба для продолжения — раз уж монга и json — может примеры сраза на java-script — по крайней мере не нужно будет отдельно шаблон + код — сразу можно.

        Мне вот монга интересна сугубо этим — чтобы часть данных сразу из базы на front-end отдавать напрямую.

        Плюс почему бы результирующую html-ку с js-ом не выложить себе на хостинг — заодно посмотрим как монга выдержит хабраэффект — запустить онную на микроинстансе амазона например :)
        • +9
          > В реляционных базах подобное поведение невозможно.

          Прежде, чем утверждать подобное, было бы неплохо хотя бы погуглить.

          www.postgresql.org/docs/9.1/static/arrays.html
        • –2
          Статья это хорошо, а 10gen против не будут что их материал перепечатывается?
          • +3
            Их материал изложен в видео лекциях ( в статье использованы фрагменты 2х кадров ), я излагаю его в своей интерпретации, к тому же на русском. ДУмаю учитывая, что курсы бесплатные и открытые, они не будут против=)

            Вдобавок в заголовке поста, имеется ссылка на топик с описанием курсов.
            • 0
              Они таки будут не против, или вы так подумали?) Может стоит поинтересоваться у авторов курса?

              Я тоже верю, что они будут только ЗА, но подтверждение было бы только плюсом.

              А так, вон, видите — люди принимают за «кражу». Надо раскрыть им глаза. Аргументами, и доказательствами.
              • 0
                Простите, но кто принимает за кражу? O_o
              • +3
                Прочтите копирайтс.

                Я полагаю, что, все кто сейчас участвует в этих курсах являются кроликами на которых тестируется сам курс. А в дальнейшем он станет платным. По сейму у меня возникли подозрения что 10gen могут быть против данного поста, и лучше бы автору получить письменное разрешение.

                К тому же не думаю что 10gen будут против рекламы.
                • 0
                  думаю, что где-то рядом с видеолекциями есть ссылка на Term of use
                  в целом за обзор курса спасибо
                • 0
                  Я думаю, в 10gen только порадуются, что Монго несут в массы. Все же курс этот затеян в т.ч. для пиара продукта и для просвещения масс.
                  • +1
                    Ну если уже пересказ события да еще и на другом языке считаеся кражей. Не знаю как в России, но в Украине перевод является сомостоятельным объектом применения авторского права. Так скоро и за пересказ футбольного матчас с парой фотографий на телефоне будет считаться кражей, не перегибайте палку.
                    • 0
                      То есть, перевод любой книги я могу продавать как свою?!
                      Да и где вы увидели в моих словах обвинения в краже?
                  • +3
                    Спасибо за интересную тему. Буду рад увидеть примеры с Yii.
                    • 0
                      Не вижу в этом большого смысла. Человек, пишущий на любом фреймворке, знает и vanilla php, а вот перевод с одного фреймворка на другой может быть менее тривиален. Хотя пока и сам код незатейлив.
                      • 0
                        А зачем? Зачем вообще примеры на PHP, ну т.е. я тоже много на нем пишу, но мне кажется, что курс все-таки про mongo, а особенности драйвера для конкретного языка это 2 странички документации, тем более для PHP он очень даже ничего.
                      • +3
                        А вот про проблемы с утерей данных с домашними заданиями на курсе автор топика промолчал.
                        • 0
                          Да, я сегодня вместо курса на следующую неделю тоже увидел сюрприз… Печально это, особенно с учетом того что в день запуска вообще все лежало.
                          • +1
                            И про проблемы с кавычками в заданиях, присутствие или тип которых (одинарные, двойные) определяется фазой луны Юпитера
                            • 0
                              Вводили и двойные, и одинарные, без разбору, даже в одном задании — все проходило на ура.
                              • 0
                                Не верю. Загляните в дискуссии. В заданиях траблы с кавычками и периодически путаются имена: tags вместо tag, name вместо username…
                                • 0
                                  У меня не прокатывало. С двойными проходит с одинарными нет.
                              • +3
                                Да, это действительно был epic fail, учитывая что сами они продвигают mongo как failover базу данных. Позабавило меня это)
                                • –1
                                  Проблема могла быть железной например.
                                  • –1
                                    А при чем тут MongoDB? Курсы запущены на edX платформе, разве она использует MongoDB?
                                  • 0
                                    Я с ней не сталкивался, в дискуссиях читал, но не думаю что там все очень масштабно. Тем более вроде как все восстановили
                                  • 0
                                    Офтопик: Уже выложили 2ую неделю курса. Бегло просмотрел и не увидел там «домашки». Только квизы и лекции. Вопрос это я не так смотрю или ее действительно нет?:
                                  • 0
                                    Огромное спасибо, доступно, на пальцах буквально) А то все думал, с какой стороны бы подойти, для нубов вроде меня — самое то!
                                    • –1
                                      Не совсем понял, в каких случаях лучше использовать mongo вместо реляционной бызы?
                                      • –2
                                        Если для вас на первом месте масштабируемость и производительность. По функционалу — все что можно сделать на реляционной базе, можно сделать на монго.
                                        • +4
                                          Не все. Нет транзакций. Нету целостности данных. Поэтому важно понимать цто монго не понацея.
                                          • –1
                                            В монго целостность данных обеспечивается иерархичностью. Действие над элементом коллекции — атомарно.
                                            • +2
                                              Хорошо. Я не очень правильно выразился. нет изолированности (собственность то что есть транзакция в субд). То есть если я скажем проведу ряд операций над данными в одном приложении нет гарантии что данные с котором ведется работа будут изолированы и что в эти данные скажем не внесет изменение некое другое приложение.
                                              • –1
                                                Если интересно, гляньте How does concurrency work

                                                Этот вопрос будет рассмотрен подробно в следующих статьях
                                                • 0
                                                  Спасибо. Обязательно изучу.
                                        • –3
                                          Во всех ;)
                                      • 0
                                        Вопрос по проектированию
                                        Правильное ли проектирование в уроке, если, скажем, в дальнейшем нам нужно выводить последние комментарии из всех статей и потом еще добавить условие, чтобы не выводить в последних комментариях комментарии из одной и той же статьи
                                        И можно пример еще как вывод последних комментариев сделать?
                                        • 0
                                          Если я Ваши опасения правильно понял то Вам сюда: Dot notation
                                        • 0
                                          Спасибо, интересно.

                                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.