7 сентября 2010 в 10:58

Getting Started with MongoDB and PHP перевод

PHP*
Generation Next
За последний год произошла небольшая революция в мире СУБД, связанная с появлением безсхемных (безструктурных) СУБД таких как Apache CouchDB. В этих базах данных используется другой подход нежели в реляционных БД. Они достаточно быстро становятся популярны среди Web-разрабочиков из-за своей гибкости, простоты и легкой интеграции с современными технологиями такими как JSON.
В данной статье изложен краткий обзор MongoDB, одной из новых поколений безсхемных СУБД, которая получила большое внимание разработчиков.

Start Me Up
На официальном сайте MongoDB написано, что это расширяемая, высокопроизводительная, документоориентированная БД с открытым исходным кодом. Существует под большое количество платформ и распространяется под лицензией GNU AGPL.
Хотя MongoDB и похожа на CouchDB, между ними существуют серьезные различия:
  • Разработчики MongoDB должны использовать встроенные в язык драйверы (native language drivers) для доступа к БД, в то время как для CouchDB используется REST
  • MongoDB поддерживает большее количество типов данных
  • Масштабируемость MongoDB строится на основе технологии sharding (русского аналога нет, оставил на английском), в то время как для CouchDB — это репликация.
  • Запросы MongoDB используют объекты BSON, в то время как запросы CouchDB генерируются с помощью JavaScript.

Документация MongoDB детально описывается все эти (и немного больше) различия, также имеются тесты сравнивающие MongoDB, CouchDB и MySQL (еще один повод для holy war).
Беглый обзор закончен, время для скачивания и установки MongoDB. В большинстве случаев стандартного бинарного пакеат хватает; он содержит MongoDB и клиент, работающий из командной строки, набор утилит для бэкапа, восстановления, а также сохранения и получения бинарных фалов. Для начала скачаем подходящую версию для вашей системы:
shell> cd /usr/local
shell> tar -xzvf mongodb-linux-i686-1.4.2.tgz
shell> ln -s mongodb-linux-i686-1.4.2 mongo
Если у вас Ubuntu, вы можете установить MongoDB используя aptitude. Для этого добавьте в /etc/apt/sources.list следующую строку:
deb downloads.mongodb.org/distros/ubuntu 10.4 10gen
Затем установите этот пакет используя aptitude:
shell> aptitude update
shell> aptitude install mongodb-stable
Когда пакет установлен запустите MongoDB сервер
shell> mkdir /usr/local/mongo/data
shell> /usr/local/mongo/bin/mongod --dbpath=/usr/local/mongo/data


Заметьте, что по умолчанию, сервер MongoDB server считает что данные сохраняются в /data/db, и заканчивает выполнение, если не находит этого пути. Параметр --dbpath позволяет указать другой путь.
Вы можете использовать командную строку для доступа к серверу, как это показано ниже:
shell>/usr/local/mongo/mongo
Вот как это выглядит:


Можно отдавать команды серверу, также как и в MySQL-клиенте. Ниже пример, который показывает версию сервера и доступные БД:
>show dbs
admin
local
>db.version()
1.4.2
>db.stats()
{
    "collections" : 0,
    "objects" : 0,
    "dataSize" : 0,
    "storageSize" : 0,
    "numExtents" : 0,
    "indexes" : 0,
    "indexSize" : 0,
    "ok" : 1
}

* This source code was highlighted with Source Code Highlighter.


Collecting Ideas
Во вселенной MongoDB, эквивалентом таблицы является «коллекция». Подобно тому, как таблицы имеют множество записей, в коллекциях — множество «документов». Эти документы представлены как JSON объекты, с полями и значениями представляющими пары ключ-значение, и сохраненные (сериализованы) в BSON (Binary JSON) для хранения. Ниже пример одного такого документа:
{
«orderDate»: «10-05-2010»,
«orderTotal»: 468.99,
}
Так как основной элекмент MongoDB это документ в формате JSON, а JSON поддерживает иерархические данные, вы можете включать один документ в другой. Далее, так как документы сериализуются в формат BSON для хранения, MongoDB может легко искать вложенные документы. Ниже пример:
{
«orderDate»: «10-05-2010»,
«orderTotal»: 468.99,
«shipTo»:
{
«street»: «17 Hill View»,
«zip»: «12345»,
«country»: «US»
}
}
Для создания новой MongoDB коллекции, запустите клиент в командной строке и выполните следующие команды, которые создадут коллекцию под названием items" и добавят несколько документов:
  1. > db.items.insert({ name: 'eggs', quantity: 10, price: 1.50 })
  2. > db.items.insert({ name: 'bacon', quantity: 3, price: 3.50 })
  3. > db.items.insert({ name: 'tomatoes', quantity: 30, price: 0.50 })
* This source code was highlighted with Source Code Highlighter.

Продолжаем дальше, добавьте еще документов как показано выше. Для отображения полного спика документов в коллекции вызовите метод find() без аргументов:
  1. > db.items.find({})
  2. { "_id" : ObjectId("4bea15293302000000006dcf"), "name" : "eggs", "quantity" : 10, "price" : 1.5 }
  3. { "_id" : ObjectId("4bea15463302000000006dd0"), "name" : "bacon", "quantity" : 3, "price" : 3.5 }
  4. { "_id" : ObjectId("4bea15523302000000006dd1"), "name" : "tomatoes", "quantity" : 30, "price" : 0.5
  5. }
* This source code was highlighted with Source Code Highlighter.

