Obsługa bazy dokumentów CouchDB w Pythonie

CouchDB to bezschematowa i nierelacyjna baza danych "dokumentów" napisana w Erlangu. Obecnie projekt znajduje się w fazie rozwojowej, lecz jest dość użyteczny i aktywnie rozwijany pod egidą Fundacji Apache. W ostatnich miesiącach coraz głośniej o tym projekcie na różnych konferencjach, jak i poprzez wykorzystanie CouchDB w nowych projektach. Oto krótka charakterystyka:
  • Dostępna przez API REST (HTTP POST/PUT/GET/DELETE)
  • Bezschematowa i nierelacyjna.
  • Replikowalna
  • Widoki o dużych możliwościach
  • JSON na wejściu i wyjściu
  • Napisana w Erlangu - współbieżnym języku programistycznym
W praktyce oznacza to że możemy w bazie przechowywać "dokumenty" - powiedzmy wizytówki klientów i dla dowolnej wizytówki mieć różny zestaw danych.

Instalacja

Na wiki CouchDB znajdziemy sekcję dotyczącą instalacji CouchDB pod różnymi dystrybucjami i systemami operacyjnymi. Oprócz zainstalowania samej aplikacji należy zająć się wstępną konfiguracją czy skryptami startowymi (lecz tym powinny zająć się pakiety dla poszczególnych dystrybucji). Ja skorzystałem z ebuilda dla Gentoo i otrzymałem gotowy do pracy serwer baz danych. Istnieje także pakiet Pythona do obsługi tej bazy danych - couchdb-python, który możemy zainstalować poleceniem:
easy_install CouchDB
Po zainstalowaniu CouchDB powinniśmy być w stanie uruchomić serwer baz danych (pod Gentoo /etc/init.d/couchdb start). Gdy serwer zostanie uruchomiony pod adresem http://localhost:5984/_utils/ powinien dostępny być panel CouchDB (odpowiednik phpmyadmin), za pomocą którego możemy zarządzać bazami danych.
couch2
couch1

Jak to działa

Dane trzymane są w postaci struktur JSON posiadających unikalne ID oraz numer rewizji. W przypadku zmiany wpisu nowa wersja jest dopisywana z nowym numerem rewizji. Stare rekordy istnieją aż do przeczyszczenia bazy danych ("Compact"). Celem dopisywania, a nie nadpisywania jest kontrola integralności, baza też nie pozwoli na aktualizacje jeżeli posługujemy się starszą rewizją. By pobrać określone rekordy w CouchDB stosuje się widoki (perspektywy) napisane w JavaScript stosujące Map/Reduce (choć można skorzystać z serwera widoków napisanego w innym języku!). Dane jakie widok zwraca generowane są przy jego pierwszym wywołaniu, tak więc kolejne wywołania od razu dostają przygotowane dane (choć istnieją widoki tymczasowe wykonywa za każdym razem). Jest to szczegółowo opisane w dokumentacji.

Obsługa CouchDB z poziomu Pythona

couchdb-python dostarcza trzy biblioteki - serwer widoków dla funkcji napisanych w Pythonie, konwertera obiektów Pythona na struktury JSON (i na odwrót) oraz standardowego klienta do bazy danych. Dokumentację do couchdb-python znajdziemy w postaci Docstringów w plikach pakietu. Oto prosty przykład zastosowania klienta:
from couchdb import *

server = Server('http://localhost:5984/')
# wybieramy istniejącą bazę
db = server['test01']
#lub tworzymy:
#db = server.create('test01')
# wyświetlamy rekord o id "1"
print db['1']

#dodajemy rekord
doc_id = db.create({'name': 'John', 'surname': 'Doe'})
# i go wyświetlamy
print db[doc_id]
Klasa couchdb.schema umożliwia także tworzenie modeli dokumentów (pewnie strukturyzowanie danych):
# -*- coding: utf-8 -*-
from couchdb import *
from couchdb.schema import Document, TextField, IntegerField, DateTimeField
import datetime

# połączenie z serwerem
server = Server('http://localhost:5984/')

# tworzymy, bądź usuwamy/tworzymy bazę na fotki
try:
	db = server.create('fotki')
except:
	del server['fotki']
	db = server.create('fotki')

class Fotka(Document):
	name = TextField()
	desc = TextField()
	h = IntegerField()
	w = IntegerField()
	added = DateTimeField(default=datetime.datetime.now())

# dodajemy kilka fotek
f = Fotka(name='Moje foto rodzinne', desc='ciekawa fota', h = 100, w = 200)
f.store(db)
f = Fotka(name='Ja i zdzisiek', desc='no comments', h = 200, w = 300)
f.store(db)
f = Fotka(name='Tapeta01', desc='wyjechana tapeta', h = 1200, w = 1300)
f.store(db)

# pobieramy wszystkie fotki
for uuid in db:
   print Fotka.load(db, uuid)

# wykonujemy zapytanie za pomocą widoku tymczasowego
print
code = "function(d) { if (d.h > 1000 ) emit(d.name,null); }"
result = db.query(code)
for res in result:
	print res.key
Widoki trwałe (permanent) tworzy się dodając do bazy dokumentów specjalny wpis (Design Document) postaci:
{'_id': '_design/fotki', 'language': 'javascript', "views": KOD}
Gdzie wartość pola _id musi zaczynać się od _design/ i kończyć nazwą grupy widoków. Wartość klucza views to kod widoków, np:
{
    "all": {
      "map": "function(doc) { emit(null, doc) }"
    },
    "by_height": {
      "map": "function(doc) { emit(doc.h, doc) }"
    },
    "tapety": {
      "map": "function(doc) { if (doc.h > 1000)  emit(doc.h, doc) }",
    }
  }
Pierwszy widok zwraca wszystkie wpisy, drugi sortuje po wysokości (kluczem jest wysokość i podlega sortowaniu), a trzeci zwraca tylko fotki o wysokości ponad 1000 pikseli. Trwały widok można dodać (i zarządzać) w Fultonie:
couchdb_n1
Po dodaniu widoków stają się dostępne na liście wyboru dokumentów:
couchdb_n2
Można to zrobić też z poziomu Pythona za pomocą ViewDefinition. Oto rozszerzenie powyższego przykładu:
from couchdb.design import ViewDefinition
...
# dodajemy trwały widok
print
tapety = ViewDefinition('fotki', 'tapety',
    'function(doc) { if (doc.h > 1000)  emit(doc.h, doc) }')

ViewDefinition.sync_many(db, [tapety, ])

# wywołujemy
for res in db.view('fotki/tapety'):
	print res.value

W Sieci

Obsługa CouchDB z poziomu JavaScript
Prezencja o CouchDB - PDF, Polska
Using CouchDB with Django
RkBlog

Programowanie Sieciowe, 17 February 2009

Comment article
Comment article RkBlog main page Search RSS Contact