2010-03-08 32 views

回答

4

你必須選擇一個「列」爲重點(必須是唯一的,我想這將是你的情況「用戶名」) - 的唯一途徑搜索將永遠不可能發生。其他列可以用任何你喜歡的方式作爲該鍵的單個字符串值,從酸洗到簡單連接一個保證在任何列中都不會出現的字符,比如'\ 0' 「可讀的文本字符串」。

如果您需要能夠通過不同的鍵進行搜索,則需要將其他輔助和單獨的bsddb數據庫設置爲主表中的「索引」 - 這有很多工作要做,並且有大量關於學科。 (或者,你可以轉向更高層次的抽象技術,比如sqlite,它可以代表你整潔地處理索引;-)。

1

tl,dr:要在berkley db這樣的有序鍵值存儲區中表示多個列,您需要了解關鍵構成。查看關於bsddb的其他答案以瞭解更多信息。

有幾種方法可以做到,使用命令鍵/值存儲。

最簡單的解決方法是store documents as json values with a correct key

現在你可能要建立在這些列檢索文件的索引,而不必遍歷所有的HashMap來找到正確的對象。爲此,您可以使用secondaryDB,它會自動爲您建立索引。或者你可以自己建立索引。

如果你不想處理key packing(這是一個好主意,開始了),你可以利用DB.set_bt_compare,這將允許同時還具有您使用的cPickle,JSON或msgpack兩個鍵和值這個命令可以用來創建索引並進行查詢。這是較慢的方法,但引入關鍵組合的模式。

要充分利用什麼下令關鍵是,你可以利用Cursor.set_range(key)設置數據庫的位置在查詢的開始。

另一種模式,被稱爲EAV模式商店後面的方案(entity, attribute, value)元組,然後你通過使用元組的排列訂做各種指標。我學習了這種模式研究數據組。對於較少資源的飢餓數據庫,您將採用「靜態類型」方式,並儘可能多地將常見信息存儲在「元數據」表中,並將文檔(實際上是RDBMS表)分割成它們自己的散列表。

爲了讓您在這裏開始使用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))