Заметьте, специальный ключ '_id' есть у каждого документа. Когда вы записываете новый документ в коллекцию, MongoDB автоматически добавляет уникальный идентификатор к каждому документу. Этот идентификатор может быть использован для получения или модификации документа, это некое подобие автоинкрементного ключа в реляционных базах данных.
Для отображения списка документов по какому-либо критерию, добавьте этот критерий (в виде объекта JSON) в метод find(). Ниже пример, показывающий записи с количеством большим 9 и ценой меньше 1:
  1. > db.items.find({quantity: {$gt: 9}, price: {$lt: 1}})
  2. { "_id" : ObjectId("4bea15523302000000006dd1"), "name" : "tomatoes", "quantity" : 30, "price" : 0.5
  3. }
  4.  
* This source code was highlighted with Source Code Highlighter.

Теперь давайте попробуем написать что-нибудь на php!

Hooking Things Up
Поддержка MongoDB в PHP реализована посредством расширения, которое обеспечивает API для доступа к коллекциям MongoDB. Это расширение разрабатывается Kristina Chodorow и бесплатно доступно в PECL под лицензией Apache License. Расширение стабильно и позволяет выполнять большинство задач, относящихся к доступу и использованию БД MongoDB database из приложений, написанных на языке PHP.
ДЛя того чтобы использовать это расширение установите его используя стандартную команду pecl
shell> pecl install mongo
Или вы можете скачать исходный код, скомпилировать его в загружаемый модуль PHP:
shell> tar -xzvf mongo-1.0.7.tar.gz
shell> cd mongo-1.0.7
shell> phpize
shell> ./configure
shell> make
shell> make install
Теперь у вас есть загружаемый PHP модуль под названием mongo.so, который находится в стандартном каталоге для модулей PHP. Подключите данное расширение в php.ini, перепустите Web-сервер и проверьте активность этого расширения командой phpinfo():


Теперь давайте посмотрим, что мы можем сделать, используя это расширение
  1. <?php
  2. try {
  3.  // open connection to MongoDB server
  4.  $conn = new Mongo('localhost');
  5.  
  6.  // access database
  7.  $db = $conn->test;
  8.  
  9.  // access collection
  10.  $collection = $db->items;
  11.  
  12.  // execute query
  13.  // retrieve all documents
  14.  $cursor = $collection->find();
  15.  
  16.  // iterate through the result set
  17.  // print each document
  18.  echo $cursor->count() . ' document(s) found. <br/>'
  19.  foreach ($cursor as $obj) {
  20.   echo 'Name: ' . $obj['name'] . '<br/>';
  21.   echo 'Quantity: ' . $obj['quantity'] . '<br/>';
  22.   echo 'Price: ' . $obj['price'] . '<br/>';
  23.   echo '<br/>';
  24.  }
  25.  
  26.  // disconnect from server
  27.  $conn->close();
  28. } catch (MongoConnectionException $e) {
  29.  die('Error connecting to MongoDB server');
  30. } catch (MongoException $e) {
  31.  die('Error: ' . $e->getMessage());
  32. }
  33. ?>
* This source code was highlighted with Source Code Highlighter.

В начале этого скрипта инициализируется новый объект Mongo, в конструктор которого передается информация, необходимая для установки соединения с сервером MongoDB (в примере имя хоста). Даный объект используется для всех последующих взаимодействий с сервером MongoDB.
Следующим шагом является получение доступа к базе данных. Это может быть сделано используя метод selectDB() или что более проще, посредством магического метода call, обратиться к базе как к свойству объекта. Как только получен доступ к базе данных не составит особого труда получить доступ к коллекции, испольуя метод selectCollection() или посредством магического метода call, обратившись к коллекции как к свойству. Коллекции предствавлены в виде объектов MongoCollection.
У каждого объекта MongoCollection есть метод find(), который может быть использован для выполнения запросов. Метод принимает в качестве аргументов два массива: массив параметров запросаи массив полей, которые долны быть получены в результате ответа на запрос. Возвращаемым значением является курсор, представленный объектом типа MongoCursor object. Тип MongoCursor реализует (implement) шаблон Iterator, поэтому достаточно легко пробежать по всему возвращаемому набору используя foreach(). Тип MongoCursor содержит метод count(), возвращающий количество записей.
PHP расширение Mongo поддерживает модель исключений, реализованную в PHP 5.x, и определяет 5 типов исключений: MongoConnectionException для ошибок, связанных с соединением, MongoCursorException и MongoCursorTimeoutException для ошибок связанных с запросом; MongoGridFSException для ошибок связанных с файловым взаимодействием; и MongoException для всех остальных ошибок. В предыдущем примере было бы неплохой идеей заключить код в try/catch блок.
Ниже пример вывода предыдущего примера:


Addition And Subtraction
Добавление нового документа в коллекцию достаточно просто. Посмотрите следующий пример:
<?php
try {
 // open connection to MongoDB server
 $conn = new Mongo('localhost');

 // access database
 $db = $conn->test;

 // access collection
 $collection = $db->items;

 // insert a new document
 $item = array(
  'name' => 'milk',
  'quantity' => 10,
  'price' => 2.50,
  'note' => 'skimmed and extra tasty'
 );
 $collection->insert($item);
 echo 'Inserted document with ID: ' . $item['_id'];
 
 // disconnect from server
 $conn->close();
} catch (MongoConnectionException $e) {
 die('Error connecting to MongoDB server');
} catch (MongoException $e) {
 die('Error: ' . $e->getMessage());
}
?>

* This source code was highlighted with Source Code Highlighter.

Для добавление нового документа в коллекцию, создайте новый массив содержащий пару ключ-значение, которые вы хотите вставить (механизм поддерживает также вложенные массивы, которые конвертируются во встроенные документы) и передайте этот массив в метод insert объекта типа MongoCollection. Этот метод добавит документ в коллекцию и рассчитает значение '_id' (уникальный идентификатор документа), которое добавится в исходный массив. Этот идентификатор представляется специальным типом MongoId, который есть шестнадцатиричное представление идентификатора. Поэтому можно легко получить идентификатор документа, не делая дополнительный запрос.
Ниже пример:


