added cursor namespaces, cleanups;

refactored helpers for DBCursor into own namespace;
cleaned up previous hacks;
added extra classes for add-options;
added tests for cursor namespace;
This commit is contained in:
Timo Sulg 2013-09-21 15:46:01 +02:00
parent 58b5b1ddac
commit 2dca992496
3 changed files with 204 additions and 87 deletions

View file

@ -25,8 +25,7 @@
* http://clojuremongodb.info/articles/aggregation.html"
(:refer-clojure :exclude [find remove count drop distinct empty?])
(:import [com.mongodb Mongo DB DBCollection WriteResult DBObject WriteConcern
DBCursor MapReduceCommand MapReduceCommand$OutputType
Bytes]
DBCursor MapReduceCommand MapReduceCommand$OutputType]
[java.util List Map]
[clojure.lang IPersistentMap ISeq]
org.bson.types.ObjectId)
@ -123,91 +122,6 @@
;;
;; monger.collection/find
;;
;;;;TODO: add query-options
(comment
(import '[com.mongodb Mongo DB DBCollection WriteResult DBObject WriteConcern
DBCursor MapReduceCommand MapReduceCommand$OutputType
Bytes]
'[java.util List Map]
'[clojure.lang IPersistentMap ISeq]
'org.bson.types.ObjectId)
(require '[monger.core :as mongo]
'[monger.result])
(require '[monger.conversion :refer [from-db-object to-db-object as-field-selector]])
(mongo/connect!)
(mongo/set-db! (mongo/get-db "veye_dev"))
(def collection :products)
(def coll (.getCollection monger.core/*mongodb-database* (name collection)))
)
(defn ^DBCursor make-db-cursor
([^String collection] (make-db-cursor collection {} {}))
([^String collection ^Map ref] (make-db-cursor collection ref {}))
([^String collection ^Map ref fields]
(.find
(.getCollection monger.core/*mongodb-database* (name collection))
(to-db-object ref)
(as-field-selector fields))))
(def cursor-options-map {:awaitdata Bytes/QUERYOPTION_AWAITDATA
:exhaust Bytes/QUERYOPTION_EXHAUST
:notimeout Bytes/QUERYOPTION_NOTIMEOUT
:oplogreplay Bytes/QUERYOPTION_OPLOGREPLAY
:partial Bytes/QUERYOPTION_PARTIAL
:slaveok Bytes/QUERYOPTION_SLAVEOK
:tailable Bytes/QUERYOPTION_TAILABLE})
(defn get-cursor-options
""
[db-cur]
(into {}
(for [[opt option-mask] cursor-options-map]
[opt (< 0 (bit-and (.getOptions db-cur) option-mask))])))
(defn ^DBCursor apply-cursor-options [db-cur options]
"Applies cursor options and return cursor."
(doseq [[opt value] (seq options)]
(if (= true value)
(.addOption db-cur (get cursor-options-map opt 0))
(.setOptions db-cur (bit-and-not (.getOptions db-cur) (get cursor-options-map opt 0)))))
db-cur)
(defmulti format-cursor (fn [db-cur as] as))
(defmethod format-cursor :map [db-cur as]
(map #(from-db-object %1 true) db-cur))
(defmethod format-cursor :seq [db-cur as]
(seq db-cur))
(defmethod format-cursor :default [db-cur as]
db-cur)
(defn find-all
"Queries for objects from specified collection and accepts additional parameters as keywords.
Arguments:
collection - required, name of collections as string or keyword value
Keyword arguments:
:criteria - clojure map of matching criterias, for example {:name 'Monger', :language 'Clojure'}
:fields - specify fields, valid formats [:field1 :field2], {:_id -1, :name 1}
:options - clojure map of options for cursors, for example {:notimeout true :partial false}
allows notimeout and cancels shardings for query's cursor.
:as - specifies format of response, ala unified method to replace find, find-maps, find-seq.
Valid values are :map, :seq and other values are ignored.
Examples:
(.count (find-all :products))
(.count (find-all :products :criteria {:name 'Monger'} :fields {:_id -1}))
(find-all :product :options {:notimeout true} :as :map)
"
[collection & {:keys [criteria fields options as]
:or {criteria {}, fields {}, options nil, as nil}}]
(let [db-cur (make-db-cursor collection criteria fields)]
(-> db-cur
(apply-cursor-options options)
(format-cursor as))))
(defn ^DBCursor find
"Queries for objects in this collection.

View file

@ -0,0 +1,93 @@
(ns monger.cursor
"Helpers function for dbCursor object: creating new cursor,
CRUD functionality for cursor options
Related documentation guides:
* ...
"
(:import [com.mongodb DBCursor Bytes]
[java.util List Map]
[java.lang Integer]
[clojure.lang Keyword])
(:require [monger.conversion :refer [to-db-object from-db-object as-field-selector]]))
(defn ^DBCursor make-db-cursor
"initializes new db-collection."
([^String collection] (make-db-cursor collection {} {}))
([^String collection ^Map ref] (make-db-cursor collection ref {}))
([^String collection ^Map ref fields]
(.find
(.getCollection monger.core/*mongodb-database* (name collection))
(to-db-object ref)
(as-field-selector fields))))
(def cursor-options {:awaitdata Bytes/QUERYOPTION_AWAITDATA
;;:exhaust Bytes/QUERYOPTION_EXHAUST - not human settable
:notimeout Bytes/QUERYOPTION_NOTIMEOUT
:oplogreplay Bytes/QUERYOPTION_OPLOGREPLAY
:partial Bytes/QUERYOPTION_PARTIAL
:slaveok Bytes/QUERYOPTION_SLAVEOK
:tailable Bytes/QUERYOPTION_TAILABLE})
(defn get-options
"Returns map of cursor's options with current state."
[^DBCursor db-cur]
(into {}
(for [[opt option-mask] cursor-options]
[opt (< 0 (bit-and (.getOptions db-cur) option-mask))])))
(defn add-option! [^DBCursor db-cur, ^String opt]
(.addOption db-cur (get cursor-options (keyword opt) 0)))
(defn remove-option! [^DBCursor db-cur, ^String opt]
(.setOptions db-cur (bit-and-not (.getOptions db-cur)
(get cursor-options (keyword opt) 0))))
(defmulti add-options (fn [db-cur opts] (class opts)))
(defmethod add-options Map [^DBCursor db-cur options]
"Applies cursor options with switch values, where true means switch on
and false removes specified options from current cursor.
example: (add-options db-cur {:notimeout true, :tailable false})
returns cursor."
(doseq [[opt value] (seq options)]
(if (= true value)
(add-option! db-cur opt)
(remove-option! db-cur opt)))
db-cur)
(defmethod add-options List [^DBCursor db-cur options]
"Takes list of options to add current key"
(doseq [opt (seq options)]
(add-option! db-cur opt))
db-cur)
(defmethod add-options Integer [^DBCursor db-cur, option]
"Takes com.mongodb.Byte value and adds it to current settings."
(.addOption db-cur option)
db-cur)
(defmethod add-options Keyword [^DBCursor db-cur, option]
(add-option! db-cur option)
db-cur)
(defmethod add-options :default [^DBCursor db-cur, options]
(println "add-options dont support type for options: " (class options))
db-cur)
(defn ^DBCursor reset-options
"Resets cursor options to default value and returns cursor"
[^DBCursor db-cur]
(.resetOptions db-cur)
db-cur)
(defmulti format-as (fn [db-cur as] as))
(defmethod format-as :map [db-cur as]
(map #(from-db-object %1 true) db-cur))
(defmethod format-as :seq [db-cur as]
(seq db-cur))
(defmethod format-as :default [db-cur as]
db-cur)

View file

@ -0,0 +1,110 @@
(set! *warn-on-reflection* true)
(ns monger.test.cursor-test
(:import [com.mongodb DBCursor DBObject Bytes]
[java.util List Map])
(:require [monger.test.helper :as helper])
(:use clojure.test
monger.cursor
monger.test.fixtures))
(helper/connect!)
(deftest make-db-cursor-for-collection
(is (= DBCursor
(class (make-db-cursor :docs)))))
(deftest getting-cursor-options-value
(let [db-cur (make-db-cursor :docs)
opts (get-options db-cur)]
(is (= true (isa? (class opts) Map)))
(is (= 0 (.getOptions db-cur))) ;;test default value
(is (= false (:notimeout opts)))
(is (= false (:partial opts)))
(is (= false (:awaitdata opts)))
(is (= false (:oplogreplay opts)))
(is (= false (:slaveok opts)))
(is (= false (:tailable opts)))))
(deftest adding-option-to-cursor
(let [db-cur (make-db-cursor :docs)
_ (add-option! db-cur :notimeout)]
(is (= (:notimeout cursor-options)
(.getOptions db-cur)))
(add-option! db-cur :tailable)
(is (= (.getOptions db-cur)
(bit-or (:notimeout cursor-options)
(:tailable cursor-options))))))
(deftest remove-option-from-cursor
(let [db-cur (make-db-cursor :docs)]
(add-option! db-cur :partial)
(add-option! db-cur :awaitdata)
;; removing not-set option should not affect result
(remove-option! db-cur :notimeout)
(is (= (.getOptions db-cur)
(bit-or (:partial cursor-options)
(:awaitdata cursor-options))))
;; removing active option should remove correct value
(remove-option! db-cur :awaitdata)
(is (= (.getOptions db-cur)
(:partial cursor-options)))))
(deftest test-reset-options
(let [db-cur (make-db-cursor :docs)]
(add-option! db-cur :partial)
(is (= (.getOptions db-cur)
(:partial cursor-options)))
(is (= 0
(int (.getOptions (reset-options db-cur)))))))
(deftest add-options-with-hashmap
(let [db-cur (make-db-cursor :docs)
_ (add-options db-cur {:notimeout true :slaveok true})
opts (get-options db-cur)]
(is (= true (:notimeout opts)))
(is (= true (:slaveok opts)))
(is (= false (:tailable opts)))
(is (= false (:oplogreplay opts)))))
(deftest add-options-with-hashmap-and-remove-option
(let [db-cur (make-db-cursor :docs)
_ (add-options db-cur {:notimeout true :slaveok true})
opts (get-options db-cur)]
(is (= true (:notimeout opts)))
(is (= true (:slaveok opts)))
;;remove key and add another option
(add-options db-cur {:partial true :slaveok false})
(let [opts (get-options db-cur)]
(is (= true (:notimeout opts)))
(is (= true (:partial opts)))
(is (= false (:slaveok opts)))
(is (= false (:tailable opts))))))
(deftest add-options-with-list
(let [db-cur (make-db-cursor :docs)
_ (add-options db-cur [:notimeout :slaveok])
opts (get-options db-cur)]
(is (= true (:notimeout opts)))
(is (= true (:slaveok opts)))
(is (= false (:tailable opts)))
(is (= false (:oplogreplay opts)))))
(deftest add-options-with-Bytes
(let [db-cur (make-db-cursor :docs)
_ (add-options db-cur Bytes/QUERYOPTION_NOTIMEOUT)
opts (get-options db-cur)]
(is (= true (:notimeout opts)))
(is (= false (:slaveok opts)))
(is (= false (:tailable opts)))
(is (= false (:oplogreplay opts)))))
(deftest add-options-with-one-keyword
(let [db-cur (make-db-cursor :docs)
_ (add-options db-cur :notimeout)
opts (get-options db-cur)]
(is (= true (:notimeout opts)))
(is (= false (:slaveok opts)))
(is (= false (:tailable opts)))
(is (= false (:oplogreplay opts)))))