class DocDB(object): """Creates instances of DocDB. Each instance will have a db connection. Extracted from web2py docs: --------------------------- Here are examples of connection strings for specific types of supported back-end databases (in all cases, we assume the database is running from localhost on its default port and is named "test"): SQLite sqlite://storage.db MySQL mysql://username:password@localhost/test PostgreSQL postgres://username:password@localhost/test MSSQL mssql://username:password@localhost/test FireBird firebird://username:password@localhost/test Oracle oracle://username/password@test DB2 db2://username:password@test Ingres ingres://username:password@localhost/test Informix informix://username:password@test Google App Engine/SQL google:sql Google App Engine/NoSQL google:datastore Notice that in SQLite the database consists of a single file. If it does not exist, it is created. This file is locked every time it is accessed. In the case of MySQL, PostgreSQL, MSSQL, FireBird, Oracle, DB2, Ingres and Informix the database "test" MUST exist. """ def __init__(self, conn='sqlite://temp.db', pool_size=0, migrate=True): """ Generates a connection with the given DB. @connection: system path to the sqlite file to use or DB connection string. If None given, a default temp.db file will be created. """ if '://' not in conn: print """Connection string needed!\n \ Some examples:\n \ SQLite sqlite://storage.db MySQL mysql://username:password@localhost/test \ PostgreSQL postgres://username:password@localhost/test """ sys.exit(2) self._conn = conn self._db = DAL(conn, folder=PATH, pool_size=pool_size) self._db.define_table('documents', Field('key'), Field('data', 'text'), Field('valid', 'boolean'), migrate = migrate) if not self._db(self._db.documents).count(): try: self._db.executesql('CREATE INDEX keyx ON documents (key)') #CREATE INDEX IF NOT EXISTS except Exception: self._db.rollback() def get(self, key): """ Searches and returns the doc for a given key. """ db = self._db doc = db((db.documents.key==key) & (db.documents.valid==True)).select(db.documents.data).first() if doc: return json.loads(doc['data']) return None def set(self, key, doc): """ Inserts a document (doc) into the DB. @doc: can be of any python data structure (string, number, dict, list, ... """ db = self._db data = json.dumps(doc) db((db.documents.key==key) & (db.documents.valid==True)).update(valid=False) db.documents.insert(key=key, data=data, valid=True) db.commit() return True def mset(self, docs): """ Inserts a set of documents into the DB. Example: >>> l = [('key1', {'key': 'value'}), ('key2', {'key': 'value'}), ('key3', {'key': 'value'})] >>> db.mset(*l) """ db = self._db counter = 0 for doc in docs: key, data = doc data = json.dumps(data) db((db.documents.key==key) & (db.documents.valid==True)).update(valid=False) db.documents.insert(key=key, data=data, valid=True) counter += 1 if counter > 1000: db.commit() counter = 0 db.commit() return True def versions(self, key): """ Lists all the documents related to a key. """ db = self._db results = [] for doc in db(db.documents.key == key).select(): id, data, valid = doc['id'], json.loads(doc['data']), doc['valid'] results.append((id, data, valid)) return results def revert(self, key, version): """ Reverts to a previous version of the document. """ db = self._db vers = self.versions(key) for doc in vers: id, data, valid = doc if id == version: db((db.documents.key==key) & (db.documents.valid==True)).update(valid=False) db(db.documents.id==id).update(valid=True) db.commit() return True return False def info(self): """ Returns a dict with info about the current DB (including db filesize, number of keys, etc.). """ dbsize = -1 db = self._db if "postgres" in self._conn: dbsize = db.executesql("SELECT pg_size_pretty(pg_database_size('%s'));" % self.dbname)[0][0] num_keys = db(db.documents.valid==True).count() return dict(keys=num_keys, dbsize=dbsize) def keys(self): """ Returns a list of ALL the keys in the current DB. """ db = self._db return [doc['key'] for doc in db(db.documents.valid==True).select(db.documents.key)] def flushall(self): """ Deletes ALL the content from the DB. TODO: Use truncate. """ self._db.documents.truncate() self._db.commit() #self.__init__() return True def compact(self, key=None): """ Deletes ALL the versions for a given document or the entire DB. """ db = self._db if key: db((db.documents.key==key) & (db.documents.valid==False)).delete() else: db(db.documents.valid==False).delete() db.commit() return True def __contains__(self, item): if self.get(item): return True return False def __getitem__(self, item): return self.get(item) def __setitem__(self, key, doc): return self.set(key, doc) def __delitem__(self, key): self._db.execute('UPDATE document SET valid == 0 WHERE key == ?', (key,)) self._db.commit() return True