PHP Delphi CSS HTML JavaScript Perl API ASP MySQL XML С++ VBasic WEB разработка *NIX CouchDB Hack Python
Главная Статьи CouchDB Использование CouchDB
Главная
 Главная  Контакты
 
Программинг
Статьи Книги ЧаВО
 
xBOOKi
Fresh Books Операционки Сети
 
Поиск
-------
 
Counters
Яндекс цитирования
Rambler's Top100
-------
 
CryptDisk.4h
Программа которая позволяет создать виртуальный шифрованный логический диск.

cryptdisk.4hack.com

-------
 
 

Продолжаю знакомить с CouchDB, сегодня я опишу, как работать с этой документно-ориентированной БД на примере организации разграничения прав доступа пользователей.

Рассмотрим типичную модель в релиационной БД, реализующую хранение таких данных.

 

Модель разграничения прав доступа

 

Каждый пользователь может входить в несколько групп при этом каждой группе пользователей может быть назначено несколько прав доступа. Для определения прав для пользователя в таком случае мы выполняем SELECT с 2-мя JOIN-ами по таблицам group и permission. А теперь попробуем реализовать это в CouchDB.

Понятие View

В CouchDB можно определять view. View — это по сути функции, которые могут быть написаны на любом языке программирования, которые получают на вход все документы из БД по очереди и выбирают некоторые из них по какому-либо условию. По умолчанию функции пишутся на Javascript, но можно писать на PHP, Python или другом языке, как утверждается в документации, только я пока не знаю, как)

Вьюхи хранятся в CouchDB в виде документов, как и обычные данные. Каждый документ, хранящий вьюхи, может содержать в себе неограниченное количество view-функций, это своего рода способ группировать view-функции. Обычно в один документ помещаются функции, возвращающие похожие наборы данных.

Практический пример

Рассмотрим пример запроса, возвращающего всех пользователей из БД. Т.к. документов может быть много, чтобы их кластеризовать, целесообразно ввести свойство type в документы, которое для документов, хранящих информацию о пользователях, будет type = "user", что мы и используем при выборке:

function ( doc )
{

if ( doc.type == 'user' )
{
emit( doc._id, doc._id );
}
}

Эта простая функция, которая получает по очереди каждый из документов, и те, у которых свойство type="user" передает в специальную функцию emit(). Функция emit получает в качестве аргументов пару ключ-значение и создает индекс из полученных ключей.

Ключ может быть любого типа, не обязательно id и целое число, как здесь, об этом будет несколько слов чуть ниже. В результате данная функция возвратит ID всех пользователей, что достаточно для выборки конкретных user-документов по их ID в дальнейшем.

Возвращаемые view данные всегда сортируются по ключу.

Усложняем view-функцию

Можно выстраивать более сложные условия при выборке документов. Следующий пример, также довольно простой, возвращает id пользователей, которые подтвердили свою регистрацию, что определяется по свойству confirmed, установленному в true. Полное JSON-описание view-документа с обеими функциями, сохраненного в /database/_design/users, будет выглядеть следующим образом:

{
"language": "javascript",
"views": {
"all": {
"map": "function ( doc )
{
if ( doc.type == 'user' )
{
emit( doc._id, doc._id );
}
}"
},
"registered": {
"map": "function ( doc )
{
if ( ( doc.type == 'user' ) &&
(doc.confirmed === true ) )
{
emit( doc._id, doc._id );
}
}"
}
}
}

Теперь мы определили view-функции, которые возвращают 2 разных набора документов, например, функция «registered» доступна по адресу /database/_view/users/registered. На размер выборки можно влиять параметрами skip и count (аналоги LIMIT) или keys, в котором указывается список ключей документов, передающихся во view-функцию. Это детально описано в документации, сейчас этот момент не принципиален.

Добавляем группы и права доступа

В соответствии с описанной ранее моделью нам нужно добавить группы и права доступа в CouchDB, что мы реализуем с помощью введения документов групп, содержащих ассоциацию между списком пользователей и списком назначенных им прав. Пример такого документа будет выглядеть следующим образом:

