summaryrefslogtreecommitdiff
path: root/src/db.py
diff options
context:
space:
mode:
authorKristofer Hallin <kristofer@sunet.se>2021-09-23 11:56:34 +0200
committerKristofer Hallin <kristofer@sunet.se>2021-09-23 11:56:34 +0200
commit0398e77a809abcaf78c6f7d3e6064a5bee50be23 (patch)
tree04e66c666fee6f40762cc4586bba345557227765 /src/db.py
parent838d36e7cab2f11322d4c3c211407a73ebc712b9 (diff)
Use CouchDB, this might break things.
Diffstat (limited to 'src/db.py')
-rwxr-xr-xsrc/db.py183
1 files changed, 40 insertions, 143 deletions
diff --git a/src/db.py b/src/db.py
index 980505d..0c7e998 100755
--- a/src/db.py
+++ b/src/db.py
@@ -1,10 +1,6 @@
#! /usr/bin/env python3
# A database storing dictionaries, keyed on a timestamp.
-
-# key = 8 octets timestamp | 1 octet version
-# struct.pack('!dB', time.time(), 0)
-
# value = A dict which will be stored as a JSON object encoded in
# UTF-8. Note that dict keys of type integer or float will become
# strings while values will keep their type.
@@ -13,165 +9,66 @@
# value if you're too quick with generating the timestamps, ie
# invoking time.time() several times quickly enough.
-from store import KVStore
-import json
-from pathlib import PurePath
import time
import struct
-import os
+import couchdb
+
-class DictDB(KVStore):
- VERSION = 0
+class DictDB():
+ def __init__(self, database, hostname, username, password):
+ self.server = couchdb.Server(
+ f"http://{username}:{password}@{hostname}:5984/")
- # TODO: implement indexes
- # TODO: implement search(dict key) for indexed fields
+ if database not in self.server:
+ self.server.create(database)
- def __init__(self, name, basedir='.'):
- super().__init__(name, basedir)
+ self.couchdb = self.server[database]
self._ts = time.time()
- self._index = {}
def unique_key(self):
ts = time.time()
while ts == self._ts:
ts = time.time()
- self._ts = ts
- return struct.pack('!dB', ts, DictDB.VERSION)
+ self._ts = round(ts * 1000)
+
+ return self._ts
def index_add(self, path):
- name = PurePath(path).name
- self._index[name] = DBIndex(path)
+ pass
def add(self, data, batch_write=False):
+ key = str(self.unique_key())
+
if type(data) is list:
- ret = []
- if batch_write: # Supposedly makes the update atomic.
- batch = self.batch()
- for e in data:
- ret += [self.add(e)]
- if batch_write:
- self.write(batch)
- return ret
+ for item in data:
+ self.couchdb[key] = item
else:
- key = self.unique_key()
- json_data = json.dumps(data).encode('UTF-8')
- self.put(key, json_data)
- return key
+ self.couchdb[key] = data
+
+ return key
def get(self, key):
- enc = super().get(key) # name collision, specify super class
- ret = json.loads(enc.decode('UTF-8'))
- return ret
+ return self.couchdb[key]
def slice(self, key_from=None, key_to=None):
- ret = []
- for key_raw, val_raw in list(self.range_iter(key_from=key_from, key_to=key_to)):
- (key, ver) = struct.unpack('!dB', key_raw)
- if ver == DictDB.VERSION:
- val = json.loads(val_raw.decode('UTF-8'))
- ret.append((key, val))
- return ret
-
- # Search top level dict for objects with a name matching DICT_NAME
- # and optionally value DICT_VAL.
- def search(self, dict_name, dict_val=None):
- res = []
- for key, obj in self.slice():
- if dict_name in obj:
- if dict_val is None:
- res.append((key, obj))
- elif dict_val == obj[dict_name]:
- res.append((key, obj))
- return res
-
- def timestamp_from_key(key):
- ts, _ = struct.unpack('!dB', key)
- return ts
-
-class DBIndex(KVStore):
- def __init__(self, name, basedir='.'):
- super().__init__(name, basedir)
-
-# def update_or_create(self, key, val):
-# curval = []
-# try:
-# curval = self._db.Get(key)
-# except KeyError:
-# self._db.Put(key, [val])
-# return
-
-# if curval is list:
-# self._db.Put(key, curval + [val])
-# else:
-# self._db.Put(key, json.dumps([curval, val]))
-
- # def index_full(self, name):
- # kv = self._index[name])
- # for key, val in self.range_iter():
- # valdict = json.loads(val)
- # field = valdict.get(name)
- # if field:
- # ix = kv.get(key)
- # if ix:
- # kv.put(ix + [key])
- # else:
- # kv.put([key])
-
-from operator import itemgetter
-def dict_eq(a, b):
- sort_on_key = lambda d: sorted(d.items(), key=itemgetter(0))
- return sort_on_key(a) == sort_on_key(b)
-
-if __name__ == '__main__':
- DBDIR = 'test_db.db'
- # TODO: rm -r DBDIR
- db = DictDB('db', basedir = DBDIR)
- #ix = DBIndex('foo', basedir = DBDIR)
-
- key = db.add({'foo': 'Bar'})
- assert(db.get(key) == {'foo': 'Bar'})
-
- key = db.add({0: 'Foo'}) # NOTE: int keys become strings
- assert(db.get(key) == {'0': 'Foo'})
-
- d2 = {'4711': 'Large number', '7': 'Small number', '0': 'Bar'}
- key = db.add(d2)
- res = db.get(key)
- assert(dict_eq(d2, res))
-
- key = db.add({'an int': 0})
- assert(db.get(key) == {'an int': 0})
-
- key = db.add({'a float': 1.1})
- assert(db.get(key) == {'a float': 1.1})
-
- d5 = { "ip": {"foo": "192.0.2.10" }}
- key = db.add(d5)
- res = db.get(key)
- assert(res == d5)
-
- # TODO: verify slice() too
- for key, val in db.slice():
- print(key, val)
-
- res = db.search('an int')
- assert(dict_eq(res[0][1], {'an int': 0}))
-
- res = db.search('0')
- assert(dict_eq(res[0][1], {'0': 'Foo'}))
- assert(dict_eq(res[1][1], d2))
-
- res = db.search('7', dict_val = 'Small number') # FIXME: verify better -- do we hit only '7' here, f.ex.?
- assert('7' in res[0][1])
-
- res = db.search('7', dict_val = 'not matching')
- assert(not res)
-
- N = 10 * 1000 # 10k takes ~0.2s.
- data = [{str(x): x} for x in range(N)]
- keys = db.add(data, batch_write = False)
- assert(len(keys) == N)
- for k in range(len(keys)):
- assert(db.get(keys[k]) == data[k])
+ pass
+
+ def search(self, **kwargs):
+ data = list()
+ selector = dict()
+
+ if kwargs:
+ selector = {
+ "selector": {
+ }
+ }
+
+ for key in kwargs:
+ if kwargs[key].isnumeric():
+ kwargs[key] = int(kwargs[key])
+ selector['selector'][key] = {'$eq': kwargs[key]}
+ for doc in self.couchdb.find(selector):
+ data.append(doc)
+ return data