Удаление документа из коллекции осуществляется методом remove(), который принимает в качестве параметров массив критериев, и удаляет все документы соответствующие этим критериям. Обычно remove() возвращает значение типа Boolean (true или false); однако, если передать специальный 'safe' аргумент в качестве второго аргумента позволит получить масиив с большим количеством информации, включающей в себя количество удаленных документов. Ниже пример:
<?php
try {
 // open connection to MongoDB server
 $conn = new Mongo('localhost');

 // access database
 $db = $conn->test;

 // access collection
 $collection = $db->items;

 // remove a document
 $criteria = array(
  'name' => 'milk',
 );
 $r = $collection->remove($criteria, array('safe' => true));
 echo 'Removed ' . $r['n'] . ' document(s).';
 
 // disconnect from server
 $conn->close();
} catch (MongoConnectionException $e) {
 die('Error connecting to MongoDB server');
} catch (MongoException $e) {
 die('Error: ' . $e->getMessage());
}
?>

* This source code was highlighted with Source Code Highlighter.

Также возможно удалить документ, используя его идентификатор. Однако в метод remove() передается объект метода MongoId, а не строка PHP. Ниже пример:
<?php
try {
 // open connection to MongoDB server
 $conn = new Mongo('localhost');

 // access database
 $db = $conn->test;

 // access collection
 $collection = $db->items;

 // remove a document by ID
 $criteria = array(
  '_id' => new MongoId('4bea96b400f4784c0a070000'),
 );
 $collection->remove($criteria);
 echo 'Removed document with ID: ' . $criteria['_id'];
 
 // disconnect from server
 $conn->close();
} catch (MongoConnectionException $e) {
 die('Error connecting to MongoDB server');
} catch (MongoException $e) {
 die('Error: ' . $e->getMessage());
}
?>

* This source code was highlighted with Source Code Highlighter.

Обновление документа происходит посредством метода save(), как показано ниже:
<?php
try {
 // open connection to MongoDB server
 $conn = new Mongo('localhost');

 // access database
 $db = $conn->test;

 // access collection
 $collection = $db->items;

 // retrieve existing document
 $criteria = array(
  'name' => 'eggs',
 );
 $doc = $collection->findOne($criteria);
 
 // update document with new values
 // save back to collection
 $doc['name'] = 'apples';
 $doc['quantity'] = 35;
 $doc['note'] = 'green apples taste sooooo good!';
 $collection->save($doc);
 
 // disconnect from server
 $conn->close();
} catch (MongoConnectionException $e) {
 die('Error connecting to MongoDB server');
} catch (MongoException $e) {
 die('Error: ' . $e->getMessage());
}
?>

* This source code was highlighted with Source Code Highlighter.

Заметьте, что если findOne() ничего не возвратил, вызов save() добавит новый документ.
Asking Questions
Когда заходит речь о выполнении запросов, MongoDB предлагает достаточно гибкий инструментарий. Вы уже видели метод find(), который возвращает набор документов, соответствующий критериям поиска. Также есть метод findOne(), который возвращает один документ. Можно использовать множество критериев для поиска — просто передайте в качестве критериев поиска массив в find() или findOne(), а MongoDB применит все эти критерии с модификатором AND.
Ниже пример:
<?php
try {
 // open connection to MongoDB server
 $conn = new Mongo('localhost');

 // access database
 $db = $conn->test;

 // access collection
 $collection = $db->items;

 // formulate AND query
 $criteria = array(
  'quantity' => 30,
  'price' => 0.5
 );
 
 // retrieve only 'name' and 'price' keys
 $fields = array('name', 'price');
 
 // execute query
 $cursor = $collection->find($criteria, $fields);

 // iterate through the result set
 // print each document
 echo $cursor->count() . ' document(s) found. <br/>'
 foreach ($cursor as $obj) {
  echo 'Name: ' . $obj['name'] . '<br/>';
  echo 'Price: ' . $obj['price'] . '<br/>';
  echo '<br/>';
 }

 // disconnect from server
 $conn->close();
} catch (MongoConnectionException $e) {
 die('Error connecting to MongoDB server');
} catch (MongoException $e) {
 die('Error: ' . $e->getMessage());
}
?>

* This source code was highlighted with Source Code Highlighter.

MongoDB также поддерживает большое количество условных и логических операторов для создания сложных запросов. Плюс к этому поддержка регулярных выражений. Ниже пример, который выводит все записи с количеством между 10 и 50 и чьи имена заканчиваются на 'es':
<?php
try {
 // open connection to MongoDB server
 $conn = new Mongo('localhost');

 // access database
 $db = $conn->test;

 // access collection
 $collection = $db->items;

 // formulate complex query
 $criteria = array(
  'quantity' => array(
    '$gt' => 10,
    '$lt' => 50
   ),
  'name' => new MongoRegex('/es$/i')
 );
 
 // execute query
 $cursor = $collection->find($criteria);

 // iterate through the result set
 // print each document
 echo $cursor->count() . ' document(s) found. <br/>'
 foreach ($cursor as $obj) {
  echo 'Name: ' . $obj['name'] . '<br/>';
  echo 'Quantity: ' . $obj['quantity'] . '<br/>';
  echo 'Price: ' . $obj['price'] . '<br/>';
  echo '<br/>';
 }

 // disconnect from server
 $conn->close();
} catch (MongoConnectionException $e) {
 die('Error connecting to MongoDB server');
} catch (MongoException $e) {
 die('Error: ' . $e->getMessage());
}
?>

* This source code was highlighted with Source Code Highlighter.

