Esprimendo più colonne a Berkeley DB in Python?
-
25-09-2019 - |
Domanda
Di 'Ho una semplice tabella che contiene il nome utente, nome, cognome.
Come faccio a esprimere questo concetto in Berkeley DB?
Attualmente sto usando bsddb come interfaccia.
Saluti.
Soluzione
Si deve scegliere una "colonna" come chiave (deve essere unico, immagino che sarebbe "username" nel tuo caso) - l'unico modo in cui le ricerche potrà mai accadere. Le altre colonne possono essere fatti per essere il valore stringa singola di quella chiave da qualsiasi modo che ti piace, dal decapaggio alla semplice entrare con un carattere che è garantito per non verificarsi in una delle colonne, come ad esempio `\ 0' per molti tipi di "stringhe di testo leggibili".
Se avete bisogno di essere in grado di cercare per diverse chiavi avrete bisogno di altri, complementari e distinte banche dati Bsddb impostati come "indici" nel tuo tabella principale - E 'un sacco di lavoro, e c'è un sacco di letteratura sull'argomento . (In alternativa, si passa a una tecnologia superiore di astrazione, come ad esempio SQLite, che gestisce l'indicizzazione ordinatamente a vostro nome; -)
.Altri suggerimenti
tl, dr: Per esprimere più colonne in un ordinato chiavi valore come db Berkley è necessario conoscere composizione tasto . Guardate le mie altre risposte su bsddb per saperne di più.
Ci sono diversi modi per farlo con il tasto ordinato / negozio di valore.
La soluzione più semplice è quella di come valori JSON con una chiave corretta .
Ora probabilmente si desidera indice di costruire su quelle colonne di recuperare i documenti senza dover iterare su tutta la HashMap per trovare l'oggetto giusto. Per questo è possibile utilizzare un secondaryDB che costruirà automaticamente l'indice per voi. Oppure si può costruire l'indice di te stesso.
Se non si vuole affrontare con imballaggio (ed è una buona idea per l'avvio), è possono usufruire di DB.set_bt_compare che vi permetterà di utilizzare cPickle , jSON o msgpack per entrambi i tasti e valori, pur avendo un ordine che rende sens di creare indici e query facendo. Questo è il metodo più lento ma introdurre il modello di composizione tasto .
Per sfruttare appieno ciò che ha ordinato chiave è, si può fare uso di Cursor.set_range(key)
per impostare la posizione del db, all'inizio di una query.
Un altro modello, è chiamato il EAV modello memorizza le tuple che seguono il (entity, attribute, value)
schema e poi si costruiscono vari indice utilizzando permutazione di quel tupla. Ho imparato questo modello studiando datomic.
Per meno ressource database di fame, si andrà alla "statico digitato" via e conservare il più possibile di informazioni comuni nei documenti di tabella e split "metadati" (che sono in realtà RDBMS tabelle) nella propria hashmap.
Per iniziare qui è un database di esempio utilizzando bsddb (ma si potrebbe costruire utilizzando un altro negozio chiave / valore ordinato come wiredtiger o LevelDB) che implementa il pattern EAV. In questa implementazione io di swap EAV per IKV che si traduce in identificatore univoco, Chiave, Valore. Il risultato overal è che si dispone di un database di schema meno documento completamente indicizzato. Penso che sia un buon compromesso tra efficienza e la facilità d'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))