A toolkit for using MongoDB with Clojure. This library is a pretty lightweight wrapper around the MongoDB Java driver, and can be paired with your favorite framworks or functions for validation such as clojure.spec.alpha.
The client was tested with the following MongoDB versions:
- MongoDB 3.6
- MongoDB 4.2
Thus, while the library may work just fine with higher MongoDB versions, the full feature support is not guaranteed.
Latest stable release is 2.6.0.
Leiningen dependency information:
[congomongo "2.6.0"]
congomongo {:mvn/version "2.6.0"}
Maven dependency information:
<dependency>
<groupId>congomongo</groupId>
<artifactId>congomongo</artifactId>
<version>2.6.0</version>
</dependency>
Gradle dependency information:
compile 'congomongo:congomongo:2.6.0'
(ns my-mongo-app
(:require [somnium.congomongo :as m]))
(def conn
(m/make-connection "mydb"
:instances [{:host "127.0.0.1" :port 27017}]))
=> #'user/conn
conn => {:mongo #<MongoClient Mongo: /127.0.0.1:20717>, :db #<DBApiLayer mydb>}
(def conn
(m/make-connection "mongodb://127.0.0.1/mydb"))
=> #'user/conn
conn => {:mongo #<MongoClient Mongo: /127.0.0.1:20717>, :db #<DBApiLayer mydb>}
(m/set-connection! conn)
(m/with-mongo conn
(m/insert! :robots {:name "robby"}))
(m/close-connection conn)
(m/set-write-concern conn :journaled)
These are the official write concerns, more details about them can be found in the WriteConcern javadoc.
:unacknowledged
will report network errors - but does not wait for the write to be acknowledged:acknowledged
will report key constraint and other errors - this is the default:journaled
waits until the primary has sync'd the write to the journal:fsynced
waits until a write is sync'd to the filesystem:replica-acknowledged
waits until a write is sync'd to at least one replica as well:majority
waits until a write is sync'd to a majority of replica nodes
You can pass a simple read preference (without tags) to each function accepting read preferences. This may look like:
(m/fetch :fruit :read-preference :nearest)
to get the fruit from the nearest server. You may create more advances read preferences using the read-preference
function.
(let [p (m/read-preference :nearest {:location "Europe"})]
(fetch :fruit :read-preference p)
)
to be more specific to get the nearest fruit. You may also set a default ReadPreference
on a per collection or connection basis using set-read-preference
or set-collection-read-preference!
.
(m/set-read-preference conn :primary-preferred)
(m/set-collection-read-preference! :news :secondary)
(m/insert! :robots
{:name "robby"})
(def my-robot (m/fetch-one :robots)) => #'user/my-robot
my-robot => {:name "robby",
:_id #<ObjectId> "0c23396f7e53e34a4c8cf400">}
Update query and modifier can use all mongodb operators
(m/update! :robots
{:_id (:_id my-robot)}
{:$set {:name "asimo"}})
=> #<WriteResult { "serverUsed" : "/127.0.0.1:27017" ,
"updatedExisting" : true ,
"n" : 1 ,
"connectionId" : 169 ,
"err" : null ,
"ok" : 1.0}>
(m/destroy! :robots {:name "asimo"}) => #<WriteResult { "serverUsed" : "/127.0.0.1:27017" ,
"n" : 1 ,
"connectionId" : 170 ,
"err" : null ,
"ok" : 1.0}>
(m/fetch :robots) => ()
(dorun (m/mass-insert!
:points
(for [x (range 100) y (range 100)]
{:x x
:y y
:z (* x y)}))
=> nil ;; without dorun this would produce a WriteResult with 10,000 maps in it!
(m/fetch-count :points)
=> 10000
(m/fetch-one
:points
:where {:x {:$gt 10
:$lt 20}
:y 42
:z {:$gt 500}})
=> {:x 12, :y 42, :z 504, :_id ... }
(m/aggregate
:expenses
{:$match {:type "airfare"}}
{:$project {:department 1, :amount 1}}
{:$group {:_id "$department", :average {:$avg "$amount"}}})
=> {:serverUsed "...", :result [{:_id ... :average ...} {:_id ... :average ...} ...], :ok 1.0}
This pipeline of operations selects expenses with type = 'airfare', passes just the department and amount fields thru, and groups by department with an average for each.
Based on 10gen's Java Driver example of aggregation.
The aggregate function accepts any number of pipeline operations.
(m/authenticate conn "myusername" "my password")
=> true
(m/make-connection :mydb
:instances [{:host "127.0.0.1"}]
:options (m/mongo-options :auto-connect-retry true))
The available options are hyphen-separated lowercase keyword versions of the camelCase options supported by the Java driver. Prior to CongoMongo 0.4.0, the options matched the fields in the MongoOptions class. As of CongoMongo 0.4.0, the options match the method names in the MongoClientOptions class instead (and an IllegalArgumentException will be thrown if you use an illegal option). The full list (with the 2.10.1 Java driver) is:
(:auto-connect-retry :connect-timeout :connections-per-host :cursor-finalizer-enabled
:db-decoder-factory :db-encoder-factory :description :legacy-defaults
:max-auto-connect-retry-time :max-wait-time :read-preference :socket-factory
:socket-keep-alive :socket-timeout :threads-allowed-to-block-for-connection-multiplier
:write-concern)
(m/make-connection :mydb
:instances [{:host "127.0.0.1"}]
:username "user"
:password "password"
:auth-source {:mechanism :scram-1 :source "authsourcedb"})
Supported authentication mechanisms:
Key | Mechanism |
---|---|
:plain |
AuthenticationMechanism.PLAIN |
:scram-1 |
AuthenticationMechanism.SCRAM_SHA_1 |
:scram-256 |
AuthenticationMechanism.SCRAM_SHA_256 |
(m/make-connection "mongodb://user:pass@host:27071/databasename")
;; note that authentication is handled when given a user:pass@ section
;; or using SRV DNS service detection
(m/make-connection "mongodb+srv://user:pass@mymongocluster/databasename")
A query string may also be specified containing the options supported by the MongoClientURI class.
(m/fetch-one :points
:as :json)
=> "{ \"_id\" : \"0c23396ffe79e34a508cf400\" ,
\"x\" : 0 , \"y\" : 0 , \"z\" : 0 }"
For example, use Joda types for dates:
(extend-protocol somnium.congomongo.coerce.ConvertibleFromMongo
Date
(mongo->clojure [^java.util.Date d keywordize] (new org.joda.time.DateTime d)))
(extend-protocol somnium.congomongo.coerce.ConvertibleToMongo
org.joda.time.DateTime
(clojure->mongo [^org.joda.time.DateTime dt] (.toDate dt)))
Use :explain? on fetch to get performance information about a query. Returns a map of statistics about the query, not rows:
(m/fetch :users :where {:login "alice"} :explain? true)
{:nscannedObjects 2281,
:nYields 0,
:nscanned 2281,
:millis 2,
:isMultiKey false,
:cursor "BasicCursor",
:n 1,
:indexOnly false,
:allPlans [{:cursor "BasicCursor", :indexBounds {}}],
:nChunkSkips 0,
:indexBounds {},
:oldPlan {:cursor "BasicCursor", :indexBounds {}}}
Sometimes it's very helpful to be able to provide default options for all queries (for example, specify default
max-time-ms
timeout) instead of specifying it for each call. You can do this by wrapping your code in the
with-default-query-options
macro. Options specified in a particular call will have a priority and overwrite
default-query-options
.
; This call will only return one item, since it is using the default options
(with-default-query-options {:limit 1}
(fetch :thingies :where {:foo 1}))
; You can also override the defaults by specifying a new value
(with-default-query-options {:limit 1}
(fetch :thingies :where {:foo 1} :limit 2)) ; returns 2 items
There is now a Google Group Come help us make ponies for great good.
Clojars group is congomongo.
Congomongo is made available under the terms of an MIT-style license. Please refer to the source code for the full text of this license and for copyright details.