Initial work on monger.multi.collection

This commit is contained in:
Michael Klishin 2013-04-19 00:43:27 +04:00
parent 48be8602e3
commit 4ac8f8330a
8 changed files with 291 additions and 33 deletions

View file

@ -1,5 +1,19 @@
## Changes between 1.5.0 and 1.6.0
### monger.multi.collection
`monger.multi.collection` is a new namespace with functions that are very similar to those
in the `monger.collection` namespace but always take a database reference as an explicit argument.
They are supposed to be used in cases when relying on `monger.core/*mongodb-database*` is not
enough.
## monger.core/drop-db
`monger.core/drop-db` is a new function that drops a database by name.
### One More Cache Implementation
`monger.cache/db-aware-monger-cache-factory` will return a MongoDB-backed `clojure.core.cache`

View file

@ -9,36 +9,30 @@
;; the terms of this license.
;; You must not remove this notice, or any other, from this software.
(ns ^{:doc "Provides key functionality for interaction with MongoDB: inserting, querying, updating and deleting documents, performing Aggregation Framework
queries, creating and dropping indexes, creating collections and more.
(ns monger.collection
"Provides key functionality for interaction with MongoDB: inserting, querying, updating and deleting documents, performing Aggregation Framework
queries, creating and dropping indexes, creating collections and more.
For more advanced read queries, see monger.query.
For more advanced read queries, see monger.query.
Related documentation guides:
Related documentation guides:
* http://clojuremongodb.info/articles/getting_started.html
* http://clojuremongodb.info/articles/inserting.html
* http://clojuremongodb.info/articles/querying.html
* http://clojuremongodb.info/articles/updating.html
* http://clojuremongodb.info/articles/deleting.html
* http://clojuremongodb.info/articles/aggregation.html"}
monger.collection
* http://clojuremongodb.info/articles/getting_started.html
* http://clojuremongodb.info/articles/inserting.html
* http://clojuremongodb.info/articles/querying.html
* http://clojuremongodb.info/articles/updating.html
* http://clojuremongodb.info/articles/deleting.html
* http://clojuremongodb.info/articles/aggregation.html"
(: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]
org.bson.types.ObjectId)
(:require [monger core result])
(:use [monger.conversion]))
(:require monger.core
monger.result)
(:use monger.conversion
monger.constraints))
;;
;; Implementation
;;
(definline check-not-nil!
[ref ^String message]
`(when (nil? ~ref)
(throw (IllegalArgumentException. ~message))))
;;

View file

@ -0,0 +1,11 @@
(ns monger.constraints)
;;
;; API
;;
(definline check-not-nil!
[ref ^String message]
`(when (nil? ~ref)
(throw (IllegalArgumentException. ~message))))

View file

