;; elephant-utils.lisp --- ;; Copyright (C) 2008 Clinton Ebadi ;; Author: Clinton Ebadi ;; This program is free software: you can redistribute it and/or modify ;; it under the terms of the GNU Lesser General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU Lesser General Public License ;; along with this program. If not, see . (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 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))