Как это будет выглядеть:


Вы можете также изменить количество возвращаемых документов или отсортировать их по необходимому ключу, используя limit() и sort() методы. Ниже пример:
<?php
try {
 // open connection to MongoDB server
 $conn = new Mongo('localhost');

 // access database
 $db = $conn->test;

 // access collection
 $collection = $db->items;

 // execute query
 // sort by price
 // limit to 3 documents
 $cursor = $collection->find();
 $cursor->sort(array('price' => 1))->limit(3);
 
 // iterate through the result set
 // print each document
 echo $cursor->count() . ' document(s) found. <br/>'
 foreach ($cursor as $obj) {
  echo 'Name: ' . $obj['name'] . '<br/>';
  echo 'Quantity: ' . $obj['quantity'] . '<br/>';
  echo 'Price: ' . $obj['price'] . '<br/>';
  echo '<br/>';
 }

 // disconnect from server
 $conn->close();
} catch (MongoConnectionException $e) {
 die('Error connecting to MongoDB server');
} catch (MongoException $e) {
 die('Error: ' . $e->getMessage());
}
?>

* This source code was highlighted with Source Code Highlighter.

Между прочим, если вы ходите узнать, как внутри MongoDB выполняет запрос, вы можете использовать метод explain() объекта MongoCursor для «look inside» системы обработки запросов, очень похоже на команду EXPLAIN у MySQL. Ниже пример и результат вывода:
<?php
try {
 // open connection to MongoDB server
 $conn = new Mongo('localhost');

 // access database
 $db = $conn->test;

 // access collection
 $collection = $db->items;

 // execute and explain query
 $criteria = array(
  'quantity' => array(
    '$gt' => 10,
    '$lt' => 50
   ),
  'name' => new MongoRegex('/es$/i')
 );
 $cursor = $collection->find($criteria);
 $cursor->sort(array('price' => 1))->limit(3);
 print_r($cursor->explain());
 
 // disconnect from server
 $conn->close();
} catch (MongoConnectionException $e) {
 die('Error connecting to MongoDB server');
} catch (MongoException $e) {
 die('Error: ' . $e->getMessage());
}
?>

* This source code was highlighted with Source Code Highlighter.



Rank And File
В дополнение к документам MongoDB также поддерживает бинарные данные. Бинарные данные до 4 MB могут быть сохранены в обычном документе, а данные (файлы), котороые больше этого размера, могут быть сохранены, использую маленькую штучку под названием GridFS.
GridFS это спецификация, описывающая дробление и сохранение больших файлов в MongoDB. Обычно, GridFS использует две коллекции: коллекция 'files', которая созраняет метаданные о каждом файле, и коллекция 'chunks', которая сохраняет данные, разделенные на кусочки (chunks). Каждый файл в коллекции 'files' collection имеет уникальный идентификатор, подобный идентификаторам других документов, хранимых в MongoDB; этот идентификатор можкт быть использован для получения или изменения файла.

Расширение MongoDB PECL обеспечивает набор классов MongoGridFS, которые могут быть использованы для взаимодействия с файлами, сохраненными с использованием GridFS. Каждый файл представлен экземпляром класса MongoGridFSFile, а каждый объект MongoGridFS предоставляет методы для добавления, удаления и поиска этих файлов.

Для лучшего понимания, рассмотрим следующий пример, который иллюстрирует процесс добавления изображения в MongoDB:
<?php
try {
 // open connection to MongoDB server
 $conn = new Mongo('localhost');

 // access database
 $db = $conn->test;

 // get GridFS files collection
 $gridfs = $db->getGridFS();
 
 // store file in collection
 $id = $gridfs->storeFile('/tmp/img_2312.jpg');
 echo 'Saved file with ID: ' . $id; 
 
 // disconnect from server
 $conn->close();
} catch (MongoConnectionException $e) {
 die('Error connecting to MongoDB server');
} catch (MongoException $e) {
 die('Error: ' . $e->getMessage());
}
?>

* This source code was highlighted with Source Code Highlighter.

В начале примера получается эксземпляр класса MongoGridFS, используя метод getGridFS() класса MongoDB, а затем методом storeFile() файл сохраняется в MongoDB. Если вы запустите этот пример, а затем посмотрите на содержимое вашей БД, вы увидите две новые коллекции 'fs.files' и 'fs.chunks'. Если вы взгляненте немного глубже в коллекцию 'fs.files', то вы увидите файл, корторый вы добавили.
> show collections
fs.chunks
fs.files
items
system.indexes
> db.fs.files.find()
{ "_id" : ObjectId("4beaa34f00f4784c0a300000"), "filename" : "/tmp/img_2312.jpg", "uploadDate" : "Wed May 12 2010 18:17:11 GMT+0530 (India Standard Time)", "length" : 11618, "chunkSize" : 262144, "md5" : "e66b9a33c7081ae2e4fff4c37f1f756b" }

* This source code was highlighted with Source Code Highlighter.

В качестве альтернативы the storeFile() существует метод storeUpload(), которые предназначен для использования совместно с загрузкой фалов, используя PHP. Использовать эту возможность очень легко: передайте в storeUpload название поля загрузки файла в форме, а MongoDB сама все сделает. Вы также можете передать название файла как второй аргумент.
Ниже небольшой пример:
<html>
 <head></head>
 <body>
  <form method="post" enctype="multipart/form-data">
   Select file for upload:
   <input type="file" name="f" />
   <input type="submit" name="submit" />   
  </form>
  <?php
  if (isset($_POST['submit'])) {
   try {
    // open connection to MongoDB server
    $conn = new Mongo('localhost');
   
    // access database
    $db = $conn->test;
   
    // get GridFS files collection
    $gridfs = $db->getGridFS();
    
    // check uploaded file
    // store uploaded file in collection and display ID
    if (is_uploaded_file($_FILES['f']['tmp_name'])) {
     $id = $gridfs->storeUpload('f');
     echo 'Saved file with ID: ' . $id;      
    } else {
     throw new Exception('Invalid file upload'); 
    }
        
    // disconnect from server
    $conn->close();
   } catch (MongoConnectionException $e) {
    die('Error connecting to MongoDB server');
   } catch (Exception $e) {
    die('Error: ' . $e->getMessage());
   }       
  }
  ?>
 </body>
