From c122fed06e056e8b6388ef4955e0d3bc30ed7c94 Mon Sep 17 00:00:00 2001 From: Alexis Gallagher Date: Thu, 24 Mar 2011 12:47:55 +0000 Subject: [PATCH] Add ability to do follow-up queries of revisions. This is added through the exported function GET-REVISIONS-RESULT, which returns a QUERY-RESULT object. A QUERY-RESULT object accumulates results from a query, such as GET-REVISIONS, that may require follow up queries to retrieve more results. The methods HAS-MORE-RESULTS-P and GET-MORE-RESULTS-P can be used to continue the query, and RESULTS returns the results. Results are now stored as a list of alists. I probably will update this to store results as vectors. QUERY-RESULT and friends have been written generically so they can be used to add similar behaviour to other queries besides GET-REVISIONS. The non-exported functions GET-REVISIONS-AND-CLOSURE does most of the magic for follow-up queries. --- src/packages.lisp | 6 +++++ src/query.lisp | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/src/packages.lisp b/src/packages.lisp index 5ff5d6d..9742942 100644 --- a/src/packages.lisp +++ b/src/packages.lisp @@ -17,6 +17,12 @@ #:get-action-tokens #:recent-changes #:user-contribs + #:get-revisions-result + ;; query/query-result + #:query-result + #:has-more-results-p + #:get-more-results + #:results ;; EDIT #:set-page-content #:append-text-to-page diff --git a/src/query.lisp b/src/query.lisp index 5324960..df56394 100644 --- a/src/query.lisp +++ b/src/query.lisp @@ -374,8 +374,81 @@ Parameters: ") - - +;;;; query-result and friends +;; a query-result encapsulates the response to a query, and allows +;; for repeated follow-up queries + +(define-modify-macro + appendf (&rest lists) append + "Modify-macro for APPEND. Appends LISTS to the place designated by the first argument.") + +(defclass query-result () + ((results :accessor results :initarg :results :initform '()) ; a sequence of revisions retrieved so far + (closure :initarg :closure :initform nil)) + (:documentation "Accumulating result to a get-revisions-result query. + + Use has-more-results-p to check if there are more results available. + Use get-more-results to get them through one or more queries.")) + +(defmethod has-more-results-p ((qr query-result)) + "Returns nil, or the closure used for a single follow-up query" + (slot-value qr 'closure)) + +(defmethod get-more-results-once ((qr query-result)) + "Fetches more results with one follow-up query. + + Updates the query-result object with these new results. + Returns the object, and the number of new items fetched." + (multiple-value-bind (newrevs c-token new-closure) + (funcall (slot-value qr 'closure)) + (declare (ignore c-token)) + (appendf (slot-value qr 'results) newrevs) + (setf (slot-value qr 'closure) new-closure) + (values qr (length newrevs)))) + +(defmethod get-more-results ((qr query-result) &key (at-least 0) (pause 3)) + "Fetches AT-LEAST more results, re-querying every PAUSE seconds if necessary. + + If AT-LEAST is nil, repeats until it gets all results. + Updates the query-result object with these new results. + Returns the object, and the number of new items fetched." + (loop with fetched = 0 + while (and (has-more-results-p qr) + (if (numberp at-least) (< fetched at-least) 't)) + do (incf fetched (second (multiple-value-list (get-more-results-once qr)))) + do (sleep pause) + finally (return (values qr fetched)))) + +;;;; get-revisions-result and friends +;; query functions for doing get-revisions queries that +;; return their results as query-result objects + +(defun get-revisions-and-closure (&rest args) + "Like get-revisions, but also returns a closure for a follow-up query. + + This closure can be called outside of a with-mediawiki form. + + Example: + (multiple-value-list (with-mediawiki (\"http://en.wikipedia.org/w\") + (get-revisions-and-closure \"Pigment\" :rvlimit 3))) + (multiple-value-list (funcall (elt * 2))) + (multiple-value-list (funcall (elt * 2))) + etc.." + (let ((mediawiki *mediawiki*)) ; capture *mediawiki* into lexical scope + (multiple-value-bind (revs c-token) (apply #'get-revisions args) + (values revs (when c-token c-token) + (when c-token + (setf (getf (cdr args) :rvstartid) c-token) ;set next rvstartid + (lambda () + (with-mediawiki (mediawiki) ; use captured *mediawiki* + (apply #'get-revisions-and-closure args)))))))) + +(defun get-revisions-result (&rest args) + "Like get-revisions, but returns a query-result object" + (multiple-value-bind (revs c-token closure) + (apply #'get-revisions-and-closure args) + (declare (ignore c-token)) + (make-instance 'query-result :results revs :closure closure))) ;; Copyright (c) 2008 Accelerated Data Works, Russ Tyndall -- 2.11.4.GIT