вторник, 22 ноября 2011 г.

Python and Mongodb.

Данный пост не претендует на новизну, но все же мне хотелось бы обратить внимание на эту вещь.
Нынче становится очень популярно использовать NoSQL где попало. Это очень модная тенденция и я думаю она все же приносит больше пользы чем вреда.
Каждый может ознакомится с краткой информацией здесь(но это уж совсем кратко) или ... в общем ищем в своем любимом поисковике.
Я не хочу вдаваться в вопросы хорошо это или плохо, а так же где это приемлемо использовать. Надеюсь это Вы решите сами, когда придет время.
А сейчас перейдем прямо к сути.

Хорошая документация имеется здесь или (частично на русском тут).
В принципе по одной из этих ссылок Вы можете найти почти все о чем я буду говорить. А вот основной задачей поста будет являться акцентирование внимания на пропущенные, но(на мой взгляд) очень важные вещи.

Пару слов о структуре.
В mongoDB есть три основных понятия, относящихся к хранению данных:
  1. База данных.
  2. Коллекция.
  3. Документ.
Что касается базы данных, то здесь ничего особенного нет. Говоря про "Коллекцию" можно проводить аналогию с таблицей, а по сути это просто набор документов произвольной структуры. Не смотря на то, что документы могут быть произвольной структуры, в коллекции как правило хранятся документы, которые имеют схожую структуру, так как над коллекцией документов выполняются основные задачи БД. Документ из себя представляет произвольный объект JSON. Если мы говорим о Python, то документ представляет из себя словарь произвольной структуры, ключами которого являются строки.

Добавление документов в коллекцию.
Добавление документов возможно либо по одному либо целым списком. Для этого у объекта коллекции имеются два метода:
save(doc)
insert(list_doc | doc)
Каждый документ коллекции имеет значение с ключом '_id'. Если это значение не указано, то оно добавляется автоматически и является уникальным. В случае, если в коллекцию попытаться добавить документ c '_id' который уже имеется у некоторого документа коллекции, перезапись документа не произойдет.

Выборка документов коллекции.
Для выборки предназначены два основных метода коллекции:
find(...)
find_one(...)
Первый метод возвращает объект Cursor, а второй непосредственно первый найденный документ удовлетворяющий заданным условиям.
О том как задавать условия выборки лучше почитать здесь.

По поводу выборки стоит сказать еще пару слов о самом объекте курсор, но об этом немного дальше.

Аналогично выборке работает функция удаления документов из коллекции, которая называется remove. Для удаления всей коллекции я советую использовать метод объекта Database drop_collection(name_collection), так как при использовании remove не удаляются индексы.

Обновление документов коллекции.
Для обновления данных коллекции основным является следующий метод:
update(query,data_for_update)
Вместо query указываются условия выборки документов, которые необходимо обновить. В качестве data_for_update указывается словарь специальной структуры. Каким образом формировать данный словарь лучше посмотреть здесь. По ссылке приводится документация которая соответствует непосредственно самому консольному клиенту mongodb, но синтаксис практически ничем не отличается, а логика совпадает.  