</html>

* This source code was highlighted with Source Code Highlighter.

Имея файл в БД, как его получить обратно? Конечно же, используя идентификатор документа для получения файла (MongoGridFS является наследником MongoCollection, поэтому вы можете использовать find() и findOne()) в виде объекта класса MongoGridFSFile, а затем записать его на диск, используя метод write(), или вывести его куда-либо, используя getBytes(). Если вы используйте вывод в браузер, не забудьте добавить необходимый заголовок (header)!
Ниже пример:
<?php
try {
 // open connection to MongoDB server
 $conn = new Mongo('localhost');

 // access database
 $db = $conn->test;

 // get GridFS files collection
 $grid = $db->getGridFS();
 
 // retrieve file from collection
 $file = $grid->findOne(array('_id' => new MongoId('4beaa34f00f4784c0a300000')));
 
 // send headers and file data
 header('Content-Type: image/jpeg');
 echo $file->getBytes();
 exit; 
 
 // disconnect from server
 $conn->close();
} catch (MongoConnectionException $e) {
 die('Error connecting to MongoDB server');
} catch (MongoException $e) {
 die('Error: ' . $e->getMessage());
}
?>

* This source code was highlighted with Source Code Highlighter.


Boys And Their Toys
Хотя расширение PECL MongoDB предлагает достаточно удобные методы работы с MongoDB, вам может быть необходим более высокий уровень абстракции — скажем, например, если вы пытаетесь интегрировать MongoDB into в существующее приложение (framework-based application). В этом случае вы можете использовать Morph, «библиотеку высокого уровня для MongoDB», которая доступна бесплатно по лицензии GNU GPL.

Morph написан как шаблон ActiveRecord, позволяющий создать описание объекта MongoDB, расширяя основной класс Morph_Object. Эти экземпляры класса Morph_Object отражаются напрямую на документы MongoDB и имеют те же свойства и методы, с помощью которых можно работать используя стандартную объектную нотацию.

Для иллюстрации, предположим, что вы хотите созадть базу данных игрушек. Обычные атрибуты включают в себя имя (name), описание (description), возраст ребенка (age suitability), пол ребенка (gender suitability) и цену. Вы можете представить всю эту информацию в виде объекта класса Morph_Object:
<?php
class Toy extends Morph_Object {
 
 public function __construct($id = null)
 {
  parent::__construct($id);
  $this->addProperty(new Morph_Property_String('name'))
     ->addProperty(new Morph_Property_String('colors'))
     ->addProperty(new Morph_Property_Integer('minAge'))
     ->addProperty(new Morph_Property_Integer('maxAge'))
     ->addProperty(new Morph_Property_Enum('gender', null, array('boys', 'girls', 'both')))
     ->addProperty(new Morph_Property_Float('price'));
 } 
}
?>

* This source code was highlighted with Source Code Highlighter.

Теперь вы можете создать новый документ по этому шаблону, используя объект класса Toy, установить свойства и записать его, используя метод save(). Ниже пример:
<?php
require_once 'Morph.phar';

// initialize MongoDB connection
$mongo = new Mongo('localhost');

// select database for storage
$storage = Morph_Storage::init($mongo->selectDb('test'));

// create object and set properties
$toy = new Toy();
$toy->name = 'Ride Along Fire Engine';
$toy->colors = 'red,yellow';
$toy->minAge = '2';
$toy->maxAge = '4';
$toy->gender = 'both';
$toy->price = 145.99;

// save to database
$storage->save($toy);
echo 'Document saved with ID: ' . $toy->id();
?>

* This source code was highlighted with Source Code Highlighter.

Также, вы можете получить объект по идентификатору, обновить его и сохранить его обратно.
<?php
require_once 'Morph.phar';

// initialize MongoDB connection
$mongo = new Mongo('localhost');

// select database for storage
$storage = Morph_Storage::init($mongo->selectDb('test'));

// create object and set properties
$toy = new Toy();
$toy->loadById('5421d0b9fc6217c5bb929baa14a97e08');
$toy->name = 'Jumping Squid';
$toy->colors = 'red,orange,green,blue,yellow';
$toy->minAge = '2';
$toy->maxAge = '10';
$toy->gender = 'boys';
$toy->price = 22.99;

// save to database
$storage->save($toy);
echo 'Document saved with ID: ' . $toy->id();
?>

* This source code was highlighted with Source Code Highlighter.


Все эти примеры иллюстирируют — MongoDB обеспечивает надежную, богатую на возможности реализацию безсхемной СУБД. Доступность для различных платформ, легкая интеграция с PHP и другими языками и расширенная документация (плюс очень классые онлайн возможности для экспериментов) делают MongoDB идеальной для разработки современной, документо-ориентированной БД.
+87
22631
293
great_boba 37,6 G+

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

