Support field negation in queries, closes #17
This commit is contained in:
parent
a68d8652e3
commit
6282f41f06
6 changed files with 103 additions and 59 deletions
14
ChangeLog.md
14
ChangeLog.md
|
|
@ -1,5 +1,19 @@
|
|||
## Changes between 1.0.0-beta2 and 1.0.0-beta3
|
||||
|
||||
### Support for field negation in queries
|
||||
|
||||
Previously to load only a subset of document fields with Monger, one had to specify them all. Starting
|
||||
with 1.0.0-beta3, Monger supports [field negation](http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields#RetrievingaSubsetofFields-FieldNegation) feature of MongoDB: it is possible to exclude
|
||||
certain fields instead.
|
||||
|
||||
To do so, pass a map as field selector, with fields that should be omitted set to 0:
|
||||
|
||||
``` clojure
|
||||
;; will retrieve all fields except body
|
||||
(monger.collection/find-one-map "documents" {:author "John"} {:body 0})
|
||||
```
|
||||
|
||||
|
||||
### Validateur 1.1.0-beta1
|
||||
|
||||
[Validateur](https://github.com/michaelklishin/validateur) dependency has been upgraded to 1.1.0-beta1.
|
||||
|
|
|
|||
|
|
@ -21,10 +21,6 @@
|
|||
;; Implementation
|
||||
;;
|
||||
|
||||
(defn- fields-to-db-object
|
||||
[^List fields]
|
||||
(zipmap fields (repeat 1)))
|
||||
|
||||
(definline check-not-nil!
|
||||
[ref ^String message]
|
||||
`(when (nil? ~ref)
|
||||
|
|
@ -104,13 +100,13 @@
|
|||
([^String collection ^Map ref]
|
||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
||||
(.find ^DBCollection coll ^DBObject (to-db-object ref))))
|
||||
([^String collection ^Map ref ^List fields]
|
||||
([^String collection ^Map ref fields]
|
||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)
|
||||
map-of-fields (fields-to-db-object fields)]
|
||||
map-of-fields (as-field-selector fields)]
|
||||
(.find ^DBCollection coll ^DBObject (to-db-object ref) ^DBObject (to-db-object map-of-fields))))
|
||||
([^DB db ^String collection ^Map ref ^List fields]
|
||||
([^DB db ^String collection ^Map ref fields]
|
||||
(let [^DBCollection coll (.getCollection db collection)
|
||||
map-of-fields (fields-to-db-object fields)]
|
||||
map-of-fields (as-field-selector fields)]
|
||||
(.find ^DBCollection coll ^DBObject (to-db-object ref) ^DBObject (to-db-object map-of-fields)))))
|
||||
|
||||
(defn ^ISeq find-maps
|
||||
|
|
@ -122,9 +118,9 @@
|
|||
(map (fn [x] (from-db-object x true)) (find collection)))
|
||||
([^String collection ^Map ref]
|
||||
(map (fn [x] (from-db-object x true)) (find collection ref)))
|
||||
([^String collection ^Map ref ^List fields]
|
||||
([^String collection ^Map ref fields]
|
||||
(map (fn [x] (from-db-object x true)) (find collection ref fields)))
|
||||
([^DB db ^String collection ^Map ref ^List fields]
|
||||
([^DB db ^String collection ^Map ref fields]
|
||||
(map (fn [x] (from-db-object x true)) (find db collection ref fields))))
|
||||
|
||||
(defn ^ISeq find-seq
|
||||
|
|
@ -133,9 +129,9 @@
|
|||
(seq (find collection)))
|
||||
([^String collection ^Map ref]
|
||||
(seq (find collection ref)))
|
||||
([^String collection ^Map ref ^List fields]
|
||||
([^String collection ^Map ref fields]
|
||||
(seq (find collection ref fields)))
|
||||
([^DB db ^String collection ^Map ref ^List fields]
|
||||
([^DB db ^String collection ^Map ref fields]
|
||||
(seq (find db collection ref fields))))
|
||||
|
||||
;;
|
||||
|
|
@ -157,22 +153,22 @@
|
|||
([^String collection ^Map ref]
|
||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)]
|
||||
(.findOne ^DBCollection coll ^DBObject (to-db-object ref))))
|
||||
([^String collection ^Map ref ^List fields]
|
||||
([^String collection ^Map ref fields]
|
||||
(let [^DBCollection coll (.getCollection monger.core/*mongodb-database* collection)
|
||||
map-of-fields (fields-to-db-object fields)]
|
||||
map-of-fields (as-field-selector fields)]
|
||||
(.findOne ^DBCollection coll ^DBObject (to-db-object ref) ^DBObject (to-db-object map-of-fields))))
|
||||
([^DB db ^String collection ^Map ref ^List fields]
|
||||
([^DB db ^String collection ^Map ref fields]
|
||||
(let [^DBCollection coll (.getCollection db collection)
|
||||
map-of-fields (fields-to-db-object fields)]
|
||||
map-of-fields (as-field-selector fields)]
|
||||
(.findOne ^DBCollection coll ^DBObject (to-db-object ref) ^DBObject (to-db-object map-of-fields)))))
|
||||
|
||||
(defn ^IPersistentMap find-one-as-map
|
||||
"Returns a single object converted to Map from this collection matching the query."
|
||||
([^String collection ^Map ref]
|
||||
(from-db-object ^DBObject (find-one collection ref) true))
|
||||
([^String collection ^Map ref ^List fields]
|
||||
([^String collection ^Map ref fields]
|
||||
(from-db-object ^DBObject (find-one collection ref fields) true))
|
||||
([^String collection ^Map ref ^List fields keywordize]
|
||||
([^String collection ^Map ref fields keywordize]
|
||||
(from-db-object ^DBObject (find-one collection ref fields) keywordize)))
|
||||
|
||||
|
||||
|
|
@ -195,10 +191,10 @@
|
|||
([^String collection id]
|
||||
(check-not-nil! id "id must not be nil")
|
||||
(find-one collection { :_id id }))
|
||||
([^String collection id ^List fields]
|
||||
([^String collection id fields]
|
||||
(check-not-nil! id "id must not be nil")
|
||||
(find-one collection { :_id id } fields))
|
||||
([^DB db ^String collection id ^List fields]
|
||||
([^DB db ^String collection id fields]
|
||||
(check-not-nil! id "id must not be nil")
|
||||
(find-one db collection { :_id id } fields)))
|
||||
|
||||
|
|
@ -207,10 +203,10 @@
|
|||
([^String collection id]
|
||||
(check-not-nil! id "id must not be nil")
|
||||
(from-db-object ^DBObject (find-one-as-map collection { :_id id }) true))
|
||||
([^String collection id ^List fields]
|
||||
([^String collection id fields]
|
||||
(check-not-nil! id "id must not be nil")
|
||||
(from-db-object ^DBObject (find-one-as-map collection { :_id id } fields) true))
|
||||
([^String collection id ^List fields keywordize]
|
||||
([^String collection id fields keywordize]
|
||||
(check-not-nil! id "id must not be nil")
|
||||
(from-db-object ^DBObject (find-one-as-map collection { :_id id } fields) keywordize)))
|
||||
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@
|
|||
(:import [com.mongodb DBObject BasicDBObject BasicDBList DBCursor]
|
||||
[clojure.lang IPersistentMap Keyword Ratio]
|
||||
[java.util List Map Date]
|
||||
[org.bson.types ObjectId]))
|
||||
org.bson.types.ObjectId))
|
||||
|
||||
(defprotocol ConvertToDBObject
|
||||
(to-db-object [input] "Converts given piece of Clojure data to BasicDBObject MongoDB Java driver uses"))
|
||||
(^com.mongodb.DBObject to-db-object [input] "Converts given piece of Clojure data to BasicDBObject MongoDB Java driver uses"))
|
||||
|
||||
(extend-protocol ConvertToDBObject
|
||||
nil
|
||||
|
|
@ -109,7 +109,7 @@
|
|||
|
||||
|
||||
(defprotocol ConvertToObjectId
|
||||
(to-object-id [input] "Instantiates ObjectId from input unless the input itself is an ObjectId instance. In that case, returns input as is."))
|
||||
(^org.bson.types.ObjectId to-object-id [input] "Instantiates ObjectId from input unless the input itself is an ObjectId instance. In that case, returns input as is."))
|
||||
|
||||
(extend-protocol ConvertToObjectId
|
||||
String
|
||||
|
|
@ -125,3 +125,19 @@
|
|||
input))
|
||||
|
||||
|
||||
|
||||
(defprotocol FieldSelector
|
||||
(^com.mongodb.DBObject as-field-selector [input] "Converts values to DBObject that can be used to specify a list of document fields (including negation support)"))
|
||||
|
||||
(extend-protocol FieldSelector
|
||||
DBObject
|
||||
(as-field-selector [^DBObject input]
|
||||
input)
|
||||
|
||||
List
|
||||
(as-field-selector [^List input]
|
||||
(to-db-object (zipmap input (repeat 1))))
|
||||
|
||||
Object
|
||||
(as-field-selector [input]
|
||||
(to-db-object input)))
|
||||
|
|
|
|||
|
|
@ -58,13 +58,9 @@
|
|||
([^DBCollection coll]
|
||||
(merge (empty-query) { :collection coll })))
|
||||
|
||||
(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 read-preference keywordize-fields] :or { limit 0 batch-size 256 skip 0 } }]
|
||||
(let [cursor (doto ^DBCursor (.find ^DBCollection collection (to-db-object query) (fields-to-db-object fields))
|
||||
(let [cursor (doto ^DBCursor (.find ^DBCollection collection (to-db-object query) (as-field-selector fields))
|
||||
(.limit limit)
|
||||
(.skip skip)
|
||||
(.sort (to-db-object sort))
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
(ns monger.test.conversion
|
||||
(:require [monger core collection]
|
||||
[monger.conversion :as cnv])
|
||||
(:require [monger core collection])
|
||||
(:import [com.mongodb DBObject BasicDBObject BasicDBList]
|
||||
[java.util Date Calendar List ArrayList]
|
||||
[org.bson.types ObjectId])
|
||||
(:use [clojure.test]))
|
||||
(:use clojure.test monger.conversion))
|
||||
|
||||
|
||||
;;
|
||||
|
|
@ -13,33 +12,33 @@
|
|||
|
||||
(deftest convert-nil-to-dbobject
|
||||
(let [input nil
|
||||
output (cnv/to-db-object input)]
|
||||
output (to-db-object input)]
|
||||
(is (nil? output))))
|
||||
|
||||
(deftest convert-integer-to-dbobject
|
||||
(let [input 1
|
||||
output (cnv/to-db-object input)]
|
||||
output (to-db-object input)]
|
||||
(is (= input output))))
|
||||
|
||||
(deftest convert-float-to-dbobject
|
||||
(let [input 11.12
|
||||
output (cnv/to-db-object input)]
|
||||
output (to-db-object input)]
|
||||
(is (= input output))))
|
||||
|
||||
(deftest convert-rationale-to-dbobject
|
||||
(let [input 11/2
|
||||
output (cnv/to-db-object input)]
|
||||
output (to-db-object input)]
|
||||
(is (= 5.5 output))))
|
||||
|
||||
(deftest convert-string-to-dbobject
|
||||
(let [input "MongoDB"
|
||||
output (cnv/to-db-object input)]
|
||||
output (to-db-object input)]
|
||||
(is (= input output))))
|
||||
|
||||
|
||||
(deftest convert-map-to-dbobject
|
||||
(let [input { :int 1, :string "Mongo", :float 22.23 }
|
||||
output ^DBObject (cnv/to-db-object input)]
|
||||
output ^DBObject (to-db-object input)]
|
||||
(is (= 1 (.get output "int")))
|
||||
(is (= "Mongo" (.get output "string")))
|
||||
(is (= 22.23 (.get output "float")))))
|
||||
|
|
@ -47,7 +46,7 @@
|
|||
|
||||
(deftest convert-nested-map-to-dbobject
|
||||
(let [input { :int 1, :string "Mongo", :float 22.23, :map { :int 10, :string "Clojure", :float 11.9, :list '(1 "a" :b), :map { :key "value" } } }
|
||||
output ^DBObject (cnv/to-db-object input)
|
||||
output ^DBObject (to-db-object input)
|
||||
inner ^DBObject (.get output "map")]
|
||||
(is (= 10 (.get inner "int")))
|
||||
(is (= "Clojure" (.get inner "string")))
|
||||
|
|
@ -59,19 +58,19 @@
|
|||
;; to obtain _id that was generated. MK.
|
||||
(deftest convert-dbobject-to-dbobject
|
||||
(let [input (BasicDBObject.)
|
||||
output (cnv/to-db-object input)]
|
||||
output (to-db-object input)]
|
||||
(is (= input output))))
|
||||
|
||||
(deftest convert-java-date-to-dbobject
|
||||
(let [date (Date.)
|
||||
input { :int 1, :string "Mongo", :date date }
|
||||
output ^DBObject (cnv/to-db-object input)]
|
||||
output ^DBObject (to-db-object input)]
|
||||
(is (= date (.get output "date")))))
|
||||
|
||||
(deftest convert-java-calendar-instance-to-dbobject
|
||||
(let [date (Calendar/getInstance)
|
||||
input { :int 1, :string "Mongo", :date date }
|
||||
output ^DBObject (cnv/to-db-object input)]
|
||||
output ^DBObject (to-db-object input)]
|
||||
(is (= date (.get output "date")))))
|
||||
|
||||
|
||||
|
|
@ -82,16 +81,16 @@
|
|||
;;
|
||||
|
||||
(deftest convert-nil-from-db-object
|
||||
(is (nil? (cnv/from-db-object nil false)))
|
||||
(is (nil? (cnv/from-db-object nil true))))
|
||||
(is (nil? (from-db-object nil false)))
|
||||
(is (nil? (from-db-object nil true))))
|
||||
|
||||
(deftest convert-integer-from-dbobject
|
||||
(is (= 2 (cnv/from-db-object 2 false)))
|
||||
(is (= 2 (cnv/from-db-object 2 true))))
|
||||
(is (= 2 (from-db-object 2 false)))
|
||||
(is (= 2 (from-db-object 2 true))))
|
||||
|
||||
(deftest convert-float-from-dbobject
|
||||
(is (= 3.3 (cnv/from-db-object 3.3 false)))
|
||||
(is (= 3.3 (cnv/from-db-object 3.3 true))))
|
||||
(is (= 3.3 (from-db-object 3.3 false)))
|
||||
(is (= 3.3 (from-db-object 3.3 true))))
|
||||
|
||||
(deftest convert-flat-db-object-to-map-without-keywordizing
|
||||
(let [name "Michael"
|
||||
|
|
@ -99,7 +98,7 @@
|
|||
input (doto (BasicDBObject.)
|
||||
(.put "name" name)
|
||||
(.put "age" age))
|
||||
output (cnv/from-db-object input false)]
|
||||
output (from-db-object input false)]
|
||||
(is (= (output { "name" name, "age" age })))
|
||||
(is (= (output "name") name))
|
||||
(is (nil? (output :name)))
|
||||
|
|
@ -112,7 +111,7 @@
|
|||
input (doto (BasicDBObject.)
|
||||
(.put "name" name)
|
||||
(.put "age" age))
|
||||
output (cnv/from-db-object input true)]
|
||||
output (from-db-object input true)]
|
||||
(is (= (output { :name name, :age age })))
|
||||
(is (= (output :name) name))
|
||||
(is (nil? (output "name")))
|
||||
|
|
@ -131,7 +130,7 @@
|
|||
input (doto (BasicDBObject.)
|
||||
(.put "_id" did)
|
||||
(.put "nested" nested))
|
||||
output (cnv/from-db-object input false)]
|
||||
output (from-db-object input false)]
|
||||
(is (= (output "_id") did))
|
||||
(is (= (-> output (get "nested") (get "int")) 101))
|
||||
(is (= (-> output (get "nested") (get "list")) ["red" "green" "blue"]))
|
||||
|
|
@ -145,5 +144,18 @@
|
|||
|
||||
(deftest test-conversion-to-object-id
|
||||
(let [output (ObjectId. "4efb39370364238a81020502")]
|
||||
(is (= output (cnv/to-object-id "4efb39370364238a81020502")))
|
||||
(is (= output (cnv/to-object-id output)))))
|
||||
(is (= output (to-object-id "4efb39370364238a81020502")))
|
||||
(is (= output (to-object-id output)))))
|
||||
|
||||
|
||||
;;
|
||||
;; Field selector coercion
|
||||
;;
|
||||
|
||||
(deftest test-field-selector-coercion
|
||||
(are [i o] (is (= (from-db-object (as-field-selector i) true) o))
|
||||
[:a :b :c] {:a 1 :b 1 :c 1}
|
||||
'(:a :b :c) {:a 1 :b 1 :c 1}
|
||||
{:a 1 :b 1 :c 1} {:a 1 :b 1 :c 1}
|
||||
{"a" 1 "b" 1 "c" 1} {:a 1 :b 1 :c 1}
|
||||
{:comments 0} {:comments 0}))
|
||||
|
|
|
|||
|
|
@ -54,21 +54,31 @@
|
|||
(let [collection "docs"
|
||||
doc-id (monger.util/random-uuid)
|
||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
||||
fields [:language]
|
||||
_ (mgcol/insert collection doc)
|
||||
loaded (mgcol/find-one collection { :language "Clojure" } fields)]
|
||||
loaded (mgcol/find-one collection { :language "Clojure" } [:language])]
|
||||
(is (nil? (.get ^DBObject loaded "data-store")))
|
||||
(is (= doc-id (monger.util/get-id loaded)))
|
||||
(is (= "Clojure" (.get ^DBObject loaded "language")))))
|
||||
|
||||
|
||||
(deftest find-one-partial-document-using-field-negation-when-collection-has-matches
|
||||
(let [collection "docs"
|
||||
doc-id (monger.util/random-uuid)
|
||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
||||
_ (mgcol/insert collection doc)
|
||||
^DBObject loaded (mgcol/find-one collection { :language "Clojure" } {:data-store 0 :_id 0})]
|
||||
(is (nil? (.get loaded "data-store")))
|
||||
(is (nil? (.get loaded "_id")))
|
||||
(is (nil? (monger.util/get-id loaded)))
|
||||
(is (= "Clojure" (.get loaded "language")))))
|
||||
|
||||
|
||||
(deftest find-one-partial-document-as-map-when-collection-has-matches
|
||||
(let [collection "docs"
|
||||
doc-id (monger.util/random-uuid)
|
||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }
|
||||
fields [:data-store]]
|
||||
doc { :data-store "MongoDB", :language "Clojure", :_id doc-id }]
|
||||
(mgcol/insert collection doc)
|
||||
(is (= { :data-store "MongoDB", :_id doc-id } (mgcol/find-one-as-map collection { :language "Clojure" } fields)))))
|
||||
(is (= { :data-store "MongoDB", :_id doc-id } (mgcol/find-one-as-map collection { :language "Clojure" } [:data-store])))))
|
||||
|
||||
|
||||
(deftest find-one-partial-document-as-map-when-collection-has-matches-with-keywordize
|
||||
|
|
|
|||
Loading…
Reference in a new issue