التعبير عن أعمدة متعددة في بيركلي دي بي في بيثون؟
-
25-09-2019 - |
سؤال
لنفترض أن لدي جدولًا بسيطًا يحتوي على اسم المستخدم ، FirstName ، LastName.
كيف أعبر عن هذا في بيركلي دي بي؟
أنا أستخدم حاليًا BSDDB كواجهة.
هتافات.
المحلول
يجب عليك اختيار "عمود" واحد كمفتاح (يجب أن يكون فريدًا ؛ أتصور أنه سيكون "اسم المستخدم" في قضيتك) - الطريقة الوحيدة التي قد تحدث عمليات البحث. يمكن جعل الأعمدة الأخرى هي قيمة سلسلة واحدة لهذا المفتاح بأي طريقة تريدها ، من التخليل إلى الانضمام البسيط مع شخصية مضمونة لعدم حدوثها في أي من الأعمدة ، مثل " 0" للعديد من أنواع أنواعها "سلاسل النص القابلة للقراءة".
إذا كنت بحاجة إلى أن تكون قادرًا على البحث بمفاتيح مختلفة ، فستحتاج إلى قواعد بيانات BSDDB الأخرى والمكملة والفصلة التي تم إعدادها كـ "مؤشرات" في الجدول الرئيسي الخاص بك - إنه الكثير من العمل ، وهناك الكثير من الأدبيات حول هذا الموضوع. (بدلاً من ذلك ، أنت تنتقل إلى تقنية تجسيد أعلى ، مثل SQLite ، والتي تتعامل مع الفهرسة نيابة عنك ؛-).
نصائح أخرى
TL ، DR: للتعبير عن أعمدة متعددة في متجر قيمة مفاتيح مطلوب مثل Berkley DB تحتاج إلى معرفة التكوين الرئيسي. ابحث عن إجاباتي الأخرى عن BSDDB لتعلم المزيد.
هناك عدة طرق للقيام بذلك باستخدام متجر المفاتيح/القيمة المطلوبة.
أبسط حل هو تخزين المستندات كقيم JSON مع مفتاح صحيح.
ربما ترغب الآن في إنشاء فهرس عبر تلك الأعمدة لاسترداد المستندات دون الحاجة إلى التكرار على كل hashmap للعثور على الكائن الصحيح. لذلك يمكنك استخدام أ Secondarydb سيؤدي ذلك إلى بناء الفهرس تلقائيًا. أو يمكنك بناء الفهرس بنفسك.
إذا كنت لا تريد التعامل معها التعبئة المفتاح (وهي فكرة جيدة للبدء) ، يمكنك الاستفادة منها db.set_bt_compare مما سيتيح هذه طريقة أبطأ ولكن تقديم نمط التكوين الرئيسي.
للاستفادة الكاملة من المفتاح المطلوب ، يمكنك الاستفادة Cursor.set_range(key)
لتعيين موضع DB في بداية الاستعلام.
نمط آخر ، يسمى نمط eav تخزن tuples التي تتبع المخطط (entity, attribute, value)
ثم تقوم ببناء فهرس مختلف باستخدام التقليب لتلك tuple. لقد تعلمت هذا النمط الذي يدرس datomic.
بالنسبة لقاعدة بيانات Ressource Hungry الأقل ، ستذهب إلى الطريقة "المكتوبة الثابتة" وتخزين أكبر قدر ممكن من المعلومات المشتركة في جدول "البيانات الوصفية" والمستندات المنقسمة (التي هي بالفعل جداول RDBMS) في hashmap الخاصة بهم.
للبدء في أن تبدأ هنا قاعدة بيانات مثال باستخدام BSDDB (ولكن يمكنك بنائه باستخدام مخزن مفتاح/قيمة مرتبة أخرى مثل Wiredtiger أو LevelDB) الذي ينفذ نمط EAV. في هذا التنفيذ أقوم بتبديل EAV لـ IKV والذي يترجم إلى معرف فريد ، مفتاح ، قيمة. والنتيجة المفرطة هي أن لديك قاعدة بيانات مستند مخطط أقل فهرسة. أعتقد أنه حل وسط جيد بين الكفاءة وسهولة الاستخدام.
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))