【问题标题】:Expressing multiple columns in berkeley db in python?在 python 的 berkeley db 中表达多列?
【发布时间】:2010-03-08 06:25:34
【问题描述】:

假设我有一个包含用户名、名字、姓氏的简单表。

如何在 berkeley Db 中表达这一点?

我目前使用 bsddb 作为接口。

干杯。

【问题讨论】:

标签: python berkeley-db bsddb okvs


【解决方案1】:

您必须选择一个“列”作为键(必须是唯一的;我想在您的情况下这将是“用户名”)——搜索可能发生的唯一方式。可以通过任何您喜欢的方式将其他列设置为该键的单个字符串值,从酸洗到使用保证不会出现在任何列中的字符的简单连接,例如 `\0' 用于多种“可读的文本字符串”。

如果您需要能够通过不同的键进行搜索,则需要在主表中设置其他、补充和单独的 bsddb 数据库作为“索引”——这需要大量工作,并且有很多关于该主题的文献. (或者,您可以使用更高抽象的技术,例如 sqlite,它可以代表您巧妙地处理索引;-)。

【讨论】:

    【解决方案2】:

    tl,dr:要在像 berkley db 这样的有序键值存储中表达多列,您需要了解 键组合。查看我关于bsddb 的其他答案以了解更多信息。

    有几种方法可以使用有序的键/值存储来做到这一点。

    最简单的解决方案是store documents as json values with a correct key

    现在您可能希望在这些列上建立索引来检索文档,而不必遍历所有哈希图以找到正确的对象。为此,您可以使用 secondaryDB 自动为您构建索引。或者您可以自己构建索引。

    如果您不想处理key packing(这是一个启动的好主意),您可以利用DB.set_bt_compare,这将允许您对键和值使用 cpickle、json 或 msgpack同时仍然具有创建索引和执行查询的命令。这是较慢的方法,但引入了键组合的模式

    为了充分利用有序键是什么,您可以使用Cursor.set_range(key) 在查询开始时设置数据库的位置。

    另一种模式,称为 EAV 模式 存储遵循方案 (entity, attribute, value) 的元组,然后您通过使用该元组的排列来构建各种索引。我在学习 datomic 时学会了这种模式。

    对于资源匮乏的数据库,您将采用“静态类型”的方式,将尽可能多的公共信息存储在“元数据”表中,并将文档(实际上是 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))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多