2011-09-11 10:28:38 +00:00
# Monger
2011-08-04 12:01:26 +00:00
2011-12-31 00:39:58 +00:00
Monger is an idiomatic Clojure wrapper around MongoDB Java driver. It offers powerful expressive query DSL, strives to support
every MongoDB 2.0+ feature and is well maintained.
2011-08-04 12:01:26 +00:00
2012-01-30 22:36:09 +00:00
[](http://travis-ci.org/michaelklishin/monger)
2011-09-10 16:28:13 +00:00
2011-10-19 15:32:55 +00:00
## Project Goals
There is one MongoDB client for Clojure that has been around since 2009. So, why create another one? Monger authors
wanted a client that will
* Support most of MongoDB 2.0+ features but only those that really matter. Grouping the way it is done today, for example, does not (it is easier to just use Map/Reduce directly).
* Be well documented.
* Be well tested.
2012-01-30 22:36:43 +00:00
* Be maintained, do not carry technical debt from 2009 forever.
2011-11-04 06:26:32 +00:00
* Integrate with libraries like clojure.data.json and Joda Time.
2011-10-19 15:41:49 +00:00
* Integrate usage of JavaScript files and ClojureScript (as soon as the compiler gets artifact it is possible to depend on for easy embedding).
* Learn from other clients like the Java and Ruby ones.
* Target Clojure 1.3.0 and later from the ground up.
2011-10-19 15:32:55 +00:00
2011-12-31 00:39:58 +00:00
2012-01-30 22:32:50 +00:00
## Documentation & Examples
2011-08-04 12:01:26 +00:00
2011-12-31 00:39:58 +00:00
We are working on documentation guides & examples site for the 1.0 release. In the meantime, please refer to the [test suite ](https://github.com/michaelklishin/monger/tree/master/test/monger/test ) for code examples.
2012-01-30 23:05:14 +00:00
## Mailing List
[Monger has a mailing list ](https://groups.google.com/forum/#!forum/clojure-monger ). Feel free to join it and ask any questions you may have.
2012-02-03 22:03:24 +00:00
## This is a Work In Progress
Core Monger APIs are stabilized but it is still a work in progress. Keep that in mind. 1.0 will be released in early 2012
together with documentation guides and dedicated website.
## Artifacts
Snapshot artifacts are [released to Clojars ](https://clojars.org/com.novemberain/monger ) every 24 hours.
With Leiningen:
[com.novemberain/monger "1.0.0-SNAPSHOT"]
With Maven:
< dependency >
< groupId > com.novemberain< / groupId >
< artifactId > monger< / artifactId >
< version > 1.0.0-SNAPSHOT< / version >
< / dependency >
## Supported Clojure versions
Monger is built from the ground up for Clojure 1.3 and up.
2012-01-30 22:32:50 +00:00
## Connecting to MongoDB
Monger supports working with multiple connections and/or databases but is optimized for applications that only use one connection
and one database.
``` clojure
(ns my.service.server
(:require [monger core util]))
;; localhost, default port
(monger.core/connect!)
;; given host, given port
(monger.core/connect! { :host "db.megacorp.internal" :port 7878 })
```
To set default database Monger will use, use `monger.core/get-db` and `monger.core/set-db!` functions in combination:
``` clojure
(ns my.service.server
(:require [monger core]]))
;; localhost, default port
(monger.core/connect!)
(monger.core/set-db! (monger.core/get-db "monger-test"))
```
To set default write concern, use `monger.core/set-default-write-concern!` function:
``` clojure
(monger.core/set-default-write-concern! WriteConcern/FSYNC_SAFE)
```
By default Monger will use `WriteConcern/SAFE` as write concern. We believe that MongoDB Java driver (as well as other
official drivers) are using very unsafe defaults when no exceptions are raised, even for network issues. This does not sound
like a good default for most applications: many applications use MongoDB because of the flexibility, not extreme write throughput
requirements.
## Inserting Documents
To insert documents, use `monger.collection/insert` and `monger.collection/insert-batch` functions.
``` clojure
(ns my.service.server
(:use [monger.core :only [connect! connect set-db! get-db]]
[monger.collection :only [insert insert-batch]])
(:import [org.bson.types ObjectId]
2012-01-30 22:33:58 +00:00
[com.mongodb DB WriteConcern]))
2012-01-30 22:32:50 +00:00
;; localhost, default port
(connect!)
(set-db! (monger.core/get-db "monger-test"))
;; without document id
(insert "document" { :first_name "John" :last_name "Lennon" })
;; multiple documents at once
(insert-batch "document" [{ :first_name "John" :last_name "Lennon" }
{ :first_name "Paul" :last_name "McCartney" }])
;; with explicit document id
(insert "documents" { :_id (ObjectId.) :first_name "John" :last_name "Lennon" })
;; with a different write concern
(insert "documents" { :_id (ObjectId.) :first_name "John" :last_name "Lennon" } WriteConcern/JOURNAL_SAFE)
;; with a different database
(let [archive-db (get-db "monger-test.archive")]
(insert archive-db "documents" { :first_name "John" :last_name "Lennon" } WriteConcern/NORMAL))
```
### Write Performance
Monger insert operations are efficient and have very little overhead compared to the underlying Java driver. Here
are some numbers on a MacBook Pro from fall 2010 with Core i7 and an Intel SSD drive:
```
Testing monger.test.stress
Inserting 1000 documents...
"Elapsed time: 38.317 msecs"
Inserting 10,000 documents...
"Elapsed time: 263.827 msecs"
Inserting 100,000 documents...
"Elapsed time: 1679.828 msecs"
```
With the `SAFE` write concern, it takes roughly 1.7 second to insert 100,000 documents.
## Regular Finders
`monger.collection` namespace provides several finder functions that try to follow MongoDB query language as closely as possible,
even when providing shortcuts for common cases.
2012-01-30 22:34:50 +00:00
``` clojure
2012-01-30 22:32:50 +00:00
(ns my.service.finders
(:require [monger.collection :as mc])
(:use [monger.operators]))
;; find one document by id, as Clojure map
(mc/find-map-by-id "documents" (ObjectId. "4ec2d1a6b55634a935ea4ac8"))
;; find one document by id, as `com.mongodb.DBObject` instance
(mc/find-by-id "documents" (ObjectId. "4ec2d1a6b55634a935ea4ac8"))
;; find one document as Clojure map
(mc/find-one-as-map "documents" { :_id (ObjectId. "4ec2d1a6b55634a935ea4ac8") })
;; find one document by id, as `com.mongodb.DBObject` instance
(mc/find-one "documents" { :_id (ObjectId. "4ec2d1a6b55634a935ea4ac8") })
;; all documents as Clojure maps
(mc/find-maps "documents")
;; all documents as `com.mongodb.DBObject` instances
(mc/find "documents")
;; with a query, as Clojure maps
(mc/find-maps "documents" { :year 1998 })
;; with a query, as `com.mongodb.DBObject` instances
(mc/find "documents" { :year 1998 })
;; with a query that uses operators
(mc/find "products" { :price_in_subunits { $gt 4000 $lte 1200 } })
;; with a query that uses operators as strings
(mc/find "products" { :price_in_subunits { "$gt" 4000 "$lte" 1200 } })
2012-01-30 22:34:50 +00:00
```
2012-01-30 22:32:50 +00:00
2011-12-31 00:39:58 +00:00
## Powerful Query DSL
2011-08-04 12:01:26 +00:00
2011-12-31 00:39:58 +00:00
Every application that works with data stores has to query them. As a consequence, having an expressive powerful query DSL is a must
for client libraries like Monger.
Here is what monger.query DSL feels like:
2011-11-14 11:22:12 +00:00
``` clojure
(with-collection "docs"
(find { :inception_year { $lt 2000 $gte 2011 } })
2012-01-28 11:20:55 +00:00
(fields [ :inception_year :name ])
2011-11-14 11:22:12 +00:00
(skip 10)
(limit 20)
(batch-size 50)
(hint "my-index-name")
(snapshot))
```
2011-12-31 00:39:58 +00:00
It is easy to add new DSL elements, for example, adding pagination took literally less than 10 lines of Clojure code. Here is what
it looks like:
``` clojure
(with-collection coll
(find {})
(paginate :page 1 :per-page 3)
(sort { :title 1 })
(read-preference ReadPreference/PRIMARY))
```
Query DSL supports composition, too:
``` clojure
(let
[top3 (partial-query (limit 3))
by-population-desc (partial-query (sort { :population -1 }))
result (with-collection coll
(find {})
(merge top3)
(merge by-population-desc))]
;; ...
)
```
More code examples can be found [in our test suite ](https://github.com/michaelklishin/monger/tree/master/test/monger/test ).
2011-11-14 11:22:12 +00:00
2011-09-08 20:27:49 +00:00
2012-01-30 22:32:50 +00:00
## Updating Documents
Use `monger.collection/update` and `monger.collection/save` .
## Removing Documents
Use `monger.collection/remove` .
## Counting Documents
Use `monger.collection/count` , `monger.collection/empty?` and `monger.collection/any?` .
## Determening Whether Operation Succeeded (or Failed)
To be documented.
## Validators with Validateur
Monger relies on [Validateur ](http://github.com/michaelklishin/validateur ) for data validation.
To be documented.
2012-01-30 21:52:50 +00:00
## Integration With Popular Libraries
Because Monger was built for Clojure 1.3 and later, it can take advantage of relatively new powerful Clojure features such as protocols.
### Integration with clojure.data.json
Monger was created for AMQP and HTTP services that use JSON to serialize message payloads. When serializing documents to JSON, developers
2012-01-30 21:53:41 +00:00
usually want to represent `com.mongodb.ObjectId` instances as strings in resulting JSON documents. Monger integrates with [clojure.data.json ](http://github.com/clojure/data.json ) to
2012-01-30 21:52:50 +00:00
make that effortless.
Just load `monger.json` namespace and it will extend `clojure.data.json/WriteJSON` protocol to support `com.mongodb.ObjectId` instance. Then
functions like `clojure.data.json/write-json` will be able to serialize object ids as strings exactly the way you expect it to be.
``` clojure
(ns my.service.handlers
;; Make clojure.data.json aware of ObjectId instances
(:require [monger.json]))
```
### Integration with Joda Time
Monger provides the `monger.joda-time` namespace that extend its own Clojure-to-DBObject conversion protocols as well as
2012-01-30 21:53:41 +00:00
[clojure.data.json ](http://github.com/clojure/data.json ) `WriteJSON` protocol to handle `org.joda.time.DateTime` instances. To use it, make sure that
2012-01-30 21:52:50 +00:00
you have JodaTime and clojure.data.json on your dependencies list then load `monger.joda-time` like so
``` clojure
(ns my.service.handlers
2012-01-30 21:54:53 +00:00
;; Make Monger conversion protocols and clojure.data.json aware of JodaTime's DateTime instances
2012-01-30 21:52:50 +00:00
(:require [monger.joda-time]))
```
Now `clojure.data.json/write-json` and related functions will serialize JodaTime date time objects using [ISO8601 date time format ](http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html ). In addition, functions that convert MongoDB documents to
Clojure maps will instantiate JodaTime date time objects from `java.util.Date` instances MongoDB Java driver uses.
2012-01-30 22:32:50 +00:00
## Map/Reduce. Using JavaScript Resources.
To be documented.
## Operations On Indexes
To be documented.
## Database Commands
To be documented.
## GridFS Support
To be documented.
## Helper Functions
To be documented.
2012-02-03 22:03:24 +00:00
## Development
2012-01-30 21:52:50 +00:00
2012-02-03 22:03:24 +00:00
Install [lein-multi]() with
2011-09-10 16:28:13 +00:00
2012-02-03 22:03:24 +00:00
lein plugin install lein-multi 1.1.0
2011-09-10 16:28:13 +00:00
2012-02-03 22:03:24 +00:00
then run tests against Clojure 1.3.0 and 1.4.0[-beta1] using
2011-10-09 12:56:10 +00:00
2012-02-03 22:03:24 +00:00
lein multi test
2011-10-09 12:56:10 +00:00
2012-02-03 22:03:24 +00:00
Then create a branch and make your changes on it. Once you are done with your changes and all tests pass, submit a pull request
on Github.
2011-10-09 12:56:10 +00:00
2011-09-08 20:27:49 +00:00
2011-08-04 12:01:26 +00:00
## License
2011-09-11 07:32:46 +00:00
Copyright (C) 2011 Michael S. Klishin
2011-08-04 12:01:26 +00:00
2011-10-16 13:03:14 +00:00
Distributed under the [Eclipse Public License ](http://www.eclipse.org/legal/epl-v10.html ), the same as Clojure.