0
iexx, #
Чёрт, а ведь красиво же ;)
+1
smartello, #
Типичная записка к курсовому, по тексту одно, в выводе другое :) Дело в том, что эти примеры иллюстрируют использование MongoDB, но ничего из перечисленного вами.
+4
WebMonet, #
Да, я вот тоже как- то ощутил радикальных отличий. Отсюда вопрос к знатокам — в каких случаех лучше использовать Mongo, а в каких проще оставить мускул и т.д.?
+1
PyKaB, #
Вообще в описание Mongo только Ваш вопрос и надо обсуждать.
Смысл в том, что используется «документ» как конечный элемент базы.
Самый простой пример — Вы делаете новостной портал.
Есть там новости, потом добавляются статьи, потом добавляются интервью и т.д.
С mongoDB и подобными не надо плодить новых сущностей — все записи лежат в одной коллекции(таблице) и могут отличаться набором полей — у интервью есть участники, у статьи есть автор, у новости есть новостное агентство

Реальный плюс для разработки такого сайта — шаблон будет тоже один — просто показываете те поля, что есть. В реляционной базе тоже можно так сделать, если сразу предусмотреть абсолютно все колонки таблицы или в таблице хранить XML документ(как в CMS DJEM)
0
TravisBickle, #
Я конечно рад любым статьям на эту тему, т.к. они повышают популярность решения. Однако, статья крайне неудачная. Такое ощущение что просто взяли куски мануала скопировали и добавили пару скринов. Для тех кто читал (или собирается читать) мануал от нее пользы 0.
0
great_boba, #
Прочитайте, пожалуйста, название статьи… Там как раз написано, то о чем вы пишите.
0
TravisBickle, #
А вот нифига. Под тем же названиям можно рассмотреть более сложные и интересные аспекты использования. Например, шардинг. Если человек начинает что-то использовать, это не значит что он пишет гостевую книгу ;-)
+1
great_boba, #
Может быть что-то сложное (особенно шардинг) в статье под названием «Getting Started....»
НЛО прилетело и опубликовало эту надпись здесь
0
great_boba, #
Постараюсь найти данные и выложить
0
great_boba, #
Нашел презентацию, если вам интересно то смотреть тут
–1
kliss, #
Плюс еще перевели гуглотранслейтом :-))
0
great_boba, #
покажите кусок текста, где переведено гуглом
0
kliss, #
Да ну вот хотя бы :-)
> скажем, например, если вы пытаетесь интегрировать MongoDB into в существующее приложение (framework-based application).
0
great_boba, #
Если Вы внимательно посмотрите, то в начале статьи я специально некоторые вещи добавлял на английском, так как русских аналогов нет (например тот же «шардинг»).
Более того framework-based application не переводится как существующее приложение, так как более подходящего аналога я в русском не нашел. И потом в конце статьи ссылка на оригинал, заправьте его в гугл и посмотрите, что он даст, хотя бы тот же кусок с framework-based application.
0
kliss, #
Я больше про «into в» :-)
+3
Alex_EXEcuter, #
звучит очень красиво, но крайне интересны sucess story, когда в готовом проекте изменили базу данных с mysql на mongodb и производительность при этом не упала.

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

