В этой статье будет изложен основной материал первой недели обучения. Если аудитория проявит интерес — то подобные посты будут выходить в конце каждой недели.
Мы вкратце рассмотрим, что представляет собой 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/
Добавить комментарий