Expresando múltiples columnas en berkeley db en python?

Digamos que tengo una tabla simple que contiene nombre de usuario, nombre, apellido.

¿Cómo expreso esto en berkeley Db?

Actualmente estoy usando bsddb como la interfaz.

Aclamaciones.

    Tiene que elegir una “columna” como clave (debe ser única; imagino que en su caso sería “nombre de usuario”): la única forma en que las búsquedas puedan ocurrir. Las otras columnas se pueden convertir en el único valor de cadena de esa clave de la forma que desee, desde el encurtido hasta la unión simple con un carácter que se garantiza que nunca aparecerá en ninguna de las columnas, como `\ 0 ‘ “cadenas de texto legibles”.

    Si necesita poder buscar por diferentes claves, necesitará otras bases de datos bsddb adicionales y complementarias configuradas como “índices” en su tabla principal: es mucho trabajo y hay mucha literatura sobre el tema. (Alternativamente, usted pasa a una tecnología de mayor abstracción, como sqlite, que maneja la indexación de manera ordenada en su nombre ;-).

    tl, dr: para express varias columnas en un almacén de valores clave ordenados como berkley db, debe conocer la composición de las claves . Busque mis otras respuestas sobre bsddb para obtener más información.

    Hay varias formas de hacerlo utilizando el almacén de clave / valor ordenado.

    La solución más sencilla es almacenar documentos como valores json con una clave correcta .

    Probablemente ahora desee crear un índice sobre esas columnas para recuperar documentos sin tener que iterar sobre todo el hashmap para encontrar el objeto correcto. Para eso, puedes usar un archivo secundario que construirá automáticamente el índice para ti. O puedes construir el índice tú mismo.

    Si no quiere lidiar con el empaquetado de claves (y es una buena idea para iniciar), puede aprovechar DB.set_bt_compare que le permitirá usar cpickle, json o msgpack tanto para las claves como para los valores mientras aún tiene un Orden que hace sensate crear índices y hacer consultas. Este es un método más lento pero introduce el patrón de composición clave .

    Para aprovechar al máximo la clave ordenada, puede utilizar Cursor.set_range(key) para establecer la posición de la base de datos al comienzo de una consulta.

    Otro patrón, denominado patrón de EAV, almacena las tuplas que siguen el esquema (entity, attribute, value) y luego crea varios índices mediante el uso de la permutación de esa tupla. Aprendí este patrón estudiando datomic.

    Para una base de datos con menos recursos, pasará por la “forma estática” y almacenará la información más común posible en la tabla de “metadatos” y dividirá los documentos (que en realidad son tablas RDBMS) en su propio mapa hash.

    Para comenzar, aquí hay una base de datos de ejemplo que utiliza bsddb (pero puede comstackrla utilizando otro almacén de clave / valor ordenado como wiredtiger o leveldb) que implementa el patrón EAV. En esta implementación, cambio EAV por IKV, lo que se traduce en un identificador único, clave, valor. El resultado general es que tiene un esquema completamente indexado, menos una base de datos de documentos. Creo que es un buen compromiso entre la eficiencia y la facilidad de uso.

     import struct from json import dumps from json import loads from bsddb3.db import DB from bsddb3.db import DBEnv from bsddb3.db import DB_BTREE from bsddb3.db import DB_CREATE from bsddb3.db import DB_INIT_MPOOL from bsddb3.db import DB_LOG_AUTO_REMOVE def pack(*values): def __pack(value): if type(value) is int: return '1' + struct.pack('>q', value) elif type(value) is str: return '2' + struct.pack('>q', len(value)) + value else: data = dumps(value, encoding='utf-8') return '3' + struct.pack('>q', len(data)) + data return ''.join(map(__pack, values)) def unpack(packed): kind = packed[0] if kind == '1': value = struct.unpack('>q', packed[1:9])[0] packed = packed[9:] elif kind == '2': size = struct.unpack('>q', packed[1:9])[0] value = packed[9:9+size] packed = packed[size+9:] else: size = struct.unpack('>q', packed[1:9])[0] value = loads(packed[9:9+size]) packed = packed[size+9:] if packed: values = unpack(packed) values.insert(0, value) else: values = [value] return values class TupleSpace(object): """Generic database""" def __init__(self, path): self.env = DBEnv() self.env.set_cache_max(10, 0) self.env.set_cachesize(5, 0) flags = ( DB_CREATE | DB_INIT_MPOOL ) self.env.log_set_config(DB_LOG_AUTO_REMOVE, True) self.env.set_lg_max(1024 ** 3) self.env.open( path, flags, 0 ) # create vertices and edges k/v stores def new_store(name): flags = DB_CREATE elements = DB(self.env) elements.open( name, None, DB_BTREE, flags, 0, ) return elements self.tuples = new_store('tuples') self.index = new_store('index') self.txn = None def get(self, uid): cursor = self.tuples.cursor() def __get(): record = cursor.set_range(pack(uid, '')) if not record: return key, value = record while True: other, key = unpack(key) if other == uid: value = unpack(value)[0] yield key, value record = cursor.next() if record: key, value = record continue else: break else: break tuples = dict(__get()) cursor.close() return tuples def add(self, uid, **properties): for key, value in properties.items(): self.tuples.put(pack(uid, key), pack(value)) self.index.put(pack(key, value, uid), '') def delete(self, uid): # delete item from main table and index cursor = self.tuples.cursor() index = self.index.cursor() record = cursor.set_range(pack(uid, '')) if record: key, value = record else: cursor.close() raise Exception('not found') while True: other, key = unpack(key) if other == uid: # remove tuple from main index cursor.delete() # remove it from index value = unpack(value)[0] index.set(pack(key, value, uid)) index.delete() # continue record = cursor.next() if record: key, value = record continue else: break else: break cursor.close() def update(self, uid, **properties): self.delete(uid) self.add(uid, **properties) def close(self): self.index.close() self.tuples.close() self.env.close() def debug(self): for key, value in self.tuples.items(): uid, key = unpack(key) value = unpack(value)[0] print(uid, key, value) def query(self, key, value=''): """return `(key, value, uid)` tuples that where `key` and `value` are expressed in the arguments""" cursor = self.index.cursor() match = (key, value) if value else (key,) record = cursor.set_range(pack(key, value)) if not record: cursor.close() return while True: key, _ = record other = unpack(key) ok = reduce( lambda previous, x: (cmp(*x) == 0) and previous, zip(match, other), True ) if ok: yield other record = cursor.next() if not record: break else: break cursor.close() db = TupleSpace('tmp') # you can use a tuple to store a counter db.add(0, counter=0) # And then have a procedure doing the required work # to alaways have a fresh uid def make_uid(): counter = db.get(0) counter['counter'] += 1 return counter['counter'] amirouche = make_uid() db.add(amirouche, username="amirouche", age=30) print(db.get(amirouche))