Initial version of the monger.query DSL
Here is what it looks like:
(with-collection "docs"
(find { :inception_year { $lt 2000 $gte 2011 } })
(fields { :inception_year 1 :name 1 })
(skip 10)
(limit 20)
(batch-size 50)
(hint "my-index-name")
(snapshot))
This commit is contained in:
parent
c40b0e25c1
commit
bd133c1afc
2 changed files with 172 additions and 72 deletions
89
src/monger/query.clj
Normal file
89
src/monger/query.clj
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
(ns monger.query
|
||||||
|
(:refer-clojure :exclude [select find sort])
|
||||||
|
(:require monger.core)
|
||||||
|
(:import [com.mongodb DB DBCollection DBObject DBCursor]
|
||||||
|
[java.util List])
|
||||||
|
(:use [monger conversion operators]))
|
||||||
|
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Implementation
|
||||||
|
;;
|
||||||
|
|
||||||
|
(def ^{ :dynamic true } *query-collection*)
|
||||||
|
|
||||||
|
(defn empty-query
|
||||||
|
[^DBCollection coll]
|
||||||
|
{
|
||||||
|
:collection coll
|
||||||
|
:query {}
|
||||||
|
:sort {}
|
||||||
|
:fields []
|
||||||
|
:skip 0
|
||||||
|
:limit 0
|
||||||
|
:batch-size 256
|
||||||
|
:hint nil
|
||||||
|
:snapshot false
|
||||||
|
})
|
||||||
|
|
||||||
|
(defn- fields-to-db-object
|
||||||
|
[^List fields]
|
||||||
|
(to-db-object (zipmap fields (repeat 1))))
|
||||||
|
|
||||||
|
(defn exec
|
||||||
|
[{ :keys [collection query fields skip limit sort batch-size hint snapshot] :or { limit 0 batch-size 256 skip 0 } }]
|
||||||
|
(let [cursor (doto ^DBCursor (.find ^DBCollection collection (to-db-object query) (fields-to-db-object fields))
|
||||||
|
(.limit limit)
|
||||||
|
(.skip skip)
|
||||||
|
(.sort (to-db-object sort))
|
||||||
|
(.batchSize batch-size)
|
||||||
|
(.hint ^DBObject (to-db-object hint))
|
||||||
|
)]
|
||||||
|
(if snapshot
|
||||||
|
(.snapshot cursor))
|
||||||
|
(map (fn [x] (from-db-object x true))
|
||||||
|
(seq cursor))))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; API
|
||||||
|
;;
|
||||||
|
|
||||||
|
(defn find
|
||||||
|
[m query]
|
||||||
|
(merge m { :query query }))
|
||||||
|
|
||||||
|
(defn fields
|
||||||
|
[m flds]
|
||||||
|
(merge m { :fields flds }))
|
||||||
|
|
||||||
|
(defn sort
|
||||||
|
[m srt]
|
||||||
|
(merge m { :sort srt }))
|
||||||
|
|
||||||
|
(defn skip
|
||||||
|
[m ^long n]
|
||||||
|
(merge m { :skip n }))
|
||||||
|
|
||||||
|
(defn limit
|
||||||
|
[m ^long n]
|
||||||
|
(merge m { :limit n }))
|
||||||
|
|
||||||
|
(defn batch-size
|
||||||
|
[m ^long n]
|
||||||
|
(merge m { :batch-size n }))
|
||||||
|
|
||||||
|
(defn hint
|
||||||
|
[m h]
|
||||||
|
(merge m { :hint h }))
|
||||||
|
|
||||||
|
(defn snapshot
|
||||||
|
[m]
|
||||||
|
(merge m { :snapshot true }))
|
||||||
|
|
||||||
|
(defmacro with-collection
|
||||||
|
[^String coll & body]
|
||||||
|
`(binding [*query-collection* (if (string? ~coll)
|
||||||
|
(.getCollection ^DB monger.core/*mongodb-database* ~coll)
|
||||||
|
~coll)]
|
||||||
|
(let [query# (-> (empty-query *query-collection*) ~@body)]
|
||||||
|
(exec query#))))
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
||||||
(ns monger.test.querying
|
(ns monger.test.querying
|
||||||
(:refer-clojure :exclude [select find])
|
(:refer-clojure :exclude [select find sort])
|
||||||
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure]
|
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure]
|
||||||
[org.bson.types ObjectId]
|
[org.bson.types ObjectId]
|
||||||
[java.util Date])
|
[java.util Date])
|
||||||
|
|
@ -10,8 +10,7 @@
|
||||||
[monger.result :as mgres])
|
[monger.result :as mgres])
|
||||||
(:use [clojure.test]
|
(:use [clojure.test]
|
||||||
[monger.test.fixtures]
|
[monger.test.fixtures]
|
||||||
;; [monger.query]
|
[monger conversion query operators]))
|
||||||
[monger.conversion]))
|
|
||||||
|
|
||||||
|
|
||||||
(defn purge-locations-collection
|
(defn purge-locations-collection
|
||||||
|
|
@ -78,85 +77,97 @@
|
||||||
|
|
||||||
;; < ($lt), <= ($lte), > ($gt), >= ($gte)
|
;; < ($lt), <= ($lte), > ($gt), >= ($gte)
|
||||||
|
|
||||||
;; (deftest query-using-dsl-and-$lt-operator
|
(deftest query-using-dsl-and-$lt-operator-1
|
||||||
;; (let [coll "docs"
|
(let [coll "docs"
|
||||||
;; doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 }
|
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 }
|
||||||
;; doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 }
|
doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 }
|
||||||
;; doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 }
|
doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 }
|
||||||
;; _ (mgcol/insert-batch coll [doc1 doc2])
|
_ (mgcol/insert-batch coll [doc1 doc2])
|
||||||
;; lt-result (in-collection "docs"
|
lt-result (with-collection "docs"
|
||||||
;; (find { :inception_year { "$lt" 2000 } })]
|
(find { :inception_year { $lt 2000 } })
|
||||||
;; (is (= [doc2] lt-result))))
|
(limit 2))]
|
||||||
|
(is (= [doc2] lt-result))))
|
||||||
|
|
||||||
|
(deftest query-using-dsl-and-$lt-operator-2
|
||||||
|
(let [coll "docs"
|
||||||
|
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 }
|
||||||
|
doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 }
|
||||||
|
doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 }
|
||||||
|
_ (mgcol/insert-batch coll [doc1 doc2])
|
||||||
|
lt-result (with-collection "docs"
|
||||||
|
(find { :inception_year { $lt 2000 } }))]
|
||||||
|
(is (= [doc2] lt-result))))
|
||||||
|
|
||||||
|
|
||||||
(deftest query-with-find-maps-using-$lt-operator
|
|
||||||
(let [coll "docs"
|
(deftest query-with-find-maps-using-$lt-operator
|
||||||
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 }
|
(let [coll "docs"
|
||||||
doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 }
|
doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 }
|
||||||
doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 }
|
doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 }
|
||||||
_ (mgcol/insert-batch coll [doc1 doc2])
|
doc3 { :language "Scala" :_id (ObjectId.) :inception_year 2003 }
|
||||||
lt-result (mgcol/find-maps coll { :inception_year { "$lt" 2000 } })
|
_ (mgcol/insert-batch coll [doc1 doc2])
|
||||||
lte-result (mgcol/find-maps coll { :inception_year { "$lte" 1992 } })
|
lt-result (mgcol/find-maps coll { :inception_year { "$lt" 2000 } })
|
||||||
gt-result (mgcol/find-maps coll { :inception_year { "$gt" 2005 } })
|
lte-result (mgcol/find-maps coll { :inception_year { "$lte" 1992 } })
|
||||||
gte-result (mgcol/find-maps coll { :inception_year { "$gte" 2006 } })]
|
gt-result (mgcol/find-maps coll { :inception_year { "$gt" 2005 } })
|
||||||
(is (= [doc2] lt-result))
|
gte-result (mgcol/find-maps coll { :inception_year { "$gte" 2006 } })]
|
||||||
(is (= [doc2] lte-result))
|
(is (= [doc2] lt-result))
|
||||||
(is (= [doc1] gt-result))
|
(is (= [doc2] lte-result))
|
||||||
(is (= [doc1] gte-result))))
|
(is (= [doc1] gt-result))
|
||||||
|
(is (= [doc1] gte-result))))
|
||||||
|
|
||||||
|
|
||||||
;; $all
|
;; $all
|
||||||
|
|
||||||
(deftest query-with-find-maps-using-$all
|
(deftest query-with-find-maps-using-$all
|
||||||
(let [coll "docs"
|
(let [coll "docs"
|
||||||
doc1 { :_id (ObjectId.) :title "Clojure" :tags ["functional" "homoiconic" "syntax-oriented" "dsls" "concurrency features" "jvm"] }
|
doc1 { :_id (ObjectId.) :title "Clojure" :tags ["functional" "homoiconic" "syntax-oriented" "dsls" "concurrency features" "jvm"] }
|
||||||
doc2 { :_id (ObjectId.) :title "Java" :tags ["object-oriented" "jvm"] }
|
doc2 { :_id (ObjectId.) :title "Java" :tags ["object-oriented" "jvm"] }
|
||||||
doc3 { :_id (ObjectId.) :title "Scala" :tags ["functional" "object-oriented" "dsls" "concurrency features" "jvm"] }
|
doc3 { :_id (ObjectId.) :title "Scala" :tags ["functional" "object-oriented" "dsls" "concurrency features" "jvm"] }
|
||||||
- (mgcol/insert-batch coll [doc1 doc2 doc3])
|
- (mgcol/insert-batch coll [doc1 doc2 doc3])
|
||||||
result1 (mgcol/find-maps coll { :tags { "$all" ["functional" "jvm" "homoiconic"] } })
|
result1 (mgcol/find-maps coll { :tags { "$all" ["functional" "jvm" "homoiconic"] } })
|
||||||
result2 (mgcol/find-maps coll { :tags { "$all" ["functional" "native" "homoiconic"] } })
|
result2 (mgcol/find-maps coll { :tags { "$all" ["functional" "native" "homoiconic"] } })
|
||||||
result3 (mgcol/find-maps coll { :tags { "$all" ["functional" "jvm" "dsls"] } })]
|
result3 (mgcol/find-maps coll { :tags { "$all" ["functional" "jvm" "dsls"] } })]
|
||||||
(is (= [doc1] result1))
|
(is (= [doc1] result1))
|
||||||
(is (empty? result2))
|
(is (empty? result2))
|
||||||
(is (= 2 (count result3)))))
|
(is (= 2 (count result3)))))
|
||||||
|
|
||||||
|
|
||||||
;; $exists
|
;; $exists
|
||||||
|
|
||||||
(deftest query-with-find-one-as-map-using-$exists
|
(deftest query-with-find-one-as-map-using-$exists
|
||||||
(let [coll "docs"
|
(let [coll "docs"
|
||||||
doc1 { :_id (ObjectId.) :published-by "Jill The Blogger" :draft false :title "X announces another Y" }
|
doc1 { :_id (ObjectId.) :published-by "Jill The Blogger" :draft false :title "X announces another Y" }
|
||||||
doc2 { :_id (ObjectId.) :draft true :title "Z announces a Y competitor" }
|
doc2 { :_id (ObjectId.) :draft true :title "Z announces a Y competitor" }
|
||||||
_ (mgcol/insert-batch coll [doc1 doc2])
|
_ (mgcol/insert-batch coll [doc1 doc2])
|
||||||
result1 (mgcol/find-one-as-map coll { :published-by { "$exists" true } })
|
result1 (mgcol/find-one-as-map coll { :published-by { "$exists" true } })
|
||||||
result2 (mgcol/find-one-as-map coll { :published-by { "$exists" false } })]
|
result2 (mgcol/find-one-as-map coll { :published-by { "$exists" false } })]
|
||||||
(is (= doc1 result1))
|
(is (= doc1 result1))
|
||||||
(is (= doc2 result2))))
|
(is (= doc2 result2))))
|
||||||
|
|
||||||
;; $mod
|
;; $mod
|
||||||
|
|
||||||
(deftest query-with-find-one-as-map-using-$mod
|
(deftest query-with-find-one-as-map-using-$mod
|
||||||
(let [coll "docs"
|
(let [coll "docs"
|
||||||
doc1 { :_id (ObjectId.) :counter 25 }
|
doc1 { :_id (ObjectId.) :counter 25 }
|
||||||
doc2 { :_id (ObjectId.) :counter 32 }
|
doc2 { :_id (ObjectId.) :counter 32 }
|
||||||
doc3 { :_id (ObjectId.) :counter 63 }
|
doc3 { :_id (ObjectId.) :counter 63 }
|
||||||
_ (mgcol/insert-batch coll [doc1 doc2 doc3])
|
_ (mgcol/insert-batch coll [doc1 doc2 doc3])
|
||||||
result1 (mgcol/find-one-as-map coll { :counter { "$mod" [10, 5] } })
|
result1 (mgcol/find-one-as-map coll { :counter { "$mod" [10, 5] } })
|
||||||
result2 (mgcol/find-one-as-map coll { :counter { "$mod" [10, 2] } })
|
result2 (mgcol/find-one-as-map coll { :counter { "$mod" [10, 2] } })
|
||||||
result3 (mgcol/find-one-as-map coll { :counter { "$mod" [11, 1] } })]
|
result3 (mgcol/find-one-as-map coll { :counter { "$mod" [11, 1] } })]
|
||||||
(is (= doc1 result1))
|
(is (= doc1 result1))
|
||||||
(is (= doc2 result2))
|
(is (= doc2 result2))
|
||||||
(is (empty? result3))))
|
(is (empty? result3))))
|
||||||
|
|
||||||
|
|
||||||
;; $ne
|
;; $ne
|
||||||
|
|
||||||
(deftest query-with-find-one-as-map-using-$ne
|
(deftest query-with-find-one-as-map-using-$ne
|
||||||
(let [coll "docs"
|
(let [coll "docs"
|
||||||
doc1 { :_id (ObjectId.) :counter 25 }
|
doc1 { :_id (ObjectId.) :counter 25 }
|
||||||
doc2 { :_id (ObjectId.) :counter 32 }
|
doc2 { :_id (ObjectId.) :counter 32 }
|
||||||
_ (mgcol/insert-batch coll [doc1 doc2])
|
_ (mgcol/insert-batch coll [doc1 doc2])
|
||||||
result1 (mgcol/find-one-as-map coll { :counter { "$ne" 25 } })
|
result1 (mgcol/find-one-as-map coll { :counter { "$ne" 25 } })
|
||||||
result2 (mgcol/find-one-as-map coll { :counter { "$ne" 32 } })]
|
result2 (mgcol/find-one-as-map coll { :counter { "$ne" 32 } })]
|
||||||
(is (= doc2 result1))
|
(is (= doc2 result1))
|
||||||
(is (= doc1 result2))))
|
(is (= doc1 result2))))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue