cl-ohm
2018-02-28
An object-hash mapping for Redis in Common Lisp
Upstream URL
Author
License
file:http://quickdocs.org/badge/cl-ohm.svg file:https://img.shields.io/badge/License-MIT-yellow.svg file:https://travis-ci.org/rudolfochrist/cl-ohm.svg?branch=master file:https://coveralls.io/repos/github/rudolfochrist/cl-ohm/badge.svg?branch=master
This is an implementation of Ohm in Common Lisp with some inspiration from Crane and datafly.
Ohm is an object-hash mapping for Redis.
1Usage
1.1Starting and connecting Redis
First of all, Redis must be up and running. You can start Redis with the command line
$ redis-server
Then load CL-OHM
:
(ql:quickload :cl-ohm)
Without configuration Redis runs on localhost:6379
. If you're using a different host or port you have to
configure CL-OHM
. For example, if Redis is running on 198.162.55.12
on port 12455
than you must setup
CL-OHM like this:
(ohm:setup-redis-connection :host #(198 162 55 12) :port 12455)
1.2Mapping objects to Redis
Use ohm:define-ohm-model
to specify your models.
(ohm:define-ohm-model person ()
:attributes ((first-name :indexp t)
(last-name :indexp t)
(email :uniquep t)))
You can create new persisted objects with CREATE
:
(ohm:create 'person :first-name "John" :last-name "McCarthy")
Attributes are setfable like ordinary objects slots (Note: if you don't provide readers or writers for an attribute, an accessor will be created) but has to be explicitly saved to be persisted.
(ohm:create 'person :first-name "Bill")
(setf (first-name *) "William")
(ohm:save **)
1.3Loading objects from the data store
When you know an object's ID then you can load it with filter-id
(ohm:filter-id 'person "5")
;;; or
(ohm:filter-id 'person 5)
1.3.1Indexes
For each attribute marked with :INDEXP
and index gets created. With this index it is possible to load
objects by their values.
(ohm:filter 'person :first-name "Bill")
This load all objects with first-name=Bill
. Indexed attributes can be combined in FILTER
.
(ohm:filter 'person :first-name "Bill" :last-name "Miller")
If you omit any attribute specifiers from FILTER
than all objects for the given type are retrieved.
(ohm:filter 'person)
1.4Unique values
Each attribute marked as :UNIQUEP
must be unique for all instances of a given model. Considering the
person
model from above this means two instances cannot have the same email
. :UNIQUEP
also creates an
index, query-able with FILTER-WITH
.
(ohm:filter-with 'person :email "e@example.org")
This load the person
object with email=e@example.org
1.5Counters
Counters let you count atomically.
(ohm:define-ohm-model candidate (person)
:counters (votes))
(let ((candidate (create 'candidate :first-name "Bill")))
(ohm:incr (votes candidate))
(ohm:incr (votes candidate))
(ohm:counter (votes candidate)) ;=> 2
(ohm:decr (votes candidate) 2)
(ohm:counter (votes candidate))) ;=> 0
1.6Sets and Lists
Each model can define sets or lists as attributes. Sets and lists can hold other persisted objects defined by
DEFINE-OHM-MODEL
. Therefore you most provide the set's or list's element-type.
(ohm:define-ohm-model tag ()
:attributes ((name :indexp t)))
(ohm:define-ohm-model post ()
:lists ((authors :element-type person))
:sets ((tags :element-type tag)))
CL-OHM
persisted objects are internally stored in sets.
(ohm:create 'person :first-name "Donald" :last-name "Duck")
(ohm:filter 'person) ;=> #<CL-OHM::OHM-SET {1009FAB643}>
This lets you combine the FITLER
function with set operations.
1.6.1Set operations
Creating some test data:
(ohm:create 'person :first-name "Donald" :last-name "Duck")
(ohm:create 'person :first-name "Daisy" :last-name "Duck")
(ohm:create 'person :first-name "Gladstone" :last-name "Gander")
Creating the union of persons named Duck and persons named Gander:
(ohm:elements (ohm:union (ohm:filter 'person :last-name "Duck")
(ohm:filter 'person :last-name "Gander")))
(#<PERSON CL-OHM::ID="1" FIRST-NAME="Donald" LAST-NAME="Duck" {1008745153}> #<PERSON CL-OHM::ID="2" FIRST-NAME="Daisy" LAST-NAME="Duck" {1008745333}> #<PERSON CL-OHM::ID="3" FIRST-NAME="Gladstone" LAST-NAME="Gander" {1008745513}>)
Use EXCEPT
to exclude objects with specific properties. Say, exclude persons named Gander from all persons:
(ohm:elements (ohm:except (ohm:filter 'person) ; all persons
(ohm:filter 'person :last-name "Gander")))
(#<PERSON CL-OHM::ID="1" FIRST-NAME="Donald" LAST-NAME="Duck" {1008AA3963}> #<PERSON CL-OHM::ID="2" FIRST-NAME="Daisy" LAST-NAME="Duck" {1008AA3B43}>)
Use COMBINE
to limit the resulting set. Say, all persons with last name Duck and first name Donald:
(ohm:elements (ohm:combine (ohm:filter 'person :last-name "Duck")
(ohm:filter 'person :first-name "Donald")))
(#<PERSON CL-OHM::ID="1" FIRST-NAME="Donald" LAST-NAME="Duck" {1008C6B8F3}>)
1.7Persistence Strategy
Sets, lists and counters are stored implicitly after their mutation. If you change normal attributes (with
SETF
) then those objects have to be persisted with SAVE
.
2API Documentation
See CL-OHM HTML Documentation.
3Running the tests
CL-OHM uses FiveAM for testing. Please installed it with
(ql:quickload :fiveam)
Then you can run the test through ASDF:
(asdf:test-system :cl-ohm)