MongoDB Library for Clojure
Pull request Compare This branch is 22 commits behind yuushimizu:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


A MongoDB Library for Clojure.

Mongoika simplify building queries behaved like lazy sequences, and supports basic operations and GridFS using Mongo Java Driver.


;; Use mongoika namespace.
(use 'mongoika)

;; Connect to a MongoDB server.
(with-mongo [connection {:host your-mongodb-host :port your-mongodb-port}]
  ;; Bind a database dynamically.
  (with-db-binding (database connection :your-database)
    ;; Insertion:
    (insert! :fruits {:name "Banana" :color :yellow :price 100})
    (insert! :fruits {:name "Apple" :color :red :price 80})
    (insert! :fruits {:name "Lemon" :color :yellow :price 50})

    ;; Fetch all:
    (query :fruits)
    ; => [{:_id #<ObjectId> :name "Banana" :color "yellow" :price 100}
    ;     {:_id #<ObjectId> :name "Apple" :color "red" :price 80}
    ;     {:_id #<ObjectId> :name "Lemon" :color "yellow" :price 50}]

    ;; Find:
    (restrict :color :yellow :fruits)
    ; => [banana lemon]

    (restrict :price {< 100} :fruits)
    ; => [apple lemon]

    ;; Sort:
    (order :price :asc :fruits)
    ; => [lemon apple banana]

    ;; Find and sort:
    (order :price :asc (restrict :color :yellow :fruits))
    ; => [lemon banana]

    ;; Fetch first:
    (fetch-one (order :price :desc (restrict :color :yellow :fruits)))))
    ; => banana


Connect to a MongoDB server.

(mongo {:host "" :port 27017})
; Connect with options
(mongo {:host "" :port 27017} {:safe true :socketTimeout 5})

mongo returns a Mongo instance of Mongo Java Driver. It has connection pooling.

(with-mongo [connection {:host "" :port 27017} {:safe true}]

with-mongo binds a Mongo instance. The mongo instance is closed automatically.

Use a database

(database connection :your-database)

database returns a DB instance.

(with-db-binding (database connection :your-database)

with-db-binding binds a specified database to the *db* global var dynamically, and most functions in Mongoika use it. This macro calls .requestStart before the body is executed, and .requestDone after.

(set-default-db! (database connection :your-database)

You can use set-default-db! to set a database to *db* globally.

bound-db returns the current database.


(insert! :foods {:name "Cheese" :quantity 120 :price 300})
; => db.foods.insert({name: "Cheese", quantity: 120, price 300})

insert! inserts a document to the specified collection, and returns the inserted document as a map that has an _id field. Each key in a map returned from insert! is converted to a keyword.

(insert-multi! :foods
               {:name "Cookie" :quantity 70 :price 120}
               {:name "Banana" :quantity 40 :price 100}
               {:name "Chunky Bacon" :quantity 600 :price 800})

insert-multi! inserts multiple documents, and returns inserted documents.


(query :foods)
; => db.foods.find()

query makes a query behaved like a lazy sequence that contains all documents in the specified collection.

(doseq [food (query :foods)]
  (println (:name food)))

This code prints names of all foods.


(restrict :name "Cheese" :foods)
; => db.foods.find({name: "Cheese"})
(restrict :quantity {:$gt 100} :price {:$lt 300} :foods)
; => db.foods.find({quantity: {$gt: 100}, price: {$lt: 300}})

You can use following functions as operators in conditions.

> => < <= mod type not
(restrict :quantity {> 100} :price {< 300} :foods)
; => db.foods.find({quantity: {$gt: 100}, price: {$lt: 300}})


(project :name :price :foods)
; => db.foods.find({}, {name: 1, price: 1})
(project :name :price (restrict :price {> 100} :foods))
; => db.foods.find({price: {$gt: 100}}, {name: 1, price: 1})
(project {:price false :quantity false} :foods)
; => db.foods.find({}, {price: 0, quantity: 0})


(order :price :asc :foods)
; => db.foods.find().sort({price: 1})
(order :price :desc :name :asc (restrict :quantity {< 100} :foods))
; => db.foods.find({quantity: {$lt: 100}}).sort({price: -1, name: 1})

order sorts documents that are returned from the specified query. You can use :asc and :desc instead of 1 and -1.

(reverse-order (order :price :asc :name :desc :foods))
; => db.foods.find().sort({price: -1, name: 1})

`reverse-order` reverses the order of the specified query.

#### Limit

(limit 3 :foods)
; => db.foods.find().limit(3)
(limit 2 (order :price :asc (restrict :price {> 50} :foods)))
; => db.foods.find({price: {$gt: 50}}).sort({price: 1}).limit(2)


(skip 2 :foods)
; => db.foods.find().skip(2)
(skip 3 (order :price :asc :foods))
; => db.foods.find().sort({price: 1}).skip(3)


(map-after #(assoc :discounted-price (* (:price %) 0.8))
           (restrict :price {> 100} :foods))

map-after applies the specified function to each document returned from the specified query, that is, map-after behaves like map, but map-after returns a new query instead of a sequence of objects.

(restrict :price {> 100}
          (map-after #(assoc :discounted-price (* (:price %) 0.8)) :foods))

You can pass a query returned from map-after to other functions that receive a query.


(count (restrict :price {> 100} :oods))

MongoDB does not return any documents when count is called.


(update! :$set {:quantity 80} (restrict :name "Banana" :foods))
; => db.foods.update({name: "Banana"}, {$set: {quantity: 80}}, false, false)
(update! :$set {:quantity 80} :$inc {:price 10} (restrict :name "Banana" :foods))
; => db.foods.update({name: "Banana"}, {$set: {quantity: 80}, $inc: {:price 10}}, false, false)

`update!` updates just one document that are returned from the specified query.

(upsert! :$set {:price 100 :quantity 80} (restrict :name "Cheese" :foods))
; => db.foods.update({name: "Cheese"}, {$set: {price: 100, autntity: 80}}, true, false)

upsert! does an "upsert" operation.

(update-multi! :$inc {:price 10} :foods)
; => db.foods.update({}, {$inc: {price: 10}}, false, true)

update-multi! updates all documents that are returned from the specified query.


(delete! (restrict :price {< 100} :foods))
; => db.foods.remove({price: {$lt: 100}})

delete! removes all documents that are returned from the specified query.

(delete-one! (restrict :price {< 100} :foods))
; => db.foods.findAndModify({query: {price: {$lt: 100}}, remove: true})

remove-one! removes just one document and returns it.


(grid-fs :images)

grid-fs returns a GridFS instance. You can pass it to functions as a query.

(query (grid-fs :images))
(restrict :_id image-id (grid-fs :images))
(count (restrict :metadata.width {> 200} (grid-fs :images)))

A map returned from a GridFS query has a :data field, and it's value is an InputStream of bytes.

You can use insert!, insert-multi! and delete! for GridFS, but update!, upsert! and update-multi! does not support GridFS.

(insert! {:data byte-array-or-iterator
          :filename "image1.png"
          :contentType "image/png"}
         (grid-fs :images))
(delete! (restrict :_id image-id (grid-fs :images)))



[mongoika "0.6.12"]

to your project.clj.


Mongoika is named from a cuttlefish called "Mongou Ika" (紋甲イカ) in Japanese.