Added missing fns to multi/collection. Related to #53.

This commit is contained in:
Erik Bakstad 2013-06-13 07:31:43 +02:00
parent 7cc7b3a2d5
commit 0d635721a1
3 changed files with 474 additions and 6 deletions

View file

@ -4,7 +4,7 @@
Use these functions when you need to work with multiple databases or manage database
and connection lifecycle explicitly."
(:refer-clojure :exclude [find remove count])
(:refer-clojure :exclude [find remove count empty? distinct drop])
(:import [com.mongodb Mongo DB DBCollection WriteResult DBObject WriteConcern DBCursor MapReduceCommand MapReduceCommand$OutputType]
[java.util List Map]
[clojure.lang IPersistentMap ISeq]
@ -60,11 +60,13 @@
;;
;; monger.collection/find
;; monger.multi.collection/find
;;
(defn ^DBCursor find
"Like monger.collection/find but always takes a database as explicit argument"
([^DB db ^String collection]
(.find (.getCollection db (name collection))))
([^DB db ^String collection ^Map ref]
(.find (.getCollection db (name collection))
(to-db-object ref)))
@ -75,6 +77,9 @@
(defn find-maps
"Like monger.collection/find-maps but always takes a database as explicit argument"
([^DB db ^String collection]
(with-open [dbc (find db collection)]
(map (fn [x] (from-db-object x true)) dbc)))
([^DB db ^String collection ^Map ref]
(with-open [dbc (find db collection ref)]
(map (fn [x] (from-db-object x true)) dbc)))
@ -82,8 +87,20 @@
(with-open [dbc (find db collection ref fields)]
(map (fn [x] (from-db-object x true)) dbc))))
(defn find-seq
"Like monger.collection/find-seq but always takes a database as explicit argument"
([^DB db ^String collection]
(with-open [dbc (find db collection)]
(seq dbc)))
([^DB db ^String collection ^Map ref]
(with-open [dbc (find db collection ref)]
(seq dbc)))
([^DB db ^String collection ^Map ref fields]
(with-open [dbc (find db collection ref fields)]
(seq dbc))))
;;
;; monger.collection/find-one
;; monger.multi.collection/find-one
;;
(defn ^DBObject find-one
@ -106,7 +123,22 @@
(from-db-object ^DBObject (find-one db collection ref fields) keywordize)))
;;
;; monger.collection/find-by-id
;; monger.multi.collection/find-and-modify
;;
(defn ^DBObject find-and-modify
"Like monger.collection/find-and-modify but always takes a database as explicit argument"
([^DB db ^String collection ^Map conditions ^Map document & {:keys [fields sort remove return-new upsert keywordize] :or
{fields nil sort nil remove false return-new false upsert false keywordize true}}]
(let [coll (.getCollection db (name collection))
maybe-fields (when fields (as-field-selector fields))
maybe-sort (when sort (to-db-object sort))]
(from-db-object
^DBObject (.findAndModify ^DBCollection coll (to-db-object conditions) maybe-fields maybe-sort remove
(to-db-object document) return-new upsert) keywordize))))
;;
;; monger.multi.collection/find-by-id
;;
(defn ^DBObject find-by-id
@ -125,10 +157,13 @@
(from-db-object ^DBObject (find-one-as-map db collection {:_id id}) true))
([^DB db ^String collection id fields]
(check-not-nil! id "id must not be nil")
(from-db-object ^DBObject (find-one-as-map db collection {:_id id} fields) true)))
(from-db-object ^DBObject (find-one-as-map db collection {:_id id} fields) true))
([^DB db ^String collection id fields keywordize]
(check-not-nil! id "id must not be nil")
(from-db-object ^DBObject (find-one-as-map db collection {:_id id} fields) keywordize)))
;;
;; monger.collection/count
;; monger.multi.collection/count
;;
(defn count
@ -137,3 +172,190 @@
(.count (.getCollection db (name collection)) (to-db-object {})))
(^long [^DB db ^String collection ^Map conditions]
(.count (.getCollection db (name collection)) (to-db-object conditions))))
(defn any?
"Like monger.collection/any? but always takes a database as explicit argument"
([^DB db ^String collection]
(> (count db collection) 0))
([^DB db ^String collection ^Map conditions]
(> (count db collection conditions) 0)))
(defn empty?
"Like monger.collection/empty? but always takes a database as explicit argument"
([^DB db ^String collection]
(= (count db collection {}) 0)))
(defn ^WriteResult update
"Like monger.collection/update but always takes a database as explicit argument"
([^DB db ^String collection ^Map conditions ^Map document & {:keys [upsert multi write-concern] :or {upsert false
multi false
write-concern monger.core/*mongodb-write-concern*}}]
(.update (.getCollection db (name collection))
(to-db-object conditions)
(to-db-object document)
upsert
multi
write-concern)))
(defn ^WriteResult upsert
"Like monger.collection/upsert but always takes a database as explicit argument"
[^DB db ^String collection ^Map conditions ^Map document & {:keys [multi write-concern] :or {multi false
write-concern monger.core/*mongodb-write-concern*}}]
(update db collection conditions document :multi multi :write-concern write-concern :upsert true))
(defn ^WriteResult update-by-id
"Like monger.collection/update-by-id but always takes a database as explicit argument"
[^DB db ^String collection id ^Map document & {:keys [upsert write-concern] :or {upsert false
write-concern monger.core/*mongodb-write-concern*}}]
(check-not-nil! id "id must not be nil")
(.update (.getCollection db (name collection))
(to-db-object {:_id id})
(to-db-object document)
upsert
false
write-concern))
(defn ^WriteResult save
"Like monger.collection/save but always takes a database as explicit argument"
([^DB db ^String collection ^Map document]
(.save (.getCollection db (name collection))
(to-db-object document)
monger.core/*mongodb-write-concern*))
([^DB db ^String collection ^Map document ^WriteConcern write-concern]
(.save (.getCollection db (name collection))
(to-db-object document)
write-concern)))
(defn ^clojure.lang.IPersistentMap save-and-return
"Like monger.collection/save-and-return but always takes a database as explicit argument"
([^DB db ^String collection ^Map document]
(save-and-return ^DB db collection document ^WriteConcern monger.core/*mongodb-write-concern*))
([^DB db ^String collection ^Map document ^WriteConcern write-concern]
;; see the comment in insert-and-return. Here we additionally need to make sure to not scrap the :_id key if
;; it is already present. MK.
(let [doc (merge {:_id (ObjectId.)} document)]
(save db collection doc write-concern)
doc)))
(defn ^WriteResult remove
"Like monger.collection/remove but always takes a database as explicit argument"
([^DB db ^String collection]
(.remove (.getCollection db (name collection)) (to-db-object {})))
([^DB db ^String collection ^Map conditions]
(.remove (.getCollection db (name collection)) (to-db-object conditions))))
(defn ^WriteResult remove-by-id
"Like monger.collection/remove-by-id but always takes a database as explicit argument"
([^DB db ^String collection id]
(check-not-nil! id "id must not be nil")
(let [coll (.getCollection db (name collection))]
(.remove coll (to-db-object {:_id id})))))
;;
;; monger.multi.collection/create-index
;;
(defn create-index
"Like monger.collection/create-index but always takes a database as explicit argument"
([^DB db ^String collection ^Map keys]
(.createIndex (.getCollection db (name collection)) (as-field-selector keys)))
([^DB db ^String collection ^Map keys ^Map options]
(.createIndex (.getCollection db (name collection))
(as-field-selector keys)
(to-db-object options))))
;;
;; monger.multi.collection/ensure-index
;;
(defn ensure-index
"Like monger.collection/ensure-index but always takes a database as explicit argument"
([^DB db ^String collection ^Map keys]
(.ensureIndex (.getCollection db (name collection)) (as-field-selector keys)))
([^DB db ^String collection ^Map keys ^Map options]
(.ensureIndex (.getCollection db (name collection))
(as-field-selector keys)
(to-db-object options)))
([^DB db ^String collection ^Map keys ^String name ^Boolean unique?]
(.ensureIndex (.getCollection db (name collection))
(as-field-selector keys)
name
unique?)))
;;
;; monger.multi.collection/indexes-on
;;
(defn indexes-on
"Like monger.collection/indexes-on but always takes a database as explicit argument"
[^DB db ^String collection]
(from-db-object (.getIndexInfo (.getCollection db (name collection))) true))
;;
;; monger.multi.collection/drop-index
;;
(defn drop-index
"Like monger.collection/drop-index but always takes a database as explicit argument"
([^DB db ^String collection ^String idx-name]
(.dropIndex (.getCollection db (name collection)) idx-name)))
(defn drop-indexes
"Like monger.collection/drop-indexes but always takes a database as explicit argument"
([^DB db ^String collection]
(.dropIndexes (.getCollection db (name collection)))))
;;
;; monger.multi.collection/exists?, /create, /drop, /rename
;;
(defn exists?
"Like monger.collection/exists? but always takes a database as explicit argument"
([^DB db ^String collection]
(.collectionExists db collection)))
(defn create
"Like monger.collection/create but always takes a database as explicit argument"
([^DB db ^String collection ^Map options]
(.createCollection db collection (to-db-object options))))
(defn drop
"Like monger.collection/drop but always takes a database as explicit argument"
([^DB db ^String collection]
(.drop (.getCollection db (name collection)))))
(defn rename
"Like monger.collection/rename but always takes a database as explicit argument"
([^DB db ^String from, ^String to]
(.rename (.getCollection db from) to))
([^DB db ^String from ^String to ^Boolean drop-target]
(.rename (.getCollection db from) to drop-target)))
;;
;; Map/Reduce
;;
(defn map-reduce
"Like monger.collection/map-reduce but always takes a database as explicit argument"
([^DB db ^String collection ^String js-mapper ^String js-reducer ^String output ^Map query]
(let [coll (.getCollection db (name collection))]
(.mapReduce coll js-mapper js-reducer output (to-db-object query))))
([^DB db ^String collection ^String js-mapper ^String js-reducer ^String output ^MapReduceCommand$OutputType output-type ^Map query]
(let [coll (.getCollection db (name collection))]
(.mapReduce coll js-mapper js-reducer output output-type (to-db-object query)))))
;;
;; monger.multi.collection/distinct
;;
(defn distinct
"Like monger.collection/distinct but always takes a database as explicit argument"
([^DB db ^String collection ^String key]
(.distinct (.getCollection db (name collection)) ^String (to-db-object key)))
([^DB db ^String collection ^String key ^Map query]
(.distinct (.getCollection db (name collection)) ^String (to-db-object key) (to-db-object query))))

View file

@ -0,0 +1,145 @@
(ns monger.test.multi.find-test
(:import [com.mongodb WriteResult WriteConcern DBCursor DBObject DBRef]
org.bson.types.ObjectId
java.util.Date)
(:require [monger.core :as mg]
[monger.util :as mu]
[monger.multi.collection :as mgcol]
[monger.test.helper :as helper]
[monger.conversion :as mgcnv])
(:use clojure.test
monger.operators
monger.conversion
monger.test.fixtures))
(helper/connect!)
(use-fixtures :each purge-people purge-docs purge-things purge-libraries)
;;
;; find
;;
(deftest find-full-document-when-collection-is-empty
(let [db (mg/get-db "monger-test")
collection "docs"
cursor (mgcol/find db collection)]
(is (empty? (iterator-seq cursor)))))
(deftest find-document-seq-when-collection-is-empty
(let [db (mg/get-db "monger-test")
collection "docs"]
(is (empty? (mgcol/find-seq db collection)))))
(deftest find-multiple-documents-when-collection-is-empty
(let [db (mg/get-db "monger-test")
collection "libraries"]
(is (empty? (mgcol/find db collection { :language "Scala" })))))
(deftest find-multiple-maps-when-collection-is-empty
(let [db (mg/get-db "monger-test")
collection "libraries"]
(is (empty? (mgcol/find-maps db collection { :language "Scala" })))))
(deftest find-multiple-documents-by-regex
(let [db (mg/get-db "monger-test")
collection "libraries"]
(mgcol/insert-batch db collection [{ :language "Clojure", :name "monger" }
{ :language "Java", :name "nhibernate" }
{ :language "JavaScript", :name "sprout-core" }])
(is (= 2 (monger.core/count (mgcol/find db collection { :language #"Java*" }))))))
(deftest find-multiple-documents
(let [db (mg/get-db "monger-test")
collection "libraries"]
(mgcol/insert-batch db collection [{ :language "Clojure", :name "monger" }
{ :language "Clojure", :name "langohr" }
{ :language "Clojure", :name "incanter" }
{ :language "Scala", :name "akka" }])
(is (= 1 (monger.core/count (mgcol/find db collection { :language "Scala" }))))
(is (= 3 (.count (mgcol/find db collection { :language "Clojure" }))))
(is (empty? (mgcol/find db collection { :language "Java" })))))
(deftest find-document-specify-fields
(let [db (mg/get-db "monger-test")
collection "libraries"
_ (mgcol/insert db collection { :language "Clojure", :name "monger" })
result (mgcol/find db collection { :language "Clojure"} [:language])]
(is (= (seq [:_id :language]) (keys (mgcnv/from-db-object (.next result) true))))))
(deftest find-and-iterate-over-multiple-documents-the-hard-way
(let [db (mg/get-db "monger-test")
collection "libraries"]
(mgcol/insert-batch db collection [{ :language "Clojure", :name "monger" }
{ :language "Clojure", :name "langohr" }
{ :language "Clojure", :name "incanter" }
{ :language "Scala", :name "akka" }])
(doseq [doc (take 3 (map (fn [dbo]
(mgcnv/from-db-object dbo true))
(mgcol/find-seq db collection { :language "Clojure" })))]
(is (= "Clojure" (:language doc))))))
(deftest find-and-iterate-over-multiple-documents
(let [db (mg/get-db "monger-test")
collection "libraries"]
(mgcol/insert-batch db collection [{ :language "Clojure", :name "monger" }
{ :language "Clojure", :name "langohr" }
{ :language "Clojure", :name "incanter" }
{ :language "Scala", :name "akka" }])
(doseq [doc (take 3 (mgcol/find-maps db collection { :language "Clojure" }))]
(is (= "Clojure" (:language doc))))))
(deftest find-multiple-maps
(let [db (mg/get-db "monger-test")
collection "libraries"]
(mgcol/insert-batch db collection [{ :language "Clojure", :name "monger" }
{ :language "Clojure", :name "langohr" }
{ :language "Clojure", :name "incanter" }
{ :language "Scala", :name "akka" }])
(is (= 1 (count (mgcol/find-maps db collection { :language "Scala" }))))
(is (= 3 (count (mgcol/find-maps db collection { :language "Clojure" }))))
(is (empty? (mgcol/find-maps db collection { :language "Java" })))
(is (empty? (mgcol/find-maps db collection { :language "Java" } [:language :name])))))
(deftest find-multiple-partial-documents
(let [db (mg/get-db "monger-test")
collection "libraries"]
(mgcol/insert-batch db collection [{ :language "Clojure", :name "monger" }
{ :language "Clojure", :name "langohr" }
{ :language "Clojure", :name "incanter" }
{ :language "Scala", :name "akka" }])
(let [scala-libs (mgcol/find db collection { :language "Scala" } [:name])
clojure-libs (mgcol/find db collection { :language "Clojure"} [:language])]
(is (= 1 (.count scala-libs)))
(is (= 3 (.count clojure-libs)))
(doseq [i clojure-libs]
(let [doc (mgcnv/from-db-object i true)]
(is (= (:language doc) "Clojure"))))
(is (empty? (mgcol/find db collection { :language "Erlang" } [:name]))))))
(deftest finds-one-as-map
(let [db (mg/get-db "monger-test")
collection "libraries"]
(mgcol/insert-batch db collection [{ :language "Clojure", :name "monger" }
{ :language "Clojure", :name "langohr" }])
(let [res (mgcol/find-one-as-map db collection { :name "langohr" })]
(is (map? res))
(is (= "langohr" (:name res)))
(is (= "Clojure" (:language res))))
(is (= 2 (count (mgcol/find-one-as-map db collection { :name "langohr" } [:name]))))
(is (= "langohr" (get (mgcol/find-one-as-map db collection { :name "langohr" } [:name] false) "name")))))
(deftest find-and-modify
(let [db (mg/get-db "monger-test")
collection "libraries"]
(mgcol/insert-batch db collection [{ :language "Clojure", :name "monger" }
{ :language "Clojure", :name "langohr" }])))
(run-tests)

View file

@ -86,3 +86,104 @@
result (mc/insert db "widgets" doc)]
(is (= (sort ["kw1" "kw2"])
(sort (get-in (mc/find-map-by-id db collection id) [:keyword1 :keyword2]))))))
(defrecord Metrics
[rps eps])
(deftest insert-a-document-with-clojure-record-in-it
(let [db (mg/get-db "altdb")
collection "widgets"
id (ObjectId.)
doc {:record (Metrics. 10 20) "_id" id}
result (mc/insert db "widgets" doc)]
(is (= {:rps 10 :eps 20} (:record (mc/find-map-by-id db collection id))))))
(deftest test-insert-a-document-with-dbref
(let [db (mg/get-db "altdb")]
(mc/remove db "widgets")
(mc/remove db "owners")
(let [coll1 "widgets"
coll2 "owners"
oid (ObjectId.)
joe (mc/insert db "owners" {:name "Joe" :_id oid})
dbref (DBRef. (mg/current-db) coll2 oid)]
(mc/insert coll1 {:type "pentagon" :owner dbref})
(let [fetched (mc/find-one-as-map db coll1 {:type "pentagon"})
fo (:owner fetched)]
(is (= {:_id oid :name "Joe"} (from-db-object @fo true)))))))
;;
;; insert-and-return
;;
(deftest insert-and-return-a-basic-document-without-id-and-with-default-write-concern
(let [db (mg/get-db "altdb")
collection "people"
doc {:name "Joe" :age 30}
result (mc/insert-and-return db :people doc)]
(is (= (:name doc)
(:name result)))
(is (= (:age doc)
(:age result)))
(is (:_id result))
(is (= 1 (mc/count collection)))))
(deftest insert-and-return-a-basic-document-without-id-but-with-a-write-concern
(let [db (mg/get-db "altdb")
collection "people"
doc {:name "Joe" :age 30 :ratio 3/4}
result (mc/insert-and-return db "people" doc WriteConcern/FSYNC_SAFE)]
(is (= (:name doc)
(:name result)))
(is (= (:age doc)
(:age result)))
(is (= (:ratio doc)
(:ratio result)))
(is (:_id result))
(is (= 1 (mc/count collection)))))
(deftest insert-and-return-with-a-provided-id
(let [db (mg/get-db "altdb")
collection "people"
oid (ObjectId.)
doc {:name "Joe" :age 30 :_id oid}
result (mc/insert-and-return db :people doc)]
(is (= (:_id result) (:_id doc) oid))
(is (= 1 (mc/count collection)))))
;;
;; insert-batch
;;
(deftest insert-a-batch-of-basic-documents-without-ids-and-with-default-write-concern
(let [db (mg/get-db "altdb")
collection "people"
docs [{:name "Joe" :age 30} {:name "Paul" :age 27}]]
(is (monger.result/ok? (mc/insert-batch db "people" docs)))
(is (= 2 (mc/count collection)))))
(deftest insert-a-batch-of-basic-documents-without-ids-and-with-explicit-write-concern
(let [db (mg/get-db "altdb")
collection "people"
docs [{:name "Joe" :age 30} {:name "Paul" :age 27}]]
(is (monger.result/ok? (mc/insert-batch db "people" docs WriteConcern/NORMAL)))
(is (= 2 (mc/count collection)))))
(deftest insert-a-batch-of-basic-documents-with-explicit-database-without-ids-and-with-explicit-write-concern
(let [db (mg/get-db "altdb")
collection "people"
docs [{:name "Joe" :age 30} {:name "Paul" :age 27}]]
(dotimes [n 44]
(is (monger.result/ok? (mc/insert-batch db "people" docs WriteConcern/NORMAL))))
(is (= 88 (mc/count collection)))))
(deftest insert-a-batch-of-basic-documents-from-a-lazy-sequence
(let [db (mg/get-db "altdb")
collection "people"
numbers (range 0 1000)]
(is (monger.result/ok? (mc/insert-batch db "people" (map (fn [^long l]
{:n l})
numbers))))
(is (= (count numbers) (mc/count collection)))))