Допустим у нас есть объект — курсор из субд. Но мы хотим расширить его, для возвращения объектов. Добавить метод next_object(), который будет использовать метод getNext()
Проблема:
Мы не можем заставить модель возвращать объекты другого типа.
Решение 1: сделать итератор на этот курсор(который тоже является итератором). Сделать много магии для методов и свойств и получить новый итератор с нужным нам свойством.
Решение 2: Для PHP 5.4.0, использовать анонимные функции. О нём я и расскажу:
Внутри мапера, в методе findWhere($where) сделаем добавление анонимной функции в курсор, с передачей колбека и самого курсора
/** * @return MongoCursor */ public function findWhere($where) { $collection=$this->collection->find($where); $callback=$this; $collection->next_object=function() use ($collection, $callback) { $data=$collection->getNext(); $obj=$callback->create_object($data); return ($obj); }; return ($collection); }
Теперь, по идее можно обращаться к $collection->next_object() и получать объект, созданный мапером. Но к сожалению это не работает, т.к. PHP попробует выполнить user_func_array и не найдёт метод. Для этого присваиваем свойство переменной, значение которого является анонимной функцией и выполняем, через $func():
public function findOneWhere($where) { $collection=$this->findWhere($where); $func=$collection->next_object; return ($func()); }
Кривенько, но работает.
Забавная особенность метода count(), применительно к курсору результата запроса.
$items=$collection->find($where)->sort($sort)->limit($limit)->skip($offset*$limit); var_dump($items->count()); //1 var_dump($items->count(true)); //2
в первом случае получим count всех записей, без учёта limit и skip, но с учётом where
во втором случае получим count записей с учётом limit, skip, where.
Бэкапим. База при этом онлайн и обслуживает запросы.
mongodump -o DIR_NAMEВосстанавливаем. База при этом онлайн и обслуживает запросы.
mongorestore DIR_NAME
Пример:
igor@msws:~$ mongodump -o ~/bkp/2012_03_14/ igor@msws:~$ mongorestore ~/bkp/2012_03_14/
Всё
Готова первая версия агрегатора купонов http://otkuponer.ru/ (если не открывается, ждите пока приедут DNS-ы).
Как обычно минимальный дизайн. На текущий момент работает 2 системы купонов(биглион и групон), на подходе купикупон.
Проект выполняется в чистом стиле проектирования по модели предметной области, но без Active Record (ty to CanceRus). Для разработки используется Agile подход.
СУБД юзается MongoDB, программировать мега-приятно (ty to 10gen).
Фреймворк для разработки — старичок CodeIgniter (но по скорости работы он весьма не старичок).
В планах сделать парсеры на 100500 сайтов купонов и что-нить улучшить для посетителей.
Что интересного:
Узнал как найти и модифицировать findAndModify Command. Удобная команда, если нужно захватить документ на обработку(к примеру задание), результат выполнения команды — возвращение модифицированного документа.
Узнал как получить часть вложенного массива поля документа через «$slice»: Retrieving a Subset of Fields
Узнал как разработчики рекомендуют хранить древовидные комментарии:
Я ранее делал примерно так:
Вложенным способом отлично, все комментарии выборкой одного документа, быстро, интуитивно, выводить на страницу сайта достаточно легко.
Но и неприятности — есть ограничение документа в 16 мегабайт, про которое нужно помнить. Сложно искать в этом дереве.
Разработчики предложили:
Храним каждый комментарий как отдельный документ коллекции. У каждого комментария есть поле со значениями всех предшествующих предков(это для того, чтобы дёрнуть одним запросом все комментарии нужной ноды(чаще всего главной)), и поле с указанием прямого родителя. Красота.
Книга «PHP and MongoDB Web Development Beginner’s Guide (Rubayeet Islam)» достаточна интересна, описывает основные принципы работы с MongoDB из PHP. Читается быстро, за 4-8 часов(если пролистывать примеры =) ). Что особенно порадовало — разбирается map-reduce.
Это единственная книжка, которую нашёл по связке PHP — MongoDB, так что настоятельно рекомендую.
Ещё хотел бы написать о канале #mongodb на IRC freenode. Там более 300 человек, легко идут на контакт, так что велком.
В интернетах бытует мнение, что хранить файлы в БД — школоло. Разрушаем стереотип с Mongo GridFS.
Введение:
MongoDB предоставляет возможность хранить файлы любого размера в Mongo GridFS
Для драйвера PHP — это обычная коллекция, в которой есть поле для хранения бинарных данных.
Бинарные данные в свою очередь разбиваются на чунки(небольшие куски).
Бинарные данные можно извлечь как целиком, так и почунково(это экономит память).
Плюсы хранения данных в БД MongoDB:
MongoDB может хранить миллионы файлов в БД, хранение такого объёма данных в файловой системе может вызвать проблемы(начиная с IO проблем, заканчивая проблемами доступа к данным из разных серверов).
MongoDB реплицирует данные на все сервера, что упрощает доступ к данным на отдельно взятом сервере.
Нет проблем с бекапами и целостностью данных на разных серверах. надёжность обеспечивается MongoDB.
К загруженному файлу можно прикрепить метаданные (описание, комментарии, лайки, и.т.д.)
Т.к. файлы хранятся в чунках — можно работать с любой частью файла.
Минусы:
Быстродействие ниже, чем nginx + файловая истема. Сравним:
nginx + файловая система, получаем 6559.31 операций в секунду
apache + файловая система: получаем 2625.37 операций в секунду
nginx + модуль nginx-gridfs, получаем 1083.88 операций в секунду
Вывод: nginx + fs на высоте, но это решение не даёт масштабируемости и гибкости.
nginx + nginx-gridfs даёт приличные результаты, которые существенно возрастут, при использовании нескольких серверов MongoDB.
Благодарность, http://tokarchuk.ru за тесты.
Задача: хранить авторов к публикациям.
Делема: хранить авторов в отдельной коллекции или внутри документа публикации?
Решение #1:
{ "_id" : ObjectId("4dd491695072aefc456c9aca"), "username" : "alphareplicant", "email" : "roybatty@androids.org", "fullname" : "Roy Batty", "joined_at" : ISODate("2011-05-19T03:41:29.703Z"), "address" : { "street" : "13 Tannhauser Gate", "city" : "Caprica", "state" : "CC", "zipcode" : 512 }, }
Решение #2:
{ _id : ObjectId("4dcd2abe5981aec801010000"), title : "The only perfect site is hind-site", content : "Loren ipsum dolor sit amet…", saved_at : ISODate('2011-05-16T18:42:57.949Z'), author_id : ObjectId("4dd491695072aefc456c9aca") }
Так как же поступить?
Этот вопрос всегда возникает, при проектировании способа хранения данных. в nosql решениях.
Решение #1:
Плюсы: данные о авторе доступны в основном документе публикации, что экономит на количестве запросов в базу.
Минусы: Если документы существенно велики, и используется не курсор, а массив, для работы с данными. Можно допустить перерасход памяти.
решение #2:
Плюсы: лёгкие документы
Минусы: дополнительная коллекция, документы которой можно извлечь дополнительным запросом.
Когда вы делаете find(), драйвер MongoDB старше версии 1.1.0+ пошлёт запросы на получение данных на один из слейвов. Причём если вы его не определили при запросе, драйвер сам его выберет.
Автоматически будет выбран слейв с лучшим пингом =)
Более подробнее здесь http://www.php.net/manual/en/mongo.queries.php
Ну и всегда нужно понимать, что данные на слейве немного не актуальны, ядро важных операций заставляйте работать с мастером.
имхо очень хорошая штука.