From c25609a5c354e967ec2d64e72b2365f16ac23300 Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Wed, 28 Dec 2011 19:46:29 +0400 Subject: [PATCH 01/21] monger.conversion/to-object-id --- src/monger/conversion.clj | 26 +++++++++++++++++++++++--- test/monger/test/conversion.clj | 13 ++++++++++++- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/monger/conversion.clj b/src/monger/conversion.clj index 7654e61..dd646f5 100644 --- a/src/monger/conversion.clj +++ b/src/monger/conversion.clj @@ -22,9 +22,10 @@ ;; THE SOFTWARE. (ns monger.conversion - (:import (com.mongodb DBObject BasicDBObject BasicDBList DBCursor) - (clojure.lang IPersistentMap Keyword) - (java.util List Map))) + (:import [com.mongodb DBObject BasicDBObject BasicDBList DBCursor] + [clojure.lang IPersistentMap Keyword] + [java.util List Map Date] + [org.bson.types ObjectId])) (defprotocol ConvertToDBObject (to-db-object [input] "Converts given piece of Clojure data to BasicDBObject MongoDB Java driver uses")) @@ -101,3 +102,22 @@ (assoc m k (from-db-object v false)))) {} (reverse pairs))) + + +(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.")) + +(extend-protocol ConvertToObjectId + String + (to-object-id [^String input] + (ObjectId. input)) + + Date + (to-object-id [^Date input] + (ObjectId. input)) + + ObjectId + (to-object-id [^ObjectId input] + input)) + + diff --git a/test/monger/test/conversion.clj b/test/monger/test/conversion.clj index d27fac8..2d15643 100644 --- a/test/monger/test/conversion.clj +++ b/test/monger/test/conversion.clj @@ -2,7 +2,8 @@ (:require [monger core collection] [monger.conversion :as cnv]) (:import [com.mongodb DBObject BasicDBObject BasicDBList] - [java.util Date Calendar List ArrayList]) + [java.util Date Calendar List ArrayList] + [org.bson.types ObjectId]) (:use [clojure.test])) @@ -131,3 +132,13 @@ (is (= (-> output (get "nested") (get "list")) ["red" "green" "blue"])) (is (= (-> output (get "nested") (get "dblist")) [0 1])))) + + +;; +;; ObjectId coercion +;; + +(deftest test-conversion-to-object-id + (let [output (ObjectId. "4efb39370364238a81020502")] + (is (= output (cnv/to-object-id "4efb39370364238a81020502"))) + (is (= output (cnv/to-object-id output))))) From ef3c224441183bc25d1c3b20aada54ca651e82e0 Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Fri, 30 Dec 2011 17:10:36 +0400 Subject: [PATCH 02/21] Fix these early tests to use locals --- test/monger/test/collection.clj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/monger/test/collection.clj b/test/monger/test/collection.clj index 2e7f4c0..e25dfa2 100644 --- a/test/monger/test/collection.clj +++ b/test/monger/test/collection.clj @@ -130,9 +130,9 @@ (deftest find-one-full-document-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) - (def ^DBObject found-one (mgcol/find-one collection { :language "Clojure" })) + doc { :data-store "MongoDB", :language "Clojure", :_id doc-id } + _ (mgcol/insert collection doc) + found-one (mgcol/find-one collection { :language "Clojure" })] (is (= (:_id doc) (monger.util/get-id found-one))) (is (= (mgcnv/from-db-object found-one true) doc)) (is (= (mgcnv/to-db-object doc) found-one)))) @@ -150,9 +150,9 @@ (let [collection "docs" doc-id (monger.util/random-uuid) doc { :data-store "MongoDB", :language "Clojure", :_id doc-id } - fields [:language]] - (mgcol/insert collection doc) - (def ^DBObject loaded (mgcol/find-one collection { :language "Clojure" } fields)) + fields [:language] + _ (mgcol/insert collection doc) + loaded (mgcol/find-one collection { :language "Clojure" } fields)] (is (nil? (.get ^DBObject loaded "data-store"))) (is (= doc-id (monger.util/get-id loaded))) (is (= "Clojure" (.get ^DBObject loaded "language"))))) From 1cb024dcad25e2664c96eb936383bd9ae9ed8db4 Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Fri, 30 Dec 2011 17:19:44 +0400 Subject: [PATCH 03/21] Add read preference support for monger.query DSL --- src/monger/query.clj | 15 ++++++++++----- test/monger/test/querying.clj | 5 +++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/monger/query.clj b/src/monger/query.clj index cdc9153..916b7de 100644 --- a/src/monger/query.clj +++ b/src/monger/query.clj @@ -2,7 +2,7 @@ (:refer-clojure :exclude [select find sort]) (:require [monger.core] [monger.internal pagination]) - (:import [com.mongodb DB DBCollection DBObject DBCursor] + (:import [com.mongodb DB DBCollection DBObject DBCursor ReadPreference] [java.util List]) (:use [monger conversion operators])) @@ -52,16 +52,17 @@ (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 } }] + [{ :keys [collection query fields skip limit sort batch-size hint snapshot read-preference] :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 + (.hint ^DBObject (to-db-object hint)))] + (when snapshot (.snapshot cursor)) + (when read-preference + (.setReadPreference cursor read-preference)) (map (fn [x] (from-db-object x true)) (seq cursor)))) @@ -101,6 +102,10 @@ [m] (merge m { :snapshot true })) +(defn read-preference + [m ^ReadPreference rp] + (merge m { :read-preference rp })) + (defn paginate [m & { :keys [page per-page] :or { page 1 per-page 10 } }] (merge m { :limit per-page :skip (monger.internal.pagination/offset-for page per-page) })) diff --git a/test/monger/test/querying.clj b/test/monger/test/querying.clj index d90b21b..1e12522 100644 --- a/test/monger/test/querying.clj +++ b/test/monger/test/querying.clj @@ -2,7 +2,7 @@ (ns monger.test.querying (:refer-clojure :exclude [select find sort]) - (:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure] + (:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure ReadPreference] [org.bson.types ObjectId] [java.util Date]) (:require [monger core util] @@ -189,7 +189,8 @@ result1 (with-collection coll (find {}) (paginate :page 1 :per-page 3) - (sort { :title 1 })) + (sort { :title 1 }) + (read-preference ReadPreference/PRIMARY)) result2 (with-collection coll (find {}) (paginate :page 2 :per-page 3) From 5bbac64801be8e8146d1a730dca5d7a76ceceb4e Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 03:23:52 +0400 Subject: [PATCH 04/21] Query DSL composition example --- test/monger/test/querying.clj | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/test/monger/test/querying.clj b/test/monger/test/querying.clj index 1e12522..44d5caa 100644 --- a/test/monger/test/querying.clj +++ b/test/monger/test/querying.clj @@ -106,8 +106,22 @@ (find { :inception_year { "$gt" 2002 } }) (limit 1) (sort { :inception_year -1 }))) - (doc1 (with-collection coll - (find { :inception_year { "$gte" 2006 } })))))) + (doc1 (with-collection coll + (find { :inception_year { "$gte" 2006 } })))))) + + +(deftest query-using-$gt-$lt-$gte-$lte-operators-using-dsl-composition + (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 } + srt (-> {} + (limit 1) + (sort { :inception_year -1 })) + _ (mgcol/insert-batch coll [doc1 doc2 doc3])] + (is (= [doc1] (with-collection coll + (find { :inception_year { "$gt" 2002 } }) + (merge srt)))))) ;; $all From 8300b7c0c48ff1802f87df6735f0928d7e62cfa6 Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 04:24:27 +0400 Subject: [PATCH 05/21] Eliminate compiler warning --- src/monger/collection.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/monger/collection.clj b/src/monger/collection.clj index 72ab2f9..ac73457 100644 --- a/src/monger/collection.clj +++ b/src/monger/collection.clj @@ -8,7 +8,7 @@ ;; You must not remove this notice, or any other, from this software. (ns monger.collection - (:refer-clojure :exclude [find remove count drop distinct]) + (:refer-clojure :exclude [find remove count drop distinct empty?]) (:import [com.mongodb Mongo DB DBCollection WriteResult DBObject WriteConcern DBCursor MapReduceCommand MapReduceCommand$OutputType] [java.util List Map] [clojure.lang IPersistentMap ISeq]) From f3efbcec1690762e159467cf4bd4d03cd7322987 Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 04:25:26 +0400 Subject: [PATCH 06/21] One more query DSL composition example, introduce monger.query/partial-query --- src/monger/query.clj | 29 +++++++++++++++++------------ test/monger/test/querying.clj | 17 +++++++++++++++++ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/monger/query.clj b/src/monger/query.clj index 916b7de..2b0092a 100644 --- a/src/monger/query.clj +++ b/src/monger/query.clj @@ -34,18 +34,19 @@ ;; deleted during the query, it may or may not be returned, even with snapshot mode). Note that short query responses ;; (less than 1MB) are always effectively snapshotted. Currently, snapshot mode may not be used with sorting or explicit hints. (defn empty-query - [^DBCollection coll] - { - :collection coll - :query {} - :sort {} - :fields [] - :skip 0 - :limit 0 - :batch-size 256 - :hint nil - :snapshot false - }) + ([] + { + :query {} + :sort {} + :fields [] + :skip 0 + :limit 0 + :batch-size 256 + :hint nil + :snapshot false + }) + ([^DBCollection coll] + (merge (empty-query) { :collection coll }))) (defn- fields-to-db-object [^List fields] @@ -117,3 +118,7 @@ ~coll)] (let [query# (-> (empty-query *query-collection*) ~@body)] (exec query#)))) + +(defmacro partial-query + [& body] + `(-> {} ~@body)) diff --git a/test/monger/test/querying.clj b/test/monger/test/querying.clj index 44d5caa..494f740 100644 --- a/test/monger/test/querying.clj +++ b/test/monger/test/querying.clj @@ -216,3 +216,20 @@ (is (= [doc1 doc5 doc7] result1)) (is (= [doc2 doc6 doc4] result2)) (is (= [doc3] result3)))) + + +(deftest combined-querying-dsl-example1 + (let [coll "docs" + ma-doc { :_id (ObjectId.) :name "Massachusetts" :iso "MA" :population 6547629 :joined_in 1788 :capital "Boston" } + de-doc { :_id (ObjectId.) :name "Delaware" :iso "DE" :population 897934 :joined_in 1787 :capital "Dover" } + ny-doc { :_id (ObjectId.) :name "New York" :iso "NY" :population 19378102 :joined_in 1788 :capital "Albany" } + ca-doc { :_id (ObjectId.) :name "California" :iso "CA" :population 37253956 :joined_in 1850 :capital "Sacramento" } + tx-doc { :_id (ObjectId.) :name "Texas" :iso "TX" :population 25145561 :joined_in 1845 :capital "Austin" } + top3 (partial-query (limit 3)) + by-population-desc (partial-query (sort { :population -1 })) + _ (mgcol/insert-batch coll [ma-doc de-doc ny-doc ca-doc tx-doc]) + result (with-collection coll + (find {}) + (merge top3) + (merge by-population-desc))] + (is (= result [ca-doc tx-doc ny-doc])))) From fbe9820c9b14a6aeec0c7a3d4ab7a46c824d1d65 Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 04:39:58 +0400 Subject: [PATCH 07/21] Brush up the README [ci skip] --- README.md | 47 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fe1de09..e8b6904 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Monger -Monger is an idiomatic Clojure wrapper around MongoDB Java driver. +Monger is an idiomatic Clojure wrapper around MongoDB Java driver. It offers powerful expressive query DSL, strives to support +every MongoDB 2.0+ feature and is well maintained. ## Project Goals @@ -17,11 +18,18 @@ wanted a client that will * Learn from other clients like the Java and Ruby ones. * Target Clojure 1.3.0 and later from the ground up. + ## Usage -We are working on documentation guides & examples site for the 1.0 release. Please refer to the test suite for code examples. +We are working on documentation guides & examples site for the 1.0 release. In the meantime, please refer to the [test suite](https://github.com/michaelklishin/monger/tree/master/test/monger/test) for code examples. -Here is what monger.query DSL looks like right now: + +## Powerful Query DSL + +Every application that works with data stores has to query them. As a consequence, having an expressive powerful query DSL is a must +for client libraries like Monger. + +Here is what monger.query DSL feels like: ``` clojure (with-collection "docs" @@ -34,7 +42,32 @@ Here is what monger.query DSL looks like right now: (snapshot)) ``` -More code examples can be found in our test suite. +It is easy to add new DSL elements, for example, adding pagination took literally less than 10 lines of Clojure code. Here is what +it looks like: + +``` clojure +(with-collection coll + (find {}) + (paginate :page 1 :per-page 3) + (sort { :title 1 }) + (read-preference ReadPreference/PRIMARY)) +``` + +Query DSL supports composition, too: + +``` clojure +(let + [top3 (partial-query (limit 3)) + by-population-desc (partial-query (sort { :population -1 })) + result (with-collection coll + (find {}) + (merge top3) + (merge by-population-desc))] + ;; ... + ) +``` + +More code examples can be found [in our test suite](https://github.com/michaelklishin/monger/tree/master/test/monger/test). ## This is a Work In Progress @@ -48,7 +81,7 @@ Snapshot artifacts are [released to Clojars](https://clojars.org/com.novemberain With Leiningen: - [com.novemberain/monger "0.11.0-SNAPSHOT"] + [com.novemberain/monger "1.0.0-SNAPSHOT"] With Maven: @@ -56,7 +89,7 @@ With Maven: com.novemberain monger - 0.11.0-SNAPSHOT + 1.0.0-SNAPSHOT @@ -65,7 +98,7 @@ With Maven: [![Continuous Integration status](https://secure.travis-ci.org/michaelklishin/monger.png)](http://travis-ci.org/michaelklishin/monger) -CI is hosted by [travis-ci.org](http://travis-ci.org) +CI is hosted by [travis-ci.org](http://travis-ci.org). From 68135eebb4eae11bd4c8307c5c70a826ef1a427c Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 04:42:00 +0400 Subject: [PATCH 08/21] Promises, promises [ci skip] --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e8b6904..4f21e04 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,8 @@ More code examples can be found [in our test suite](https://github.com/michaelkl ## This is a Work In Progress -Core Monger APIs are stabilized but it is still a work in progress. Keep that in mind. 1.0 will be released in late 2011. +Core Monger APIs are stabilized but it is still a work in progress. Keep that in mind. 1.0 will be released in early 2012 +together with documentation guides and dedicated website. ## Artifacts From 3c813132d88c02c90167a67214a674fec38daf97 Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 06:44:34 +0400 Subject: [PATCH 09/21] Return type of monger.conversion/to-db-object is always the same, hint it --- src/monger/conversion.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/monger/conversion.clj b/src/monger/conversion.clj index dd646f5..8d09592 100644 --- a/src/monger/conversion.clj +++ b/src/monger/conversion.clj @@ -28,7 +28,7 @@ [org.bson.types ObjectId])) (defprotocol ConvertToDBObject - (to-db-object [input] "Converts given piece of Clojure data to BasicDBObject MongoDB Java driver uses")) + (^DBObject to-db-object [input] "Converts given piece of Clojure data to BasicDBObject MongoDB Java driver uses")) (extend-protocol ConvertToDBObject nil From eab4405012f801f8da590c6c48ffb6d900555904 Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 06:52:51 +0400 Subject: [PATCH 10/21] Initial bits of monger.gridfs --- src/monger/conversion.clj | 2 +- src/monger/gridfs.clj | 77 ++++++++++++++++++++++++++++ test/monger/test/gridfs.clj | 42 +++++++++++++++ test/monger/test/lib_integration.clj | 3 +- 4 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 src/monger/gridfs.clj create mode 100644 test/monger/test/gridfs.clj diff --git a/src/monger/conversion.clj b/src/monger/conversion.clj index 8d09592..dd646f5 100644 --- a/src/monger/conversion.clj +++ b/src/monger/conversion.clj @@ -28,7 +28,7 @@ [org.bson.types ObjectId])) (defprotocol ConvertToDBObject - (^DBObject to-db-object [input] "Converts given piece of Clojure data to BasicDBObject MongoDB Java driver uses")) + (to-db-object [input] "Converts given piece of Clojure data to BasicDBObject MongoDB Java driver uses")) (extend-protocol ConvertToDBObject nil diff --git a/src/monger/gridfs.clj b/src/monger/gridfs.clj new file mode 100644 index 0000000..75fcd06 --- /dev/null +++ b/src/monger/gridfs.clj @@ -0,0 +1,77 @@ +(ns monger.gridfs + (:refer-clojure :exclude [remove]) + (:require [monger.core] + [clojure.java.io :as io]) + (:use [monger.conversion]) + (:import [com.mongodb DBObject] + [com.mongodb.gridfs GridFS GridFSInputFile])) + +;; +;; Implementation +;; + +(def + ^{:doc "Type object for a Java primitive byte array." + :private true + } + byte-array-type (class (make-array Byte/TYPE 0))) + +(def ^:dynamic *chunk-size* (* 2 1024 1024)) + +(defn filename + [m ^String s] + (merge m { :filename s })) + +(defn content-type + [m ^String s] + (merge m { :content-type s })) + +(defn- exec + [{ :keys [input filename content-type] }] + ) + +;; ... + + + +;; +;; API +;; + + +(defn remove + ([] + (remove {})) + ([query] + (.remove ^GridFS monger.core/*mongodb-gridfs* ^DBObject (to-db-object query)))) + +(defn remove-all + [] + (remove {})) + +(defn all-files + ([] + (.getFileList ^GridFS monger.core/*mongodb-gridfs*)) + ([query] + (.getFileList ^GridFS monger.core/*mongodb-gridfs* query))) + + +(defprotocol GridFSInputFileFactory + (^GridFSInputFile make-input-file [input] "Makes GridFSInputFile out of given input")) + +(extend byte-array-type + GridFSInputFileFactory + { :make-input-file (fn [^bytes input] + (.createFile ^GridFS monger.core/*mongodb-gridfs* input)) }) + +(extend-protocol GridFSInputFileFactory + String + (make-input-file [^String input] + (.createFile ^GridFS monger.core/*mongodb-gridfs* (.getBytes input)))) + + + + +(defmacro store + [^GridFSInputFile input & body] + `(.save ^GridFSInputFile (doto ~input ~@body) *chunk-size*)) diff --git a/test/monger/test/gridfs.clj b/test/monger/test/gridfs.clj new file mode 100644 index 0000000..cda1f0a --- /dev/null +++ b/test/monger/test/gridfs.clj @@ -0,0 +1,42 @@ +(ns monger.test.gridfs + (:refer-clojure :exclude [count remove find]) + (:use [clojure.test] + [monger.core :only [count]] + [monger.test.fixtures] + [monger.operators] + [monger.gridfs :only (store make-input-file)]) + (:require [monger.gridfs :as gridfs] + [monger.test.helper :as helper] + [clojure.java.io :as io]) + (:import [java.io InputStream] + [com.mongodb.gridfs GridFS GridFSInputFile])) + + +(defn purge-gridfs + [f] + (gridfs/remove-all) + (f) + (gridfs/remove-all)) + +(use-fixtures :each purge-gridfs) + +(helper/connect!) + + + +(deftest test-storing-strings-to-gridfs + (let [input "A string"] + (is (= 0 (count (gridfs/all-files)))) + (gridfs/store (gridfs/make-input-file input) + (.setFilename "monger.test.gridfs.file1") + (.setContentType "application/octet-stream")) + (is (= 1 (count (gridfs/all-files)))))) + + +(deftest test-storing-bytes-to-gridfs + (let [input (.getBytes "A string")] + (is (= 0 (count (gridfs/all-files)))) + (store (make-input-file input) + (.setFilename "monger.test.gridfs.file1") + (.setContentType "application/octet-stream")) + (is (= 1 (count (gridfs/all-files)))))) diff --git a/test/monger/test/lib_integration.clj b/test/monger/test/lib_integration.clj index 891165c..297770a 100644 --- a/test/monger/test/lib_integration.clj +++ b/test/monger/test/lib_integration.clj @@ -6,7 +6,8 @@ (:import [org.joda.time DateTime ReadableInstant] [org.joda.time.format ISODateTimeFormat] [java.io StringWriter PrintWriter] - [org.bson.types ObjectId]) + [org.bson.types ObjectId] + [com.mongodb DBObject]) (:require [clojure.data.json :as json] [clj-time.core :as t])) From d0f2cbc73719b3a5ea9eb586607cf04fce0c2880 Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 06:57:29 +0400 Subject: [PATCH 11/21] Add monger.core/*mongodb-gridfs* and related with-* macro. Blow the dust off monger.core along the way. --- src/monger/core.clj | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/monger/core.clj b/src/monger/core.clj index 697d3e8..151d42a 100644 --- a/src/monger/core.clj +++ b/src/monger/core.clj @@ -14,8 +14,9 @@ monger.core (:refer-clojure :exclude [count]) (:use [monger.conversion]) - (:import (com.mongodb Mongo DB WriteConcern DBObject DBCursor) - (java.util Map))) + (:import [com.mongodb Mongo DB WriteConcern DBObject DBCursor CommandResult] + [com.mongodb.gridfs GridFS] + [java.util Map])) ;; ;; Defaults @@ -28,6 +29,7 @@ (declare ^:dynamic ^DB *mongodb-database*) (def ^:dynamic ^WriteConcern *mongodb-write-concern* WriteConcern/SAFE) +(declare ^:dynamic ^GridFS *mongodb-gridfs*) ;; ;; API @@ -75,33 +77,37 @@ `(binding [*mongodb-database* ~db] (do ~@body))) +(defmacro with-gridfs + [fs & body] + `(binding [*mongodb-gridfs* ~fs] + (do ~@body))) + (defn connect! - "Connect to MongoDB, save connection to *mongodb-connection* dynamic variable" + "Connect to MongoDB, store connection in the *mongodb-connection* var" ^Mongo [& args] (def ^:dynamic *mongodb-connection* (apply connect args))) (defn set-db! - "Set dynamic *mongodb-database* variable to given :db" + "Sets *mongodb-database* var to given db, updates *mongodb-gridfs* var state. Recommended to be used for + applications that only use one database." [db] - (def ^:dynamic *mongodb-database* db)) + (def ^:dynamic *mongodb-database* db) + (def ^:dynamic *mongodb-gridfs* (GridFS. db))) (defn set-default-write-concern! [wc] - "Set dynamic *mongodb-write-concert* to :wc + "Set *mongodb-write-concert* var to :wc - We recommend to use WriteConcern/SAFE by default to make sure your data was written." + Unlike the official Java driver, Monger uses WriteConcern/SAFE by default. We think defaults should be safe first + and WebScale fast second." (def ^:dynamic *mongodb-write-concern* wc)) -(defn command - "Available commands (please check MongoDB documentation for a complete list of commands for particular DB version. - Returns CommandResult. - Use (.ok result) to get response status. - It implements AbstractMap interface, so you can access it's internals: - - (get (monger.core/command { :collstats \"things\") \"ns\")) ;; => monger-test.things +(defn ^CommandResult command + "Runs a database command (please check MongoDB documentation for the complete list of commands). Some common commands + are: { :buildinfo 1 } returns version number and build information about the current MongoDB server, should be executed via admin DB. @@ -162,7 +168,7 @@ (extend-protocol Countable DBCursor - (count [^com.mongodb.DBCursor this] + (count [^DBCursor this] (.count this))) (defn ^DBObject get-last-error @@ -184,5 +190,3 @@ (.getLastError ^DB database w wtimeout fsync)) ([^DB database ^WriteConcern write-concern] (.getLastError ^DB database write-concern))) - - From b649c7977e360bd24193caa40774bb1b091da00a Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 06:57:34 +0400 Subject: [PATCH 12/21] Cosmetics --- test/monger/test/atomic_modifiers.clj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/monger/test/atomic_modifiers.clj b/test/monger/test/atomic_modifiers.clj index d9bcd20..165a782 100644 --- a/test/monger/test/atomic_modifiers.clj +++ b/test/monger/test/atomic_modifiers.clj @@ -3,9 +3,7 @@ (ns monger.test.atomic-modifiers (:import [com.mongodb WriteResult WriteConcern DBCursor DBObject CommandResult$CommandFailure] [org.bson.types ObjectId] - [java.util Date] - - ) + [java.util Date]) (:require [monger core util] [monger.collection :as mgcol] [monger.result :as mgres] From bb806d5c0f7e160d94ff406f4426cfc2c2fb210b Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 07:03:15 +0400 Subject: [PATCH 13/21] Remove unused DSL support functions --- src/monger/gridfs.clj | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/monger/gridfs.clj b/src/monger/gridfs.clj index 75fcd06..991ae44 100644 --- a/src/monger/gridfs.clj +++ b/src/monger/gridfs.clj @@ -18,18 +18,6 @@ (def ^:dynamic *chunk-size* (* 2 1024 1024)) -(defn filename - [m ^String s] - (merge m { :filename s })) - -(defn content-type - [m ^String s] - (merge m { :content-type s })) - -(defn- exec - [{ :keys [input filename content-type] }] - ) - ;; ... From ced9b501539e1dcd5b5618a2e1d266e05f4cf23b Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 14:57:17 +0400 Subject: [PATCH 14/21] Make monger.gridfs.IOFactory implementation for String to work the same way as clojure.java.io/IOFactory one does It treats string arguments as local filesystem paths --- src/monger/gridfs.clj | 5 +++-- test/monger/test/gridfs.clj | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/monger/gridfs.clj b/src/monger/gridfs.clj index 991ae44..bfb1d73 100644 --- a/src/monger/gridfs.clj +++ b/src/monger/gridfs.clj @@ -4,7 +4,8 @@ [clojure.java.io :as io]) (:use [monger.conversion]) (:import [com.mongodb DBObject] - [com.mongodb.gridfs GridFS GridFSInputFile])) + [com.mongodb.gridfs GridFS GridFSInputFile] + [java.io InputStream])) ;; ;; Implementation @@ -55,7 +56,7 @@ (extend-protocol GridFSInputFileFactory String (make-input-file [^String input] - (.createFile ^GridFS monger.core/*mongodb-gridfs* (.getBytes input)))) + (.createFile ^GridFS monger.core/*mongodb-gridfs* ^InputStream (io/make-input-stream input { :encoding "UTF-8" })))) diff --git a/test/monger/test/gridfs.clj b/test/monger/test/gridfs.clj index cda1f0a..668ab33 100644 --- a/test/monger/test/gridfs.clj +++ b/test/monger/test/gridfs.clj @@ -24,8 +24,8 @@ -(deftest test-storing-strings-to-gridfs - (let [input "A string"] +(deftest test-storing-files-to-gridfs-using-relative-fs-paths + (let [input "./test/resources/mongo/js/mapfun1.js"] (is (= 0 (count (gridfs/all-files)))) (gridfs/store (gridfs/make-input-file input) (.setFilename "monger.test.gridfs.file1") @@ -37,6 +37,6 @@ (let [input (.getBytes "A string")] (is (= 0 (count (gridfs/all-files)))) (store (make-input-file input) - (.setFilename "monger.test.gridfs.file1") + (.setFilename "monger.test.gridfs.file2") (.setContentType "application/octet-stream")) (is (= 1 (count (gridfs/all-files)))))) From 7da99d98a32c2ba03a25d6650e17e9b29fc6cc5b Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 15:01:00 +0400 Subject: [PATCH 15/21] Implement monger.gridfs.GridFSInputFileFactory for java.io.File instances --- src/monger/gridfs.clj | 6 +++++- test/monger/test/gridfs.clj | 10 +++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/monger/gridfs.clj b/src/monger/gridfs.clj index bfb1d73..e097d82 100644 --- a/src/monger/gridfs.clj +++ b/src/monger/gridfs.clj @@ -5,7 +5,7 @@ (:use [monger.conversion]) (:import [com.mongodb DBObject] [com.mongodb.gridfs GridFS GridFSInputFile] - [java.io InputStream])) + [java.io InputStream File])) ;; ;; Implementation @@ -56,6 +56,10 @@ (extend-protocol GridFSInputFileFactory String (make-input-file [^String input] + (.createFile ^GridFS monger.core/*mongodb-gridfs* ^InputStream (io/make-input-stream input { :encoding "UTF-8" }))) + + File + (make-input-file [^File input] (.createFile ^GridFS monger.core/*mongodb-gridfs* ^InputStream (io/make-input-stream input { :encoding "UTF-8" })))) diff --git a/test/monger/test/gridfs.clj b/test/monger/test/gridfs.clj index 668ab33..37f023d 100644 --- a/test/monger/test/gridfs.clj +++ b/test/monger/test/gridfs.clj @@ -33,10 +33,18 @@ (is (= 1 (count (gridfs/all-files)))))) +(deftest test-storing-files-to-gridfs-using-file-instances + (let [input (io/as-file "./test/resources/mongo/js/mapfun1.js")] + (is (= 0 (count (gridfs/all-files)))) + (gridfs/store (gridfs/make-input-file input) + (.setFilename "monger.test.gridfs.file2") + (.setContentType "application/octet-stream")) + (is (= 1 (count (gridfs/all-files)))))) + (deftest test-storing-bytes-to-gridfs (let [input (.getBytes "A string")] (is (= 0 (count (gridfs/all-files)))) (store (make-input-file input) - (.setFilename "monger.test.gridfs.file2") + (.setFilename "monger.test.gridfs.file3") (.setContentType "application/octet-stream")) (is (= 1 (count (gridfs/all-files)))))) From 7eec647a976127fab237751d04df589e6e550c1e Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 15:02:21 +0400 Subject: [PATCH 16/21] Use unqualified function names for what we :use --- test/monger/test/gridfs.clj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/monger/test/gridfs.clj b/test/monger/test/gridfs.clj index 37f023d..95cc4dc 100644 --- a/test/monger/test/gridfs.clj +++ b/test/monger/test/gridfs.clj @@ -27,18 +27,18 @@ (deftest test-storing-files-to-gridfs-using-relative-fs-paths (let [input "./test/resources/mongo/js/mapfun1.js"] (is (= 0 (count (gridfs/all-files)))) - (gridfs/store (gridfs/make-input-file input) - (.setFilename "monger.test.gridfs.file1") - (.setContentType "application/octet-stream")) + (store (make-input-file input) + (.setFilename "monger.test.gridfs.file1") + (.setContentType "application/octet-stream")) (is (= 1 (count (gridfs/all-files)))))) (deftest test-storing-files-to-gridfs-using-file-instances (let [input (io/as-file "./test/resources/mongo/js/mapfun1.js")] (is (= 0 (count (gridfs/all-files)))) - (gridfs/store (gridfs/make-input-file input) - (.setFilename "monger.test.gridfs.file2") - (.setContentType "application/octet-stream")) + (store (make-input-file input) + (.setFilename "monger.test.gridfs.file2") + (.setContentType "application/octet-stream")) (is (= 1 (count (gridfs/all-files)))))) (deftest test-storing-bytes-to-gridfs From a292f082e82fa567ccb29976394c87a3d14e0c8b Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 16:23:49 +0400 Subject: [PATCH 17/21] Implement monger.gridfs/find-one and /find-one-as-map --- src/monger/gridfs.clj | 33 ++++++++++++++++++++++++++++++--- test/monger/test/gridfs.clj | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/src/monger/gridfs.clj b/src/monger/gridfs.clj index e097d82..bf532de 100644 --- a/src/monger/gridfs.clj +++ b/src/monger/gridfs.clj @@ -63,8 +63,35 @@ (.createFile ^GridFS monger.core/*mongodb-gridfs* ^InputStream (io/make-input-stream input { :encoding "UTF-8" })))) - - (defmacro store [^GridFSInputFile input & body] - `(.save ^GridFSInputFile (doto ~input ~@body) *chunk-size*)) + `(let [^GridFSInputFile f# (doto ~input ~@body)] + (.save f# *chunk-size*) + (from-db-object f# true))) + + +(defprotocol Finders + (find-one [input] "Finds one file using given input (an ObjectId, filename or query)") + (find-one-as-map [input] "Finds one file using given input (an ObjectId, filename or query), converting result to Clojure map before returning")) + +(extend-protocol Finders + String + (find-one [^String input] + (.findOne ^GridFS monger.core/*mongodb-gridfs* input)) + (find-one-as-map [^String input] + (from-db-object (find-one input) true)) + + org.bson.types.ObjectId + (find-one [^org.bson.types.ObjectId input] + (.findOne ^GridFS monger.core/*mongodb-gridfs* input)) + (find-one-as-map [^org.bson.types.ObjectId input] + (from-db-object (find-one input) true)) + + + DBObject + (find-one [^DBObject input] + (.findOne ^GridFS monger.core/*mongodb-gridfs* input)) + (find-one-as-map [^DBObject input] + (from-db-object (find-one input) true))) + + diff --git a/test/monger/test/gridfs.clj b/test/monger/test/gridfs.clj index 95cc4dc..de08d83 100644 --- a/test/monger/test/gridfs.clj +++ b/test/monger/test/gridfs.clj @@ -3,12 +3,12 @@ (:use [clojure.test] [monger.core :only [count]] [monger.test.fixtures] - [monger.operators] + [monger operators conversion] [monger.gridfs :only (store make-input-file)]) (:require [monger.gridfs :as gridfs] [monger.test.helper :as helper] [clojure.java.io :as io]) - (:import [java.io InputStream] + (:import [java.io InputStream File] [com.mongodb.gridfs GridFS GridFSInputFile])) @@ -48,3 +48,33 @@ (.setFilename "monger.test.gridfs.file3") (.setContentType "application/octet-stream")) (is (= 1 (count (gridfs/all-files)))))) + +(deftest test-storing-files-to-gridfs-using-absolute-fs-paths + (let [tmp-file (File/createTempFile "monger.test.gridfs" "test-storing-files-to-gridfs-using-absolute-fs-paths") + _ (spit tmp-file "Some content") + input (.getAbsolutePath tmp-file)] + (is (= 0 (count (gridfs/all-files)))) + (store (make-input-file input) + (.setFilename "monger.test.gridfs.file4") + (.setContentType "application/octet-stream")) + (is (= 1 (count (gridfs/all-files)))))) + +(deftest test-finding-individual-files-on-gridfs + (let [input "./test/resources/mongo/js/mapfun1.js" + ct "binary/octet-stream" + filename "monger.test.gridfs.file5" + md5 "14a09deabb50925a3381315149017bbd" + stored (store (make-input-file input) + (.setFilename filename) + (.setContentType ct))] + (is (= 1 (count (gridfs/all-files)))) + (is (:_id stored)) + (is (:uploadDate stored)) + (is (= 62 (:length stored))) + (is (= md5 (:md5 stored))) + (is (= filename (:filename stored))) + (is (= ct (:contentType stored))) + (are [a b] (is (= a (:md5 (gridfs/find-one-as-map b)))) + md5 (:_id stored) + md5 filename + md5 (to-db-object { :md5 md5 })))) From cb9ed8edb551e49ded1434e428cd578de1c02bc8 Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 16:27:56 +0400 Subject: [PATCH 18/21] monger.gridfs/find-one-as-map is next to useless, remove it It does not allow you access file content stream. --- src/monger/gridfs.clj | 11 ++--------- test/monger/test/gridfs.clj | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/monger/gridfs.clj b/src/monger/gridfs.clj index bf532de..34a9c6b 100644 --- a/src/monger/gridfs.clj +++ b/src/monger/gridfs.clj @@ -71,27 +71,20 @@ (defprotocol Finders - (find-one [input] "Finds one file using given input (an ObjectId, filename or query)") - (find-one-as-map [input] "Finds one file using given input (an ObjectId, filename or query), converting result to Clojure map before returning")) + (find-one [input] "Finds one file using given input (an ObjectId, filename or query)")) (extend-protocol Finders String (find-one [^String input] (.findOne ^GridFS monger.core/*mongodb-gridfs* input)) - (find-one-as-map [^String input] - (from-db-object (find-one input) true)) org.bson.types.ObjectId (find-one [^org.bson.types.ObjectId input] (.findOne ^GridFS monger.core/*mongodb-gridfs* input)) - (find-one-as-map [^org.bson.types.ObjectId input] - (from-db-object (find-one input) true)) DBObject (find-one [^DBObject input] - (.findOne ^GridFS monger.core/*mongodb-gridfs* input)) - (find-one-as-map [^DBObject input] - (from-db-object (find-one input) true))) + (.findOne ^GridFS monger.core/*mongodb-gridfs* input))) diff --git a/test/monger/test/gridfs.clj b/test/monger/test/gridfs.clj index de08d83..fdb26d0 100644 --- a/test/monger/test/gridfs.clj +++ b/test/monger/test/gridfs.clj @@ -74,7 +74,7 @@ (is (= md5 (:md5 stored))) (is (= filename (:filename stored))) (is (= ct (:contentType stored))) - (are [a b] (is (= a (:md5 (gridfs/find-one-as-map b)))) + (are [a b] (is (= a (:md5 (from-db-object (gridfs/find-one b) true)))) md5 (:_id stored) md5 filename md5 (to-db-object { :md5 md5 })))) From 1187719ac1a4ba340753a885a92d39d16bec86ac Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 16:52:23 +0400 Subject: [PATCH 19/21] Extend monger.gridfs.GridFSInputFileFactory to support input streams --- src/monger/gridfs.clj | 20 +++++++++++--- test/monger/test/gridfs.clj | 54 +++++++++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/src/monger/gridfs.clj b/src/monger/gridfs.clj index 34a9c6b..9f179d3 100644 --- a/src/monger/gridfs.clj +++ b/src/monger/gridfs.clj @@ -1,5 +1,5 @@ (ns monger.gridfs - (:refer-clojure :exclude [remove]) + (:refer-clojure :exclude [remove find]) (:require [monger.core] [clojure.java.io :as io]) (:use [monger.conversion]) @@ -60,7 +60,11 @@ File (make-input-file [^File input] - (.createFile ^GridFS monger.core/*mongodb-gridfs* ^InputStream (io/make-input-stream input { :encoding "UTF-8" })))) + (.createFile ^GridFS monger.core/*mongodb-gridfs* ^InputStream (io/make-input-stream input { :encoding "UTF-8" }))) + + InputStream + (make-input-file [^InputStream input] + (.createFile ^GridFS monger.core/*mongodb-gridfs* ^InputStream input))) (defmacro store @@ -71,10 +75,13 @@ (defprotocol Finders - (find-one [input] "Finds one file using given input (an ObjectId, filename or query)")) + (find [input] "Finds multiple files using given input (an ObjectId, filename or query)") + (find-one [input] "Finds one file using given input (an ObjectId, filename or query)")) (extend-protocol Finders String + (find [^String input] + (vec (.find ^GridFS monger.core/*mongodb-gridfs* input))) (find-one [^String input] (.findOne ^GridFS monger.core/*mongodb-gridfs* input)) @@ -84,7 +91,12 @@ DBObject + (find [^DBObject input] + (vec (.find ^GridFS monger.core/*mongodb-gridfs* input))) (find-one [^DBObject input] - (.findOne ^GridFS monger.core/*mongodb-gridfs* input))) + (.findOne ^GridFS monger.core/*mongodb-gridfs* input)) + clojure.lang.PersistentArrayMap + (find [^clojure.lang.PersistentArrayMap input] + (find (to-db-object input)))) diff --git a/test/monger/test/gridfs.clj b/test/monger/test/gridfs.clj index fdb26d0..20abe06 100644 --- a/test/monger/test/gridfs.clj +++ b/test/monger/test/gridfs.clj @@ -8,8 +8,8 @@ (:require [monger.gridfs :as gridfs] [monger.test.helper :as helper] [clojure.java.io :as io]) - (:import [java.io InputStream File] - [com.mongodb.gridfs GridFS GridFSInputFile])) + (:import [java.io InputStream File FileInputStream] + [com.mongodb.gridfs GridFS GridFSInputFile GridFSDBFile])) (defn purge-gridfs @@ -59,6 +59,17 @@ (.setContentType "application/octet-stream")) (is (= 1 (count (gridfs/all-files)))))) +(deftest test-storing-files-to-gridfs-using-input-stream + (let [tmp-file (File/createTempFile "monger.test.gridfs" "test-storing-files-to-gridfs-using-input-stream") + _ (spit tmp-file "Some other content")] + (is (= 0 (count (gridfs/all-files)))) + (store (make-input-file (FileInputStream. tmp-file)) + (.setFilename "monger.test.gridfs.file4b") + (.setContentType "application/octet-stream")) + (is (= 1 (count (gridfs/all-files)))))) + + + (deftest test-finding-individual-files-on-gridfs (let [input "./test/resources/mongo/js/mapfun1.js" ct "binary/octet-stream" @@ -78,3 +89,42 @@ md5 (:_id stored) md5 filename md5 (to-db-object { :md5 md5 })))) + +(deftest test-finding-multiple-files-on-gridfs + (let [input "./test/resources/mongo/js/mapfun1.js" + ct "binary/octet-stream" + md5 "14a09deabb50925a3381315149017bbd" + stored1 (store (make-input-file input) + (.setFilename "monger.test.gridfs.file6") + (.setContentType ct)) + stored2 (store (make-input-file input) + (.setFilename "monger.test.gridfs.file7") + (.setContentType ct)) + list1 (gridfs/find "monger.test.gridfs.file6") + list2 (gridfs/find "monger.test.gridfs.file7") + list3 (gridfs/find "888000___.monger.test.gridfs.file") + list4 (gridfs/find { :md5 md5 })] + (is (= 2 (count (gridfs/all-files)))) + (are [a b] (is (= (map #(.get ^GridFSDBFile % "_id") a) + (map :_id b))) + list1 [stored1] + list2 [stored2] + list3 [] + list4 [stored1 stored2]))) + + +(deftest test-removing-multiple-files-from-gridfs + (let [input "./test/resources/mongo/js/mapfun1.js" + ct "binary/octet-stream" + md5 "14a09deabb50925a3381315149017bbd" + stored1 (store (make-input-file input) + (.setFilename "monger.test.gridfs.file8") + (.setContentType ct)) + stored2 (store (make-input-file input) + (.setFilename "monger.test.gridfs.file9") + (.setContentType ct))] + (is (= 2 (count (gridfs/all-files)))) + (gridfs/remove { :filename "monger.test.gridfs.file8" }) + (is (= 1 (count (gridfs/all-files)))) + (gridfs/remove { :md5 md5 }) + (is (= 0 (count (gridfs/all-files)))))) From a3d6a66eb1833fc7a0f6e35145032c6f6b828e6f Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Sat, 31 Dec 2011 16:55:32 +0400 Subject: [PATCH 20/21] Use GridFS/DEFAULT_CHUNKSIZE instead of our own default Ruby driver uses 2 MB default chunk size, Java driver uses 256K. Lets use what the Java driver does. --- src/monger/gridfs.clj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/monger/gridfs.clj b/src/monger/gridfs.clj index 9f179d3..8385152 100644 --- a/src/monger/gridfs.clj +++ b/src/monger/gridfs.clj @@ -17,8 +17,6 @@ } byte-array-type (class (make-array Byte/TYPE 0))) -(def ^:dynamic *chunk-size* (* 2 1024 1024)) - ;; ... @@ -70,7 +68,7 @@ (defmacro store [^GridFSInputFile input & body] `(let [^GridFSInputFile f# (doto ~input ~@body)] - (.save f# *chunk-size*) + (.save f# GridFS/DEFAULT_CHUNKSIZE) (from-db-object f# true))) From 12a46ca4ab67eb045d262a4ec458f30c12b7be53 Mon Sep 17 00:00:00 2001 From: "Michael S. Klishin" Date: Wed, 4 Jan 2012 19:56:38 +0400 Subject: [PATCH 21/21] Demonstrate querying with comparison operators ($lt, etc) over date fields and Joda Time integration --- test/monger/test/querying.clj | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/test/monger/test/querying.clj b/test/monger/test/querying.clj index 494f740..621736a 100644 --- a/test/monger/test/querying.clj +++ b/test/monger/test/querying.clj @@ -11,7 +11,8 @@ [monger.test.helper :as helper]) (:use [clojure.test] [monger.test.fixtures] - [monger conversion query operators])) + [monger conversion query operators joda-time] + [clj-time.core :only [date-time]])) (helper/connect!) @@ -77,7 +78,7 @@ ;; < ($lt), <= ($lte), > ($gt), >= ($gte) -(deftest query-using-dsl-and-$lt-operator +(deftest query-using-dsl-and-$lt-operator-with-integers (let [coll "docs" doc1 { :language "Clojure" :_id (ObjectId.) :inception_year 2006 } doc2 { :language "Java" :_id (ObjectId.) :inception_year 1992 } @@ -86,7 +87,21 @@ lt-result (with-collection "docs" (find { :inception_year { $lt 2000 } }) (limit 2))] - (is (= [doc2] lt-result)))) + (is (= [doc2] (vec lt-result))))) + + +(deftest query-using-dsl-and-$lt-operator-with-dates + (let [coll "docs" + ;; these rely on monger.joda-time being loaded. MK. + doc1 { :language "Clojure" :_id (ObjectId.) :inception_year (date-time 2006 1 1) } + doc2 { :language "Java" :_id (ObjectId.) :inception_year (date-time 1992 1 2) } + doc3 { :language "Scala" :_id (ObjectId.) :inception_year (date-time 2003 3 3) } + _ (mgcol/insert-batch coll [doc1 doc2]) + lt-result (with-collection "docs" + (find { :inception_year { $lt (date-time 2000 1 2) } }) + (limit 2))] + (is (= (map :_id [doc2]) + (map :_id (vec lt-result))))))