[Initil import of elephant code into lame-web clinton@unknownlamer.org**20080221183642] { adddir ./src/elephant addfile ./src/elephant/elephant-packages.lisp hunk ./src/elephant/elephant-packages.lisp 1 - +(defpackage :org.unknownlamer.golgonooza.elephant + (:use :common-lisp :elephant :org.unknownlamer.golgonooza.web) + (:import-from :arnesi :if-bind) + (:export + :get-instances-limit + :count-instances-by + + :elephant-query-view-mixin) + (:nicknames :golgonooza-db)) addfile ./src/elephant/elephant-query-view.lisp hunk ./src/elephant/elephant-query-view.lisp 1 +(in-package :org.unknownlamer.golgonooza.elephant) + +(ucw:defcomponent elephant-query-view-mixin (query-view) + ((index-class :initarg :index-class :accessor index-class) + (index-slot :initarg :index-slot :accessor index-slot))) + +(defmethod query-view-get-instances ((query-view elephant-query-view-mixin) + lower upper + &key limit skip from-end exclusive) + (with-slots (index-class index-slot) query-view + (get-instances-limit (index-class query-view) (index-slot query-view) + lower upper + :limit limit :skip skip + :from-end from-end :exclusive exclusive))) + +(defmethod query-view-count-instances ((view elephant-query-view-mixin) min max) + (count-instances-by (index-class view) (index-slot view) min max)) addfile ./src/elephant/elephant-utils.lisp hunk ./src/elephant/elephant-utils.lisp 1 - +(in-package :org.unknownlamer.golgonooza.elephant) + +;;; Basic Query Functions + +(define-condition results-limit-reached (error) + ((count :initarg :count :reader limit-count) + (result :initarg :result :reader limit-result))) + +(defgeneric get-instances-limit (class slot-name start end + &key limit skip from-end exclusive) + (:documentation "Fetch items as in + elephant:get-instances-by-range. class, slot-name, start, end, + and :from-end all behave as per the documentation for that + method. In addition extra keyword parameters are supported. + + LIMIT integer - limit total results to LIMIT + SKIP integer - skip the first SKIP results + EXCLUSIVE (or :lower :upper t) - make either start, end, or both + exclusive limits")) + +(defmethod get-instances-limit ((class symbol) slot-name start end + &key limit from-end (skip 0) exclusive) + (get-instances-limit (find-class class) slot-name start end + :limit limit :from-end from-end + :skip skip :exclusive exclusive)) + +(defmethod get-instances-limit ((class persistent-metaclass) slot-name start end + &key limit from-end (skip 0) exclusive) + (let ((results (list)) + (lower-exclusive (or (eq exclusive :lower) + (eq exclusive t))) + (upper-exclusive (or (eq exclusive :upper) + (eq exclusive t))) + (skip (if skip skip 0))) + (labels + ((make-index-mapper (fun &optional result-fun) + (let ((curr 0) + (skip skip) + (skip-total skip) + (limit (or limit -1)) ; nil -> no limit + (results (list))) + (labels ((store-result (result) + (incf curr) + (if result-fun + (funcall result-fun result) + (push result results))) + (store-or-skip (result) + (cond ((> skip 0) + (decf skip) nil) + (t (store-result result))))) + (lambda (&rest args) + (cond + ((= curr limit) + (error 'results-limit-reached + :count curr :result (nreverse results))) + ((and (= curr 0) (= skip skip-total)) + (if-bind lower-limit + (or (and (not from-end) lower-exclusive start) + (and from-end upper-exclusive end)) + (let ((result (apply fun args))) + (if (not (equalp (slot-value result slot-name) + lower-limit)) + (store-or-skip result))) + (store-or-skip (apply fun args)))) + ((> skip 0) (decf skip) nil) + (t + (let ((result (apply fun args))) + (cond ((or (and from-end lower-exclusive + (equalp (slot-value result slot-name) + start)) + (and (not from-end) upper-exclusive + (equalp (slot-value result slot-name) + end))) + nil) + (t + (car + (store-result result))))))))))) + (result-pusher (result) (push result results))) + (nreverse + (handler-case + (progn + (map-inverted-index (make-index-mapper + #'elephant::identity2 + #'result-pusher) + class + slot-name + :start start :end end + :collect nil :from-end from-end) + results) + (results-limit-reached () results)))))) + +(defun count-instances-by (class-name slot-name start end &rest keys) + (let ((count 0)) + (apply #'elephant:map-inverted-index (lambda (k v) + (declare (ignore k v)) + (incf count)) + class-name slot-name + :start start :end end + :collect nil + keys) + count)) }