An EmacsLisp interface to key/value stores (Mongo, Postgresql Hstore, etc..) with a simple default implementation based on EmacsLisp Hashtables.
The idea behind this is to make an interface for interacting with simple key/value database stores that is portable across all such stores. So you can make code once but swap out the database with relative ease.
The interface includes the following functions:
Make a DB based on the reference.
Get the value from the db with the key.
Put a new value into the db with the specified key.
Return the value as it has been put into the db.
Call func for every record in db optionally query filter.
query, if specified, should be a list of query terms.
func should take 2 arguments:
key db-value
where the DB-VALUE is whatever the db has attached to the specified KEY.
This returns an alist of the KEY and the value the function returned. If filter is t then only pairs with a value are returned.
Do query on db and return the result.
This is db-map with an identity function.
db uses the query language provided by the kv library, which is implemented as a mapping function test on ever value by the persistent hashtable implementation.
The language should be translatable to just about any database query language (Mongo, SQL, etc...).
There are only 3 constructs currently, |, & and =.
An expression could be:
(= field-name value)
To select any record where field-name has the value
(|(= field-name value)(= other-field other-value))
To select any record where field-name has the value or other-field has the value other-value
(&(= field-name value)(= other-field other-value))
To select any record where field-name has the value and other-field has the value other-value.
Logical combinations of | and & are also possible.
db comes with a simple implementation which can store any EmacsLisp object (though alists would most usually be preferred).
To make a db with the hash implementation:
(db-make `(db-hash :filename ,(format "/var/cache/some-file")))
Obviously, most often you will assign the db to a global variable.
(defvar my-db (db-make `(db-hash :filename ,(format "/var/cache/some-file")))) (db-put "001" '(("a" . 10)("b" . 20)) my-db) (db-put "002" '(("a" . 17)("b" . "hello")("xyz" . "well!")) my-db) (db-get "002" my-db)
results in:
(("a" . 17)("b" . "hello")("xyz" . "well!"))
Hash Db's are tied to filenames so to test them you often have to manage that persistence:
(unwind-protect (let ((mydb (db-make `(db-hash :filename "/tmp/mydb"))) (json (with-temp-buffer (insert-file-contents "~/work/elmarmalade/users-mongo.json") (goto-char (point-min)) (json-read)))) (--each json (db-put (car it) (cdr it) mydb)) (list (db-get 'triss mydb) (db-get 'nicferrier mydb))) (delete-file "/tmp/mydb.elc"))
Note the deleting of the elc file. That's how the hash db is stored.
Alternately one could use fakir-file (see the fakir package) to mock the file system. But that's harder than just creating and throwing away the file.