MongoDb for developers. Неделя 1

от автора

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

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

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

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

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

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

Если посмотреть на криво-нарисованную схемку ниже, видно что в мире хранения данных, есть два противоположных полюса. Шустрые, но бедные функциональностью хранилища ключ-значения (такие как 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. О нем мы поговорим несколько позже, отметим только, что он уникален в пределах одной коллекции.

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

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

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

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

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

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

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

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

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

Мы создадим простое веб-приложение выводящее на экран имена и возраст пользователей из коллекции 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 операции.

ссылка на оригинал статьи http://habrahabr.ru/post/156633/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *