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(mongo {:host "127.0.0.1" :port 27017}); Connect with options
(mongo {:host "127.0.0.1" :port 27017} {:safe true :socketTimeout 5})mongo returns a Mongo instance of Mongo Java Driver. It has connection pooling.
(with-mongo [connection {:host "127.0.0.1" :port 27017} {:safe true}]
...)with-mongo binds a Mongo instance. The mongo instance is closed automatically.
(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
```clojure
(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.
```clojure
(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)))Add
[mongoika "0.6.12"]to your project.clj.
Mongoika is named from a cuttlefish called "Mongou Ika" (紋甲イカ) in Japanese.