#!/usr/bin/env python
#!/usr/bin/env python
# formato claves utilizado
#
# post:next_id id que se incrementa por cada post guardado
# post:$id:content contiene la informacion de un post
# post:$id:comments contiene los ids de los comentarios de este post
# comment:$id:content contiene la informacion de un comentario
# coment:next_id id que se incrementa por cada comentario guardado
import tc, json
db = tc.HDB("sample.hdb",tc.HDBOWRITER | tc.HDBOCREAT)
db.vanish()
# Almacenar un "post"
pid = db.addint("post:next_id",1)
key = "post:" + str(pid)+":content"
value = json.dumps({"title":"primer post", "content":"este es un post muy popular"})
db.put(key, value)
# Agregar unos cuantos comentarios al post anterior
for i in xrange(1,10):
cid = db.addint("comment:next_id",1)
# Enlazamos el id del comentario actual con el post
comments = "post:"+str(pid)+":comments"
if not db.has_key(comments):
db.put( comments,str(cid) )
else:
db.putcat( comments,","+str(cid) )
# ahora si, almacenamos el comentario.
key = "comment:" + str(cid)+ ":content"
value = json.dumps( {"author":"Mikio","content":"This is a greate post men!"} )
db.put(key,value)
post = json.loads(db.get("post:"+str(pid)+":content"))
comments = []
for com in db.get("post:"+str(pid)+":comments").split(","):
comments.append( json.loads( db.get("comment:"+str(com)+":content") ) )
print post['title']
print "\t" + post['content']
print "\n"
print "Comentarios..."
print "\n".join( str(c['author'] + "\t" + c['content']) for c in comments )
Y esto que es?? pues bueno, es simplemente un pequeño programa que hace uso de la libreria tc que es un “wrapper” para Tokyo Cabinet, talvez puede verse muy criptico, pero por lo menos muestra algo diferente que los ejemplos convencionales que simplemente hacen esto
#!/usr/bin/env python
import tc
db = tc.HDB("sample.hdb" HDBOWRITER | HDBOCREAT)
db.put("foo", "bar")
db.get("foo")
El programa de mas arriba lo unico que hace es crear un post (una entrade de un blog por ejmp) y le asigna 10 comentarios al mismo y todo esto lo almacena en una base de datos HDB de tokyo cabinet, osea una HASH DATA BASE, donde solo podemos utilizar claves->valor, con esto podemos asumir una relacion one-to-many que es la mas comun en bases de datos relacionales. Ahora si, la explicación
en las primeras lineas de la 1 - 8 simplemente se define que el siguiente codigo sera interpretado por python y se crean algunos comentarios que revelan el formato de claves utiizados en Tokyo Cabinet, las claves en TC pueden ser tanto Strings como Binarios (?), algo que he notado es que, entre mas especifica sea una clave mas facil va ser acceder a sus valores. algo a notar es que las claves en TC pueden ser de cualquier tamaño, peero entre mas largas mas espacio en disco seva a utilizar.
import tc, json
db = tc.HDB("sample.hdb",tc.HDBOWRITER | tc.HDBOCREAT)
db.vanish()
aqui simplemente estamos importando uno de los tantos bindings para python, tc, q lo pueden encontrar aca, luego estare hablando de otros bindings que podemos utilizar. Tambien importamos la libreria para la gestion de formatos JSON, muy utilizados por su flexibilidad a la hora serializar datos. por otro lado, utilizamos la primera funcion que hace uso de TC
db = tc.HDB("sample.hdb",tc.HDBOWRITER | tc.HDBOCREAT)
simplemente creamos una nueva base de datos hash apartir del consturctor tc.HDB que recive como primer parametro el nombre que se le dara a la base de datos (que se creara en el mismo directorio donde ejecutemos el codigo) y luego le sigue una lista de constantantes que simplemente abren la BD para escritura (HDBOWRITER) y si no existe, se crea con (HDBOCREAT).
db.vanish()
ya teniendo una descriptor de fichero enlazado a TC, tenemos una serie de metodos a disposicion que nos sirven para gestionar el acceso a TC, siendo vanish, como su doc string lo indica, una funcion que elimina todos los registros de una base de datos hash.
pid = db.addint("post:next_id",1)
key = "post:" + str(pid)+":content"
value = json.dumps({"title":"primer post", "content":"este es un post muy popular"})
db.put(key, value)
y es aqui donde por fin almacenamos un par de datos, (seria algo asi como un INSERT en un RDMBS). Una de las funciones interesantes de los sitemas NoSQL son las orperaciones atomicas, las cuales nos proveen de una forma mas sencilla de hacer operaciones comunes, en este caso con db.addint(“post:next_id”,1) incrementamos un ID el cual nos servira para identificar cualquier post, lo podriamos ver como una operacion manual de los tipos AUTO_INCREMENT en los RDBMS, notar que addint recive 2 parametros, uno es el nombre de la clave (recordad que estamos en un sistema solo clave valor) y la otra es un entero que le sumaremos a esa clave, y TC nos devolvera la suma, osea q si esta funcion se repite 5 veces, pues el ultimo valor de la clave de post:next_id sera 5, sencillo :).
En la segunda linea simplemente creamos una “clave” que utilizaremos en TC siguiendo el formato definido en los comentarios, que era post:$ID:content, asi creamos una clave que valdra post:1:content, si creamos otro sera post:2:content, y asi sucesivamente… “para el formato de claves podeis utilizar el mas apropiado para ustedes :)”
en la tercera linea definimos cual sera el contenido, algo que nunca se nos debe olvidar es que LOS VALORES EN TC SON CASI SIEMPRE STRINGS, no hay tipos de datos, ni date, ni NULL ni nada.. hasta ahora reconozco los enteros, los double ,los string y binarios. y lo que se haces es simplemente serializar (o pasar un dict a un string con json.dumps) un diccionario para luego almacenarlo en TC.
y ya, en la ultima linea, con db.put(key,value) almacenamos la clave->valor en TC, pudiendola recuperar luego por medio de su clave, de la cual ya sabemos cual es el formato, pues nosotros mismos los definimos.
for i in xrange(1,10):
cid = db.addint("comment:next_id",1)
# Enlazamos el id del comentario actual con el post
comments = "post:"+str(pid)+":comments"
if not db.has_key(comments):
db.put( comments,str(cid) )
else:
db.putcat( comments,","+str(cid) )
# ahora si, almacenamos el comentario.
key = "comment:" + str(cid)+ ":content"
value = json.dumps( {"author":"Mikio","content":"This is a greate post men!"} )
db.put(key,value)
Este ciclo de 1 a 10 puede parecer muy criptico a simple vista. pero si se mira detenidamente, es muy logico y sencillo de seguir. primero que todo, en cada iteracion estamos incrementando un ID de uno en uno que utilizaremos por cada comment como se hizo previamente con los posts.
y que es eso de, enlazar el id del comentario actual con el post ??, pues sencillo, que es lo que hariamos si estuviaramos en un RDBMS ? posiblemente creariamos dos tablas, una para posts y otra para comentarios, y en cada comentario guardariamos el ID del post al que le pertenece, luego, todo lo uniriamos con una consulta tipo JOIN. Pues aca juega algo parecido pero un poco mas “complejo??”, ya que solo tenemos una forma de almacenar datos que es clave->valor, nos ponemos a pensar de cual es la mejor forma y la mas optima para resolver nuestro problema, por mi parte decidi crear un clave tipo post:IDPOST:comments, esta clave almacena un string que tiene una lista separada por “,” de los ids de comentarios que le pertenecen, y eso es lo que se hace en las subsiguientes 3 lineas, el IF simplemente verifica que la clave “post:IDPOST:comments” no exista y asi crear una lista mas organizada, el resultado seria algo asi “1,2,3,4,5…” y no “,1,2,3,4,5…” (notar la coma al principio). Tambien entra en juego el metodo PUTCAT, que simplemente, como dice el doc string, concatena un valor al final de un registro existente, y pues lo usamos para ir concatenando la lista de los ids uno en uno.
ya con esto simplemente nos queda crear el comentario, al igual que en post, creamos un key y un value utilizando los formatos seguidos en los comentarios iniciales “comment:IDCOMMENT:content” y el valor sera un STRING en formato JSON luego lo almacenamos todo con db.put(key,value).
post = json.loads( db.get("post:"+str(pid)+":content") )
comments = []
for com in db.get( "post:"+str(pid)+":comments" ).split(","):
comments.append( json.loads( db.get("comment:"+str(com)+":content") ) )
esta ultima parte es tan interesante como la anterior, se podria homologar con la funcion SELECT en los RDBMS, pues simplemente estamos realizando lecturas de lo ya almacenado.
primero que todo en la variable post lo que hacemos es, de adentro hacia afuera: recoger el valor que hay en la clave “post:ID:content”, ya sabemos el formato de la clave, solo queda extraer la informacion. luego de extraida, como sabemos que la guardamos en formato JSON, pues la volvemos a su estado inicial, el cual era un diccionario, y eso lo hacemos con json.loads(), y ya con esto tenemos un completo diccionario con la informacion que almacenamos al inicio.
notar que la variable comments es una lista vacia que simplemente se llenará con cada uno de los comentarios creados, sera algo asi como una “Lista de Diccionarios” pues cada comentario lo guardamos en formato JSON.
el ciclo hace algo interesante, aca lo que iteramos es los ID’s de los comentarios que le pertencen al post, recuerdan el formato “post:IDPOST:comments” ??, pues aca lo estamos utilizando, que esto no es mas sencillo que ir recorriendo clave por clave en toda la base de datos preguntando si es o no un comentario de X o Y post??.
“post:IDPOST:comments” simplemente es un string que se encuentra en el formato “1,2,3,4,5,6,7,8,9,10”, entonces utilizamos la funcion split() de los strings en python para dividir esa cadena por el caracter “,”, lo que nos devolvera una lista con cada numero, con el cual obtendremos los datos de cada post :D.
dentro del ciclo se hace algo sencillo, desde dentro hacia afuera, recogemos con db.get() el valor de cada clave “‘comments’+str(com)+’content’”, lo retornamos a su estado original de diccionaro con json.loads() y lo agregamos a la lista de comentarios “comments”.
sencillo no?
print post['title'] print "\t" + post['content'] print "\n" print "Comentarios..." print "\n".join( str(c['author'] + "\t" + c['content']) for c in comments
el final es pura carpinteria, tenemos una variable con el post y otra con la lista de comentarios. no queda mas que imprimir.
por cierto, aca les dejo una imagen de como se veria + o - la base de datos al final. no asi de organizada, pero es facil de comprender :)

y esto es todo, un simple ejemplo de como utilizar Tokyo Cabinet para algo interesante. que se pueden hacer muchos cambios?? si! bastantes, pero eso para otro post :) comentarios? dudas?? disparame :D
agradecimientos principalmente a:
Comments powered by Disqus