1

ユーザー名、名、姓を含む単純なテーブルがあるとします。

これを berkeley Db で表現するにはどうすればよいですか?

現在、インターフェースとして bsddb を使用しています。

乾杯。

4

2 に答える 2

4

キーとして1つの「列」を選択する必要があります(一意である必要があります。あなたの場合は「ユーザー名」になると思います)-検索が発生する唯一の方法です。他の列は、好きな方法でそのキーの単一の文字列値にすることができます。酸洗から、どの列にも決して出現しないことが保証されている文字との単純な結合まで、多くの種類の「\0」などです。 「読み取り可能なテキスト文字列」。

異なるキーで検索できるようにする必要がある場合は、メインテーブルに「インデックス」として設定された他の補助的な個別の bsddb データベースが必要になります。これには多くの作業が必要であり、この件に関する文献はたくさんあります。(あるいは、sqlite などの高度な抽象化テクノロジに移行すると、インデックス作成が適切に処理されます;-)。

于 2010-03-08T06:39:20.693 に答える
1

tl,dr: berkley db のような順序付けられたキー値ストアで複数の列を表現するには、キー構成について学ぶ必要があります。詳細については、 bsddbに関する他の回答を参照してください。

順序付けられたキー/値ストアを使用してこれを行うには、いくつかの方法があります。

最も簡単な解決策は、ドキュメントを正しいキーを持つ json 値として保存することです。

ここで、正しいオブジェクトを見つけるためにすべてのハッシュマップを反復処理することなく、これらの列にインデックスを作成してドキュメントを取得したいと思うでしょう。そのために、自動的にインデックスを作成するsecondaryDBを使用できます。または、インデックスを自分で作成することもできます。

キー パッキングを処理したくない場合(そして、起動するのは良い考えです)、キーと値の両方に cpickle、json、または msgpack を使用できるようにする DB.set_bt_compare を利用できますインデックスを作成してクエリを実行するのに意味のある順序。これは遅い方法ですが、キー合成のパターンを紹介します。

順序付けられたキーを十分に活用するにCursor.set_range(key)は、クエリの開始時にデータベースの位置を設定するために使用できます。

別のパターンはEAV パターンと呼ばれ、スキームに従うタプルを格納し(entity, attribute, value)、そのタプルの順列を使用してさまざまなインデックスを構築します。私はこのパターンをdatomicで学びました。

リソースをあまり消費しないデータベースの場合は、「静的型付け」の方法を使用して、「メタデータ」テーブルに可能な限り多くの共通情報を格納し、ドキュメント (実際には RDBMS テーブル) を独自のハッシュマップに分割します。

ここで開始するのは、EAV パターンを実装する bsddb を使用したデータベースの例です (ただし、wiredtiger や leveldb などの別の順序付きキー/値ストアを使用して構築することもできます)。この実装では、一意の識別子、キー、値に変換される IKV の EAV を交換します。全体的な結果として、完全にインデックス化されたスキーマの少ないドキュメント データベースが作成されます。効率と使いやすさのバランスが取れていると思います。

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))
于 2015-08-21T19:58:28.620 に答える