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 Use these functions when you need to work with multiple databases or manage database
and connection lifecycle explicitly." 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] (:import [com.mongodb Mongo DB DBCollection WriteResult DBObject WriteConcern DBCursor MapReduceCommand MapReduceCommand$OutputType]
[java.util List Map] [java.util List Map]
[clojure.lang IPersistentMap ISeq] [clojure.lang IPersistentMap ISeq]
@ -60,11 +60,13 @@
;; ;;
;; monger.collection/find ;; monger.multi.collection/find
;; ;;
(defn ^DBCursor find (defn ^DBCursor find
"Like monger.collection/find but always takes a database as explicit argument" "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] ([^DB db ^String collection ^Map ref]
(.find (.getCollection db (name collection)) (.find (.getCollection db (name collection))
(to-db-object ref))) (to-db-object ref)))
@ -75,6 +77,9 @@
(defn find-maps (defn find-maps
"Like monger.collection/find-maps but always takes a database as explicit argument" "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] ([^DB db ^String collection ^Map ref]
(with-open [dbc (find db collection ref)] (with-open [dbc (find db collection ref)]
(map (fn [x] (from-db-object x true)) dbc))) (map (fn [x] (from-db-object x true)) dbc)))
@ -82,8 +87,20 @@
(with-open [dbc (find db collection ref fields)] (with-open [dbc (find db collection ref fields)]
(map (fn [x] (from-db-object x true)) dbc)))) (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 (defn ^DBObject find-one
@ -106,7 +123,22 @@
(from-db-object ^DBObject (find-one db collection ref fields) keywordize))) (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 (defn ^DBObject find-by-id
@ -125,10 +157,13 @@
(from-db-object ^DBObject (find-one-as-map db collection {:_id id}) true)) (from-db-object ^DBObject (find-one-as-map db collection {:_id id}) true))
([^DB db ^String collection id fields] ([^DB db ^String collection id fields]
(check-not-nil! id "id must not be nil") (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 (defn count
@ -137,3 +172,190 @@
(.count (.getCollection db (name collection)) (to-db-object {}))) (.count (.getCollection db (name collection)) (to-db-object {})))
(^long [^DB db ^String collection ^Map conditions] (^long [^DB db ^String collection ^Map conditions]
(.count (.getCollection db (name collection)) (to-db-object 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)] result (mc/insert db "widgets" doc)]
(is (= (sort ["kw1" "kw2"]) (is (= (sort ["kw1" "kw2"])
(sort (get-in (mc/find-map-by-id db collection id) [:keyword1 :keyword2])))))) (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)))))