سؤال

لنفترض أن لدي جدولًا بسيطًا يحتوي على اسم المستخدم ، 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))
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top