Exprimant plusieurs colonnes db en python berkeley?
-
25-09-2019 - |
Question
Dire que j'ai une table simple qui contient le nom d'utilisateur, nom, prénom.
Comment puis-je exprimer en Db berkeley?
Je suis actuellement en utilisant bsddb comme l'interface.
Vive.
La solution
Vous devez choisir une « colonne » comme la clé (doit être unique, j'imagine que ce serait « nom d'utilisateur » dans votre cas) - les seules recherches de façon ne se fera jamais peut-être. Les autres colonnes peuvent être faites pour être la valeur de chaîne unique de cette clé par quelque façon que vous le souhaitez, de décapage à simple, se joindre à un caractère qui est garanti de se produire jamais dans l'une des colonnes, comme `\ 0' pour de nombreux types de "chaînes de texte lisibles".
Si vous devez être en mesure de rechercher par des clés différentes, vous aurez besoin d'autres, les bases de données bsddb supplémentaires et distincts mis en place comme « indices » dans votre table principale - il y a beaucoup de travail, et il y a beaucoup de littérature sur le sujet . (Sinon, vous passez à une technologie plus-abstraction, comme sqlite, qui gère l'indexation d'une manière ordonnée en votre nom; -).
Autres conseils
tl, dr: Pour exprimer plusieurs colonnes dans un magasin de valeur clé ordonnée comme Berkley db vous devez en apprendre davantage sur composition clé . Regardez mes autres réponses sur bsddb pour en savoir plus.
Il y a plusieurs façons de le faire en utilisant la clé ordonnée / magasin de valeur.
La solution la plus simple est de stocker des documents en tant que valeurs JSON avec une clé correcte.
Maintenant, vous voulez probablement construire l'index sur ces colonnes pour récupérer des documents sans avoir à itérer sur tous les hashmap pour trouver l'objet correct. Pour cela, vous pouvez utiliser un secondaryDB qui construira automatiquement l'index toi. Ou vous pouvez construire vous-même l'indice.
Si vous ne voulez pas traiter avec l'emballage clé (et il est une bonne idée pour le démarrage), vous peut profiter de DB.set_bt_compare qui vous permettra d'utiliser cPickle , JSON ou msgpack pour les clés et les valeurs tout en ayant un ordre qui fait sens pour créer des indices et des requêtes faites. Ceci est la méthode plus lente, mais introduire le modèle de composition clé .
Pour profiter pleinement ce qui est commandé clé, vous pouvez utiliser Cursor.set_range(key)
pour régler la position de la DB au début d'une requête.
Un autre modèle, est appelée modèle EAV magasins tuples qui suivent le schéma (entity, attribute, value)
puis vous construisez différents index en utilisant la permutation de ce tuple. J'ai appris ce modèle studing datomic.
Pour moins base de données faim de ressource, vous allez le chemin « typé statique » et stocker autant que possible des informations communes dans le tableau « métadonnées » et des documents séparés (qui sont SGBDR vraiment tables) dans leur propre hashmap.
Pour démarrer ici est une base de données exemple en utilisant bsddb (mais vous pouvez le construire en utilisant un autre magasin de clés commandé / valeur comme wiredtiger ou LevelDB) qui met en œuvre le modèle EAV. Dans cette implémentation j'échange EAV pour IKV qui se traduit par identifiant unique, clé, valeur. Le résultat overal est que vous avez un schéma entièrement indexé moins base de données de documents. Je pense qu'il est un bon compromis entre l'efficacité et la facilité d'utilisation.
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))