Пользователь
0,0
рейтинг
29 октября 2012 в 23:00

Разработка → 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 операции.
Дмитрий @sl4mmer
карма
10,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (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
        У вас парсер съел тэг sarcasm.
        • –1
          Бывает.
  • 0
    Вопрос по проектированию
    Правильное ли проектирование в уроке, если, скажем, в дальнейшем нам нужно выводить последние комментарии из всех статей и потом еще добавить условие, чтобы не выводить в последних комментариях комментарии из одной и той же статьи
    И можно пример еще как вывод последних комментариев сделать?
    • 0
      Если я Ваши опасения правильно понял то Вам сюда: Dot notation
  • 0
    Спасибо, интересно.

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