Интересно, а что там с индексами и кешированием?
0
TravisBickle, #
Нет, не регулярками, а оптимальным поиском по индексу поля. MongoDB поддерживает массивы в свойствах. Поиск будет такой же как будто там скалярное значение лежит.
–1
great_boba, #
mongodb — это хранилище документов. В бытность мою студентом, когда только появились xml-технологии, обсуждалась идея хранения в БД информации в виде xml. Однако вопрос поиска внутри xml-дерева достаточно сложный.
Что-то мне кажется, что пока это решение не для больших объемов информации
+2
TravisBickle, #
Извините а кем она обсуждалась? В курилке студентами? Никто в здравом уме не будет XML-формат использовать для базы данных, но не потому что якобы там будут проблемы с поиском (никаких проблем — индекс можно построить). А потому что формат не позволяет эффективно двигать данные и отделять объекты друг от друга. В MongoDB используется BSON.
Плохо когда мысли начинаются с «Что-то мне кажется» — это плохой признак.
0
Alex_EXEcuter, #
Ох, могу показать проект в котором владелец изобрел велосипед с хранением данных в xml. На мой вопрос «а как же масштабирование и оптимизация» поступил ответ — не использовать движок на проектах с посещаемостью больше 600 хитов в день. При этом кругом ужас-ужас, куча копий главного xml файла и всякий ад, когда этот файл правят руками.
0
great_boba, #
Вопрос обсуждался на уровне преподаватели и студенты, дело было в 1999 году. Тогда этот подход был main stream. Так что на вашем месте, я бы не был столь категоричен.
0
great_boba, #
В догонку почитайте www.citforum.ru тех лет
0
PyKaB, #
погуглите на тему DJEM
xml в базе это не всегда база на xml
0
great_boba, #
Хотел автору комментария задать вопрос, чем принципиально отличается структура записи в mongodb от xml, но он видимо в этом вопросе ОГРОМНЫЙ специалист, не стал его огорчать
+1
kenga, #
Вот хорошее видео с ответами на ваши вопросы (=
nosql.mypopescu.com/post/1016320617/mongodb-is-web-scale
0
how, #
habrahabr.ru/post/204392/
вот перевод
0
lostdj, #
0
limp, #
+2
vovich, #
вообще непонятно как работает поиск, что там с индексами, с GROUP BY и самое главное как там с JOIN. Я так понимаю, что про типы данных не имеет смысла спрашивать. И еще непонятно какое количество записей может быть в одной колекции.
+1
TravisBickle, #
С индексами там всё замечательно, правда пока нету partial indexes и functional indexes, но будут. В MySQL их тоже нет. С GROUP BY там всё хорошо, есть операция group(). JOIN там нету и не надо. Количество записей 10^79.
0
vovich, #
простите я не понял про джоины. Можно на примере объяснить?
вот к примеру есть пользователи и есть коментарии, которые пользователи оставляют. Нужно вывести все комментарии пользователя Х, а так основную информацию о пользователе. Можете мне показать какие колекции я должен создать?
0
TravisBickle, #
Сначала надо выдернуть инфу о пользователе, а потом комментарии. Коллекцию users и comments.
–2
vovich, #
отлично на странице блога будут показываться комментарии всех пользователей, скажем их 100. к каждому комментарию нужно показывать информацию о пользователе — это что, нужно по каждой записи делать запрос к коллекции Users?
+3
TravisBickle, #
«Сьешь сладенького, говорят мозгам помогает, тем у кого они есть конечно» (С) Шматрица

А вам не приходило в голову собрать все уникальные ID-шники комментаторов в массив, и сделать один единственный запрос?
+1
vovich, #
а потом джойнить массивы? или просто хранить каждую коллекцию в своем массиве. Иногда требуется более трех джоинов за раз, в данном случае все преврятится в кучу кода. Да и после каждого запроса собирать уникальные ID тоже знаете ли.
Пока джоинов не будет — работать можно только с простыми данными.
+2
TravisBickle, #
Не вижу смысла в том чтобы копировать одни и те же данные в массив. Ну это всё от прямоты рук зависит на самом деле, можно и не сильно страшный код написать.

Не надо обобщать, многие проекты не пользуются джойном и держат сложные структурированные базы данных.
–4
vovich, #
хотелось бы на них взглянуть. Сложно себе представляю нормализованную БД сложной структуры без джоинов.
0
Frikazoid, #
Легко!
Один из проектов (full ajax) кеширует данные на клиенте, а жоин происходит во время рендера. Я скажу, что прирост скорости доставляет. =)
0
DanyBoo, #
на счет joinов далеко ходить не надо — Facebook…
www.insight-it.ru/masshtabiruemost/arkhitektura-facebook/
–1
aleks_raiden, #
выдернуть все комментарии, из них выбрать массив ид юзеров (уникальных будет всегда меньше, чем общее число комментов), по этому массиву выбрать инфу. в обычных бд тоже такой же подход и он гибче и используя только основные команды, делает все быстрее.
0
vovich, #
один запрос отработает быстрее, чем два.
0
aleks_raiden, #
далеко не всегда
+1
VolCh, #
Коллекцию документов post (если речь о блоге), в которую внедрены документы comment (в том числе без всяких проблем древовидные комментарии), ссылающиеся (специальный тип поля) на документы коллекции user. В общем случае (отвечаю на вопрос ниже) да, придётся делать как это называется 1+N запрос вместо одного с джойном, вернее их придётся делать, если документы не связаны отношением типа «родитель-дети» или «автор-посты». В вашем примере так не получится — комменты должны явно ссылаться или на своего автора и неявно на пост (тогда одним запросом можно получить все комменты к посту) или явно на пост и неявно на автора (тогда можно получить все комменты автора без проблем). Сходу приходят в голову варианты обхода проблемы: кэширование запросов, «денормализация базы» (то есть хранить с комментом не только ссылку на его автора, но и, например, ник, чтобы сделать линк на профиль, причём в данном конкретнм варианте даже не надо беспокоиться о синхронизации данных — ники, как правило, изменить невозможно) и есть ещё встроенная в Моного mapreduce engine, что-то вроде хранимых процедур (пишутся они, кстати, на обычном Javascript) — наверное с её помщью можно, особенно учитывая прозрачную горизонтальную масштабируемость — обработка map/reduce функции будет выполниться на всех серверах. Тлчнее не скжу, настолько ещё не углубился, ни в функции, ни в шардинг
+1
Frikazoid, #
В этом доке очень наглядный пример реализации сложного SQL запроса с помощью Mongo. sql-to-mongodb.pdf
Ещё вот этот линк поможет в перестройки логики с SQL на манер Mongo. =)
0
vovich, #
спасибо за ссылки — теперь более менее все становится на свои места. Может я немного старомоден, но мне кажется что менять простой и понятный запрос
SELECT a.*, c.* FROM articles a INNER JOIN categories c ON a.category_id = c.id WHERE a.shared = 1
на
find_function = function(cond){
var categoriesHash = {};
var categoryIds = [];
db.categories.find(cond)
.forEach(function©{
categoriesHash[c._id.toString()] = c;
categoryIds.push(c._id);
});
var result = [];
db.articles.find({category_id: {"$in": categoryIds}}).forEach(function(a) {
a.category = categoriesHash[a.category_id.toString()];
result.push(a);
});
return result;
}
db.articles.eval(find_function);
db.articles.eval(find_function, {shared: 1});

немного не по мне. Дело вкуса конечно. ну и конечно я сомневаюсь, что тут будет высокая производительность.
+5
Frikazoid, #
В любом более менее адекватном обзоре, чуть ли не первым пунктом, говорится о целях. Каждая БД хороша в своей области.
Да и приведённый выше пример — всего лишь механика. Автор не затронул тонкости логического отличия подходов. Я имеюю ввиду «JOIN сделать возможно, а смысл?».
Архитектор системы должен расчитывать на большой «вес» одной записи. В структуру можно поместить целое дерево взаимосвязей, что автоматически отметает жойны мелких таблиц.
Я сразу слышу вопрос про изменяемые данные в отдельно взятой «мелкой таблице». Для этого в монго есть механизм ссылок. Подробнее в документации.
0
skorney, #
1) По поводу JOIN можно эмулировать (видео, слайды [слайд 52])
2) По поводу GROUP BY тоже есть (выглядеть будет примерно так — пример с книги):