Здесь впервые я столкнулся с некоторым недоумением. Дело в том, что функция update имеет еще несколько дополнительных параметров, значение которых по умолчанию False:
upsert - если значение этого параметра  True, тогда в случае пустой коллекции, в нее добавляется документ.
manipulate - это есть что-то, что я не совсем понял(.
safe - при истинном значении этого параметра функция дожидается завершения операции и возвращает данные о выполнении обновлений.
multi - если этот параметр принимает значение  False, то обновляется только одна запись в базе, которая удовлетворяет условиям, если же  True - все записи.

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

Немного о индексах.
Для работы с индексами коллекции имеют два метода:
index_information() - выводит информацию о индексах коллекции.
ensure_index(key_name)- создает индекс по имени поля.


Индексация полей имеет как положительную сторону, так и не очень. Положительной является, то что индекс сортируется при добавлении документа в коллекцию, то есть когда Вы будете осуществлять выборку по индексированному полю с сортировкой или с условием больше A но меньше B (к примеру), то он очень поможет базе не тужиться. Но этот же плюс является очевидным минусом при добавлении документов. В общем если нужно часто выбирать и сортировать, то пользуемся индексацией полей(ключей документов).


Как и обещал про курсоры.
Метод find возвращает объект Cursor. Что же он имеет такого хорошего?

  1. У Cursor есть итератор, то есть его можно прямо использовать в for, map, filter и т.д.
  2. У Cursor есть метод next(), который возвращает следующий элемент выборки.
  3. Cursor якобы может имитировать параллельный доступ к элементам через оператор [], но прошу не обольщаться, так как он только якобы параллельный и выборка последнего элемента будет в n раз дольше чем выборка первого, где n - количество элементов выборки.
  4. Cursor имеет, как будто, два важных параметра: limit и skip. Первый устанавливает максимальное количество документов при выборке, второй - сколько элементов стоит пропустить. В реальной жизни никакого ускорения работы или существенной помощи данные параметры мне не оказали. В частности skip работает по следующему принципу: skip=m, тогда при выборке n-ого элемента он будет выбирать n+m элемент начиная с 0, то есть его работа точно повторяет прямые действия cur[n+m].

Здесь можно сделать один небольшой совет:
  Если необходимо делать выборку в стиле плывущего окна. то лучше добавить некий дополнительны параметр у документов коллекции и осуществлять плывущее окно следующей выборкой:
col_name.find({'id_float_wind':{ '$gt': N0, '$lt': N0+N }}), где N0 - начало окна, N - ширина окна.

Завершим с mapreduce.
Mapreduce это хорошая вещь, с её помощью можно неплохо масштабировать проекты, которые требуют больших объемов данных. Но у этого есть одно упущение, результат будет хорош если данные не имеют сложной структуры, и документы одной коллекции являются независимыми друг от друга. В общем про эту технологию написано довольно много. Рассмотрим как устроен интерфейс для mongodb.
Основной метод, который реализует mapreduce, является(метод коллекции):
map_reduce(map,reduce,...)
map и reduce это строки или объекты Code из модуля bson.code, которые содержат текст соответствующих функций на javascript. Как организован синтаксис этиx функций можно посмотреть тут.
Я скажу лишь пару слов, про то как это функционирует:
Mongodb и использование технологии mapreduce  предполагает, что база будет размещена на нескольких вычислительных узлах.
Функция map реализует выборку данных из коллекции для обработки, причем выборка осуществляется по принципу: проходим всю коллекцию; по каждому документу можем вернуть пару (ключ, значение); после этого значения с одинаковыми ключами объединяются в списки. Данная функция выполняется одним процессом.
Поясняю объединение значений по ключам:
Допустим есть набор пар
(key_1:val_1)
(key_2:val_2)
(key_1:val_2)
После объединения получим
(key_1:[val_1,val_3])
(key_2:[val_2])

Функция reduce реализует обработку данных. Данная функция распараллеливается.

Здесь можно столкнуться со следующими двумя проблемами(я сталкивался с двумя):

  1. Начиная (что-ли с версии 1.8) у функции map_reduce есть третий обязательный параметр - имя коллекции в которую необходимо поместить результат. Если указанной коллекции не существует, она будет создана, если же указать коллекцию с документами, перед помещением результатов все будет удалено. Советую третий параметр указывать всегда, будет проще при переходе на новую версию.
  2. В разных версиях mongodb функция reduce может выполняться или нет. Если Вы будете использовать mongodb используя родные репозитории проекта, то по умолчанию функция reduce вызывается только если в списке значений соответствующему одному ключу больше одного значения(как заставить работать для каждого не знаю). А вот напротив в версии из дистрибудива Debian reduce вызывается в любом случае.

Комментариев нет:

Отправить комментарий