{
"_id" : "group-users",
"_rev" : "7984327592",
"type": "group",
"name" : "Users",
"permissions": [
"wiki-read",
"wiki-write"
],
"users": [
"user-kore",
"user-john"
]
}

В документе содержится лишь название группы (name), ID, который есть во всех документах в CouchDB, тип (type) и 2 массива с правами и идентификаторами пользователей. Таким образом, мы уместили данные, которые в реляционной БД лежали бы в 5-ти таблицах, в каких-то 2 типа документов. Теперь попробуем достать эти данные.

Один из самых популярных запросов в данном случае — это получение списка прав пользователей. Напишем view-функцию для этого:

function( doc )
{

if ( doc.type == "group" )
{
for ( var i = 0; i < doc.users.length; ++i )
{
emit( doc.users[i], doc.permissions );
}
}
}

В этой фунции мы возвращаем массив прав для каждого пользователя, ассоциированного с текущей группой. CouchDB отлично работает со множествами значений, можно ассоциировать массив данных с одним ключом. Если при вызове данной фунции задать параметр key, например, со значением "user-kore", мы получим следущее:

{
"rows": [
{
"key": "user-kore",
"value": [
"wiki-read",
"wiki-write"
]
}
]
}

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

{
"language": "javascript",
"views": {
"user_permissions": {
"map": "function( doc )
{
if ( doc.type == "group" )
{
for ( var i = 0; i < doc.users.length; ++i )
{
emit( doc.users[i], doc.permissions );
}
}
}",
"reduce": "function( keys, values )
{
var permissions = [];
for ( var i = 0; i < values.length; ++i )
{
if ( permissions.indexOf( values[i] ) == -1 )
{
permissions.push( values[i] );
}
}
return permissions;
}",
},
}
}

Функция reduce получает в качестве параметров 2 массива: ключи и значения — которые сформировались функцией map. На базе этих данных мы можем создать новые, модифицируя выбранные ранее данные или агрегируя их в какое-то значение.

В примере reduce-функция формирует из полученных массивов прав пользователей один массив неповторяющихся значений. Если вызвать view без параметров, то она вернет все права всех пользователей. Если же указать параметром key пользователя, то view вернет все его права:

{
"rows": [
{
"key": null,
"value": [
"wiki-read",
"wiki-write"
]
}
]
}

Другие интересные возможности

Вложения

Каждый документ может иметь любое количество прикрепленных к нему файлов, при этому они будут доступны по конкретному URL. Если к документу прикрепить файлы, у него появляется новое свойство, которое будет содержать список этих файлов.

View collation

Фича CouchDB, которую я не смог перевести корректно, оставил как есть) Ее суть в следующем. Ключи, передаваемые в функцию emit(), могут иметь любую JSON структуру. В сочетании с тем, что возвращаемые ею данные сортируются по ключу, это позволяет с легкостью реализовать, например, древовидную структуру данных, что очень полезно в каталогах, навигации по сайту и т.п. За деталями прошу в документацию )

PHPillow

PHPillow —довольно интересная обертка на PHP для работы с CouchDB. Ее единственный, пожалуй, недостаток — работает только под PHP 5.3 и выше. На ее базе я сделал небольшую ORM для Symfony. Пришлось несколько изменить код, чтобы все заработало на PHP 5.1–5.2. Как только оформлю в подобающий вид, обязательно выложу.

--

Написано по мотивам CouchDB - A use case Кори Нордмана. Данная статья находится под лицензией CC by-sa, чего требует исходная статья.

tags: CouchDB, document, map-reduce, view, использование CouchDB
source: http://blog.salikhovilyas.ru/2009/02/05/couchdb-using/



Свежее
Резервное копирование rsync-ом
DNS Amplification (DNS усиление)
Алгоритм Шинглов — поиск нечетких дубликатов текста
Metasploit Framework. Обзор
Использование CouchDB
-------



 
Copyright © 2003-2009   Frikazoid.
Rambler's Top100