emacs-db
emacs-db copied to clipboard
very simple database for emacslisp, can also wrap other databases.
= 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.