You have to be a little careful because in both BDB and SQL backends range queries are dependant on the ordering that the database uses for secondary indices. For instance, there is a C function that handles the ordering of elements in an index defined on a slot. The idea of a range query is to gain efficiency by only looking at a subset of objects.
Now you can approximate this by mapping your non-integer objects into an integer space according to your own ordering. This is provided for in the (add-class-derived-index ...) which takes a function that is called by the backend to determine the value. This can be an arbitrary lisp function. Unless I'm mis-remembering, you can use the name of this derived index as a 'slotname' when you call (get-instances-by-range ...). However it may work for strings today because ordering is based on the order of binary bytes in the serialized representation of lisp data. I'll see if I can document all this better in the next release; it's helpful getting all this input to see what is confusing to new users and what features they want! Ian Alain Picard wrote: > Hello to all Elephant developers, and many words of thanks > for what appears to be a useful and well thought out library! > > I've recently started a project which will (I hope) make some > good use of Elephant. I'm still wrapping my head around things, > so go easy on me. As a show of good will, here's my first attempt > at adding something to elephant. > > Suppose you have a class like this: > > (defclass test-user () > ((name :reader name :initarg :name > :type string :index t) > (timestamp :reader timestamp > :initform (get-universal-time) > :type integer > :index t)) > (:metaclass persistent-metaclass)) > > (defun make-some-users () > (let ((n 0) > (*auto-commit* t)) > (dotimes (i 1000) > (make-instance 'test-user :name (format nil "User name ~D" (incf n)))) > (get-instances-by-range 'test-user 'name "User name 10" "User name 20"))) > > (make-some-users) > > If you try to do something like > > (length (get-instances-by-range 'test-user 'timestamp 0 999999999999)) > ==> 1000 > > It succeeds. > But if you try on the name, > > (get-instances-by-range 'test-user 'name "User name 10" "User name 11") > ==> > Argument X is not a REAL: "User name 10" > [Condition of type SIMPLE-TYPE-ERROR] > > Because the get-instances-by-range function (needlessly) assumes > that the objects being returned can be compared with numeric > equality. > > This little snippet fixes this problem: > (change from the 0.6 distribution) > =============cut================================================ > (defun find-slot-type (class idx-name) > (flet ((candidate-slot-p (slot) > (and (eq (type-of slot) 'persistent-effective-slot-definition) > (slot-value slot 'indexed) > (eq (slot-definition-name slot) idx-name)))) > (find-if #'candidate-slot-p (class-slots class)))) > > (defun find-index-comparison-function (class index) > (let ((type (sb-pcl:slot-definition-type (find-indexed-slot class index)))) > (cond ((subtypep type 'number) > #'<=) > ((subtypep type 'string) > #'string<=) > (t > ;; We'll fall back to numerical, though it's not clear to > ;; me that this is sensible. Maybe should just signal an error, > ;; and force users to declare :type on all indexed slots? > #'<=)))) > > (defmethod get-instances-by-range ((class persistent-metaclass) idx-name > start end) > (let ((comparison (find-index-comparison-function class idx-name))) > (with-inverted-cursor (cur class idx-name) > (labels ((next-range (instances) > (multiple-value-bind (exists? skey val pkey) > (cursor-pnext-nodup cur) > (declare (ignore pkey)) > (if (and exists? (funcall comparison skey end)) > (next-in-range skey (cons val instances)) > (nreverse instances)))) > (next-in-range (key instances) > (multiple-value-bind (exists? skey val pkey) (cursor-pnext-dup > cur) > (declare (ignore pkey skey)) > (if exists? > (next-in-range key (cons val instances)) > (progn > (cursor-pset-range cur key) > (next-range instances)))))) > (multiple-value-bind (exists? skey val pkey) (cursor-pset-range cur > start) > (declare (ignore pkey)) > (if (and exists? (funcall comparison skey end)) > (next-in-range skey (cons val nil)) > nil)))))) > =============cut================================================ > > Note that unfortunately FIND-INDEX-COMPARISON-FUNCTION makes > use of SB-PCL:SLOT-DEFINITION-TYPE; I have not conditionalized > this for other lisps, nor do I really know what forms are appropriate > in any other lisps. > > Obviously, it is possible to extend this scheme to let users register > their own comparison functions for more complex types; I'll let you > judge if this is worth the effort. > > Hoping this helps someone, somewhere. > > If this was already covered in some other way, then consider this > message a bug report on the documentation instead. :-) > > Alain Picard > > > _______________________________________________ elephant-devel site list elephant-devel@common-lisp.net http://common-lisp.net/mailman/listinfo/elephant-devel