Emacs Db - Key/Values stores for Emacs
An EmacsLisp interface to key/value stores (Mongo, Postgresql Hstore, etc..) with a simple default implementation based on EmacsLisp Hashtables.
The interface
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:
db-make reference
Make a DB based on the reference.
db-get key db
Get the value from the db with the key.
db-put key value db
Put a new value into the db with the specified key.
Return the value as it has been put into the db.
db-map func db &optional query filter
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.
db-query db query
Do query on db and return the result.
This is db-map with an identity function.
Query language
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.
Hashtable implementation
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!"))Testing
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.