Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

278 lines (220 sloc) 10.163 kb
;; Copyright (c) 2011-2012 Michael S. Klishin
;; The use and distribution terms for this software are covered by the
;; Eclipse Public License 1.0 (
;; which can be found in the file epl-v10.html at the root of this distribution.
;; By using this software in any fashion, you are agreeing to be bound by
;; the terms of this license.
;; You must not remove this notice, or any other, from this software.
(ns ^{:author "Michael S. Klishin"
:doc "Thin idiomatic wrapper around MongoDB Java client. monger.core includes
fundamental functions that perform database/replica set connection, set default write concern, default database, performing commands
and so on. Most of the functionality is in other monger.* namespaces, in particular monger.collection, monger.query and monger.gridfs
Related documentation guides:
(:refer-clojure :exclude [count])
(:use [monger.conversion])
(:import [com.mongodb Mongo MongoURI DB WriteConcern DBObject DBCursor Bytes MongoOptions ServerAddress MapReduceOutput]
[com.mongodb.gridfs GridFS]
[java.util Map ArrayList]))
;; Defaults
(def ^:dynamic ^String *mongodb-host* "")
(def ^:dynamic ^long *mongodb-port* 27017)
(declare ^:dynamic ^Mongo *mongodb-connection*)
(declare ^:dynamic ^DB *mongodb-database*)
(def ^:dynamic ^WriteConcern *mongodb-write-concern* WriteConcern/SAFE)
(declare ^:dynamic ^GridFS *mongodb-gridfs*)
;; API
(defn ^com.mongodb.Mongo connect
"Connects to MongoDB. When used without arguments, connects to
:host (*mongodb-host* by default)
:port (*mongodb-port* by default)
(monger.core/connect { :host \"db3.intranet.local\", :port 27787 })
;; Connecting to a replica set with a couple of seeds
(let [^MongoOptions opts (mg/mongo-options :threads-allowed-to-block-for-connection-multiplier 300)
seeds [[\"\" 27017] [\"\" 27017] [\"\" 27018]]
sas (map #(apply mg/server-address %) seeds)]
(mg/connect! sas opts))
{:arglists '([]
[server-address options]
[[server-address & more] options]
[{ :keys [host port uri] :or { host *mongodb-host* port *mongodb-port* }}])}
([server-address ^MongoOptions options]
(if (coll? server-address)
;; connect to a replica set
(let [server-list ^ArrayList (ArrayList. ^java.util.Collection server-address)]
(Mongo. server-list options))
;; connect to a single instance
(Mongo. ^ServerAddress server-address options)))
([{ :keys [host port uri] :or { host *mongodb-host* port *mongodb-port* }}]
(Mongo. ^String host ^Long port)))
(defn get-db-names
"Gets a list of all database names present on the server"
(get-db-names *mongodb-connection*))
([^Mongo connection]
(set (.getDatabaseNames connection))))
(defn ^com.mongodb.DB get-db
"Get database reference by name.
(monger.core/get-db \"myapp_production\")
(monger.core/get-db connection \"myapp_production\")"
([^String name]
(.getDB *mongodb-connection* name))
([^Mongo connection ^String name]
(.getDB connection name)))
(defn ^com.mongodb.DB current-db
"Returns currently used database"
(defn authenticate
([^String db ^String username ^chars password]
(authenticate *mongodb-connection* db username password))
([^Mongo connection ^String db ^String username ^chars password]
(.authenticate (.getDB connection db) username password)))
(defmacro with-connection
[conn & body]
`(binding [*mongodb-connection* ~conn]
(do ~@body)))
(defmacro with-db
[db & body]
`(binding [*mongodb-database* ~db]
(do ~@body)))
(defmacro with-gridfs
[fs & body]
`(binding [*mongodb-gridfs* ~fs]
(do ~@body)))
(defn server-address
([^String hostname]
(ServerAddress. hostname))
([^String hostname ^Long port]
(ServerAddress. hostname port)))
(defn mongo-options
[& { :keys [connections-per-host threads-allowed-to-block-for-connection-multiplier
max-wait-time connect-timeout socket-timeout socket-keep-alive auto-connect-retry max-auto-connect-retry-time
safe w w-timeout fsync j] :or [auto-connect-retry true] }]
(let [mo (MongoOptions.)]
(when connections-per-host
(set! (. mo connectionsPerHost) connections-per-host))
(when threads-allowed-to-block-for-connection-multiplier
(set! (. mo threadsAllowedToBlockForConnectionMultiplier) threads-allowed-to-block-for-connection-multiplier))
(when max-wait-time
(set! (. mo maxWaitTime) max-wait-time))
(when connect-timeout
(set! (. mo connectTimeout) connect-timeout))
(when socket-timeout
(set! (. mo socketTimeout) socket-timeout))
(when socket-keep-alive
(set! (. mo socketKeepAlive) socket-keep-alive))
(when auto-connect-retry
(set! (. mo autoConnectRetry) auto-connect-retry))
(when max-auto-connect-retry-time
(set! (. mo maxAutoConnectRetryTime) max-auto-connect-retry-time))
(when safe
(set! (. mo safe) safe))
(when w
(set! (. mo w) w))
(when w-timeout
(set! (. mo wtimeout) w-timeout))
(when j
(set! (. mo j) j))
(when fsync
(set! (. mo fsync) fsync))
(defn set-connection!
"Sets given MongoDB connection as default by altering *mongodb-connection* var"
^Mongo [^Mongo conn]
(alter-var-root (var *mongodb-connection*) (constantly conn)))
(defn connect!
"Connect to MongoDB, store connection in the *mongodb-connection* var"
^Mongo [& args]
(let [c (apply connect args)]
(set-connection! c)))
(defn set-db!
"Sets *mongodb-database* var to given db, updates *mongodb-gridfs* var state. Recommended to be used for
applications that only use one database."
(alter-var-root (var *mongodb-database*) (constantly db))
(alter-var-root (var *mongodb-gridfs*) (constantly (GridFS. db))))
(def ^{:doc "Combines set-db! and get-db, so (use-db \"mydb\") is the same as (set-db! (get-db \"mydb\"))"}
use-db! (comp set-db! get-db))
(defn set-default-write-concern!
"Set *mongodb-write-concert* var to :wc
Unlike the official Java driver, Monger uses WriteConcern/SAFE by default. We think defaults should be safe first
and WebScale fast second."
(alter-var-root #'*mongodb-write-concern* (constantly wc)))
(defn connect-via-uri!
"Connects to MongoDB using a URI, sets up default connection and database. Commonly used for PaaS-based applications,
for example, running on Heroku. If username and password are provided, performs authentication."
(let [uri (MongoURI. uri)
;; yes, you are not hallucinating. A class named MongoURI has a method called connectDB.
;; I call it "college OOP". Or maybe "don't give a shit" OOP.
db (.connectDB uri)
conn (.getMongo db)
user (.getUsername uri)
pwd (.getPassword uri)]
;; I hope that whoever wrote the MongoDB Java driver connection/authentication parts
;; wasn't sober while at it. MK.
;; First we set connection, then DB, then authentcate
(set-connection! conn)
(when (and user pwd)
(when-not (authenticate (.getName db) user pwd)
(throw (IllegalArgumentException. (format "Could not authenticate with MongoDB. Either database name or credentials are invalid. Database name: %s, username: %s" (.getName db) user)))))
;; only do this *after* we authenticated because set-db! will try to set up a default GridFS instance. MK.
(when db
(set-db! db))
(defn ^com.mongodb.CommandResult command
"Runs a database command (please check MongoDB documentation for the complete list of commands).
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
/distinct, /count, /drop, /dropIndexes, and /mapReduce respectively."
([^Map cmd]
(.command ^DB *mongodb-database* ^DBObject (to-db-object cmd)))
([^DB database ^Map cmd]
(.command ^DB database ^DBObject (to-db-object cmd))))
(defprotocol Countable
(count [this] "Returns size of the object"))
(extend-protocol Countable
(count [^DBCursor this]
(.count this))
(count [^MapReduceOutput this]
;; MongoDB Java driver could use a lot more specific type than Iterable but
;; it always uses DBCollection#find to popular result set. MK.
(.count ^DBCursor (.results this))))
(defn ^DBObject get-last-error
"Returns the the error (if there is one) from the previous operation on this connection.
The result of this command looks like:
#<CommandResult { \"serverUsed\" : \"\" , \"n\" : 0 , \"connectionId\" : 66 , \"err\" : null , \"ok\" : 1.0}>\"
The value for err will be null if no error occurred, or a description otherwise.
Important note: when calling this method directly, it is undefined which connection \"getLastError\" is called on.
You may need to explicitly use a \"consistent Request\", see requestStart() For most purposes it is better not to call this method directly but instead use WriteConcern."
(get-last-error *mongodb-database*))
([^DB database]
(.getLastError ^DB database))
([^DB database ^Integer w ^Integer wtimeout ^Boolean fsync]
(.getLastError ^DB database w wtimeout fsync))
([^DB database ^WriteConcern write-concern]
(.getLastError ^DB database write-concern)))
Jump to Line
Something went wrong with that request. Please try again.