@ -42,7 +42,7 @@
(extend-protocol ConvertToDBObject
nil
(to-db-object [input]
input)
nil)
String
(to-db-object [^String input]

View file

@ -105,6 +105,12 @@
[]
*mongodb-database*)
(defn drop-db
"Drops a database"
([^String db]
(.dropDatabase *mongodb-connection* db))
([^MongoClient conn ^String db]
(.dropDatabase conn db)))
(defmacro with-connection
@ -243,7 +249,7 @@
Ordering of keys in the command document may matter. Please use sorted maps instead of map literals, for example:
(sorted-map geoNear \"bars\" :near 50 :test 430 :num 10)
For commonly used commands (distinct, count, map/reduce, etc), use monger/command and monger/collection functions such as
For commonly used commands (distinct, count, map/reduce, etc), use monger.command and monger.collection functions such as
/distinct, /count, /drop, /dropIndexes, and /mapReduce respectively."
([^Map cmd]
(.command ^DB *mongodb-database* ^DBObject (to-db-object cmd)))

View file

@ -0,0 +1,139 @@
(ns monger.multi.collection
"Includes versions of key monger.collection functions that always take a database
as explicit argument instead of relying on monger.core/*mongodb-database*.
Use these functions when you need to work with multiple databases or manage database
and connection lifecycle explicitly."
(:refer-clojure :exclude [find remove count])
(:import [com.mongodb Mongo DB DBCollection WriteResult DBObject WriteConcern DBCursor MapReduceCommand MapReduceCommand$OutputType]
[java.util List Map]
[clojure.lang IPersistentMap ISeq]
org.bson.types.ObjectId)
(:require monger.core
monger.result)
(:use monger.conversion
monger.constraints))
;;
;; API
;;
(defn ^WriteResult insert
"Like monger.collection/insert but always takes a database as explicit argument"
([^DB db ^String collection document]
(.insert (.getCollection db (name collection))
(to-db-object document)
monger.core/*mongodb-write-concern*))
([^DB db ^String collection document ^WriteConcern concern]
(.insert (.getCollection db (name collection))
(to-db-object document)
concern)))
(defn ^clojure.lang.IPersistentMap insert-and-return
"Like monger.collection/insert-and-return but always takes a database as explicit argument"
([^DB db ^String collection document]
(let [doc (merge {:_id (ObjectId.)} document)]
(insert db collection doc monger.core/*mongodb-write-concern*)
doc))
([^DB db ^String collection document ^WriteConcern concern]
;; MongoDB Java driver will generate the _id and set it but it tries to mutate the inserted DBObject
;; and it does not work very well in our case, because that DBObject is short lived and produced
;; from the Clojure map we are passing in. Plus, this approach is very awkward with immutable data
;; structures being the default. MK.
(let [doc (merge {:_id (ObjectId.)} document)]
(insert db collection doc concern)
doc)))
(defn ^WriteResult insert-batch
"Like monger.collection/insert-batch but always takes a database as explicit argument"
([^DB db ^String collection ^List documents]
(.insert (.getCollection db (name collection))
^List (to-db-object documents)
monger.core/*mongodb-write-concern*))
([^DB db ^String collection ^List documents ^WriteConcern concern]
(.insert (.getCollection db (name collection))
^List (to-db-object documents)
concern)))
;;
;; monger.collection/find
;;
(defn ^DBCursor find
"Like monger.collection/find but always takes a database as explicit argument"
([^DB db ^String collection ^Map ref]
(.find (.getCollection db (name collection))
(to-db-object ref)))
([^DB db ^String collection ^Map ref fields]
(.find (.getCollection db (name collection))
(to-db-object ref)
(as-field-selector fields))))
(defn find-maps
"Like monger.collection/find-maps but always takes a database as explicit argument"
([^DB db ^String collection ^Map ref]
(with-open [dbc (find db collection ref)]
(map (fn [x] (from-db-object x true)) dbc)))
([^DB db ^String collection ^Map ref fields]
(with-open [dbc (find db collection ref fields)]
(map (fn [x] (from-db-object x true)) dbc))))
;;
;; monger.collection/find-one
;;
(defn ^DBObject find-one
"Like monger.collection/find-one but always takes a database as explicit argument"
([^DB db ^String collection ^Map ref]
(.findOne (.getCollection db (name collection))
(to-db-object ref)))
([^DB db ^String collection ^Map ref fields]
(.findOne (.getCollection db (name collection))
(to-db-object ref)
^DBObject (as-field-selector fields))))
(defn ^IPersistentMap find-one-as-map
"Like monger.collection/find-one-as-map but always takes a database as explicit argument"
([^DB db ^String collection ^Map ref]
(from-db-object ^DBObject (find-one db collection ref) true))
([^DB db ^String collection ^Map ref fields]
(from-db-object ^DBObject (find-one db collection ref fields) true))
([^DB db ^String collection ^Map ref fields keywordize]
(from-db-object ^DBObject (find-one db collection ref fields) keywordize)))
;;
;; monger.collection/find-by-id
;;
(defn ^DBObject find-by-id
"Like monger.collection/find-by-id but always takes a database as explicit argument"
([^DB db ^String collection id]
(check-not-nil! id "id must not be nil")
(find-one db collection {:_id id}))
([^DB db ^String collection id fields]
(check-not-nil! id "id must not be nil")
(find-one db collection {:_id id} fields)))
(defn ^IPersistentMap find-map-by-id
"Like monger.collection/find-map-by-id but always takes a database as explicit argument"
([^DB db ^String collection id]
(check-not-nil! id "id must not be nil")
(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)))
;;
;; monger.collection/count
;;
(defn count
"Like monger.collection/count but always takes a database as explicit argument"
(^long [^DB db ^String collection]
(.count (.getCollection db (name collection)) (to-db-object {})))
(^long [^DB db ^String collection ^Map conditions]
(.count (.getCollection db (name collection)) (to-db-object conditions))))

View file

@ -26,13 +26,19 @@
(defcleaner events) ;; collection name will be taken from the events-collection var
(defcleaner people \"accounts\") ;; collection name is given
"
[entities & coll-name]
(let [coll-arg (if coll-name
(str (first coll-name))
(symbol (str entities "-collection")))
fn-name (symbol (str "purge-" entities))]
`(defn ~fn-name
[f#]
(mc/remove ~coll-arg)
(f#)
(mc/remove ~coll-arg))))
([entities]
(let [coll-arg (symbol (str entities "-collection"))
fn-name (symbol (str "purge-" entities))]
`(defn ~fn-name
[f#]
(mc/remove ~coll-arg)
(f#)
(mc/remove ~coll-arg))))
([entities coll-name]
(let [coll-arg (name coll-name)
fn-name (symbol (str "purge-" entities))]
`(defn ~fn-name
[f#]
(mc/remove ~coll-arg)
(f#)
(mc/remove ~coll-arg)))))

View file

@ -0,0 +1,88 @@
(ns monger.test.multi.inserting-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 mc]
[monger.test.helper :as helper])
(:use clojure.test
monger.operators
monger.conversion
monger.test.fixtures))
(helper/connect!)
(defn drop-altdb
[f]
(mg/drop-db "altdb")
(f))
(use-fixtures :each drop-altdb)
;;
;; insert
;;
(deftest insert-a-basic-document-without-id-and-with-default-write-concern
(let [db (mg/get-db "altdb")
collection "people"
doc {:name "Joe" :age 30}]
(is (monger.result/ok? (mc/insert db "people" doc)))
(is (= 1 (mc/count db collection)))))
(deftest insert-a-basic-document-with-explicitly-passed-database-without-id-and-with-default-write-concern
(let [db (mg/get-db "altdb")
collection "people"
doc {:name "Joe" :age 30}]
(dotimes [n 5]
(is (monger.result/ok? (mc/insert db "people" doc WriteConcern/SAFE))))
(is (= 5 (mc/count db collection)))))
(deftest insert-a-basic-document-without-id-and-with-explicit-write-concern
(let [db (mg/get-db "altdb")
collection "people"
doc {:name "Joe" :age 30}]
(is (monger.result/ok? (mc/insert db "people" doc WriteConcern/SAFE)))
(is (= 1 (mc/count db collection)))))
(deftest insert-a-basic-db-object-without-id-and-with-default-write-concern
(let [db (mg/get-db "altdb")
collection "people"
doc (to-db-object {:name "Joe" :age 30})]
(is (nil? (.get ^DBObject doc "_id")))
(mc/insert db "people" doc)
(is (not (nil? (monger.util/get-id doc))))))
(deftest insert-a-map-with-id-and-with-default-write-concern
(let [db (mg/get-db "altdb")
collection "people"
id (ObjectId.)
doc {:name "Joe" :age 30 "_id" id}
result (mc/insert db "people" doc)]
(is (= id (monger.util/get-id doc)))))
(deftest insert-a-document-with-clojure-ratio-in-it
(let [db (mg/get-db "altdb")
collection "widgets"
id (ObjectId.)
doc {:ratio 11/2 "_id" id}
result (mc/insert db "widgets" doc)]
(is (= 5.5 (:ratio (mc/find-map-by-id db collection id))))))
(deftest insert-a-document-with-clojure-keyword-in-it
(let [db (mg/get-db "altdb")
collection "widgets"
id (ObjectId.)
doc {:keyword :kwd "_id" id}
result (mc/insert db "widgets" doc)]
(is (= (name :kwd) (:keyword (mc/find-map-by-id db collection id))))))
(deftest insert-a-document-with-clojure-keyword-in-a-set-in-it
(let [db (mg/get-db "altdb")
collection "widgets"
id (ObjectId.)
doc {:keyword1 {:keyword2 #{:kw1 :kw2}} "_id" id}
result (mc/insert db "widgets" doc)]
(is (= (sort ["kw1" "kw2"])
(sort (get-in (mc/find-map-by-id db collection id) [:keyword1 :keyword2]))))))