db.runCommand({"group" : {
... "ns" : "stocks",
... "key" : "day",
... "initializer" : {"time" : 0},
... "$reduce" : function(doc, prev) {
... if (doc.time > prev.time) {
... prev.price = doc.price;
... },
... "condition" : {"day" : {"$gt" : "2010/09/30"}}
... })

НЛО прилетело и опубликовало эту надпись здесь
+3
NeLexa, #
Хочу поделиться ссылками, которые мне помогли при изучении MongoDB.

Соответствие MySQL и MongoDB запросов:
memo.undr.su/2010/01/27/sootvetstvie-mysql-i-mongodb-zaprosov/
www.dealtaker.com/blog/2010/05/12/php-mongodb-sitting-in-a-tree-part-1/
+1
zhulan0v, #
Где стоит использовать MongoDB? Можно какие-нибудь примеры из жизни с пояснением чем тут лучше подходит MongoDB, а не, например, MySQL?
+1
JetMaster, #
любую «цепочную» информацию очень удобно хранить в монгодб, например цепочка «друзья друзей»
m.habrahabr.ru/post/88246/
–2
Indeego, #
Хороший материал. Советую добавить ссылку на этот топик в разделе Q&A
+2
andreycha, #
Может в NoSQL перенести?
+1
Artima, #
За перевод спасибо, отличный материал. MongoDB действительно выглядит очень интересным решением, но я пока не встретил нормального описания вопроса сохранности информации. Что будет, если вдруг произойдет сбой? Как делается бэкап и т.д. В данный момент я боюсь потери информации.
0
remal, #
blog.mongodb.org/post/381927266/what-about-durability — это почему они отказались от single server durability. Правда эту фичу сделают в 1.8: jira.mongodb.org/browse/SERVER-980

Как делать бекапы и т.п. описано в мануале вполне понятным языком.

В теории, кроме отказов винтов, вы теряете изменения максимум за 60 секунд (время по-умолчанию для fsync'a). На практике же я встречал посты о том, что операция repair может испортить базу. Как с этим обстоит сейчас — не знаю.

Короче, мне кажется, если у вас только 1 сервер, то стоит дождаться версию 1.8.
0
Artima, #
На практике же я встречал посты о том, что операция repair может испортить базу.


Я тоже про это слышал. Это и пугает.

Про бекапы почитаю детальнее, но хотелось бы узнать во что это выливается практически. Я так понял, что при бекапе база блокируется на запись. А на сколько быстро работает бекап при различных объемах базы, например?
0
remal, #
Судя по доке (http://www.mongodb.org/display/DOCS/Backups), база не должна блокироваться. Хотя явно об этом тоже не написано…
0
Phil_ip, #
Статья интересная в качестве обзора MongoDB, да интересно было бы посмотреть на проект перенесенный с mysql, если это вообще возможно и стоит ли оно того…
0
zizop, #
Спасибо за статью, рекомендую вам посмотреть на
Doctrine MongoDB Object Document Mapper (ODM) от автора Doctrine ORM.
0
skorney, #
Так удобнее смотреть www.doctrine-project.org/projects/mongodb_odm
0
FTDeBUGgeR, #
Спасибо за перевод, все никак не доходили руки посмотреть, что за зверь. Теперь дойдут)
0
crocodile2u, #
А вот здесь уже собственно код примера…

// disconnect from server
$conn->close();
} catch (MongoConnectionException $e) {
die('Error connecting to MongoDB server');
} catch (MongoException $e) {
die('Error: '. $e->getMessage());
}
?>
0
crocodile2u, #
Чертов парсер… И ведь, главное, предпросмотр-то нормально все показывал…

В общем, пожалейте читателей, у вас в каждом примере дублируется подключение и try/catch, хотя это, очевидно, можно было бы показать один раз.
0
great_boba, #
Это перевод статьи, сохранил как у автора
0
silentroach, #
соединение можно и не закрывать
0
limp, #
с 1.7 (dev) версии консольный клиент поддерживает автокомплит
з.ы. можно использовать дев-клиент + стабильный демон
+1
nill, #
Давно искал такую статью, вообщем спасибо то что нужно.
0
VolCh, #
Имхо, не показано (или я пропустил?) самое интересные вещи в Монго и сородичах. Такое ощущение, что обычная реляционная БД с извращенным (с точки зрения SQL :) ) принципом. Я загорелся нереляционными документориентированными БД когда узнал, что в хранлище GAE (очень близком к Монго прежде всего по идеологии) в одной коллекции можно хранить документы с абсолютно разными полями. Простейший (для знатоков ООП и архитектурных паттернов) пример — в одной «таблице» можно хранить все экземпляры (объекты) какой-то иерархии классов (включая все объекты модели/приложения, так, в принципе, поступает CouchDB — без разделения на «таблицы»/«коллекции» — единый сторадж/репозиторий для объектов) без всяких «наследование в одной таблице», «наследование с таблицей для конкретного класса» и т. п. с нерациональным использованием места или проблемами ведения единого примари кей в нескольких таблицах.
0
pwlnw, #
А вот в хорошей литературе «sharding» все-таки переводят как «секционирование».
Но точно так же там переводят и partitioning.
0
great_boba, #
Ну по смыслу, наверное, его можно перевести как разделение или расслоение
0
Prapor, #
Странно. Мы говорим о БД или о том как программировать с использованием данной БД?
Когда мы говорим о реляционных БД мы рассуждаем не о том как писать SQL или как его включать в исходный код, а о том как структурированы данные в БД, каким образом распределяется пространство, как производится оптимизация запросов и т.д., а здесь? Смотрите как красиво код получается, ну и что с этого? Каким образом выделяется пространство под документы? Каким образом строятся индексы? Индексы строятся по документу или по отдельным значениям?
0
artem_kovardin, #
Именно то, что искал. Спасибо большое!
0
roller, #
интресно, а существует ли какой-то механизм коллбеков на добавление/удаление данных в коллекцию?

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