2019-11-14 02:01:38 +00:00
|
|
|
# mongo-driver-3
|
|
|
|
|
|
2019-11-14 03:40:39 +00:00
|
|
|
[](https://clojars.org/mongo-driver-3)
|
|
|
|
|
|
2019-11-17 06:17:43 +00:00
|
|
|
[](https://cljdoc.org/d/mongo-driver-3/mongo-driver-3/CURRENT)
|
2019-11-17 06:04:37 +00:00
|
|
|
|
|
|
|
|
|
2021-02-12 05:24:16 +00:00
|
|
|
A Mongo client for clojure, lightly wrapping 3.11/4.0+ versions of the [MongoDB Java Driver](https://mongodb.github.io/mongo-java-driver/)
|
2019-11-14 02:01:38 +00:00
|
|
|
|
|
|
|
|
In general, it will feel familiar to users of mongo clients like [monger](https://github.com/michaelklishin/monger).
|
|
|
|
|
Like our HTTP/2 client [hato](https://github.com/gnarroway/hato), the API is designed to be idiomatic and to make common
|
|
|
|
|
tasks convenient, whilst still allowing the underlying client to be configured via native Java objects.
|
|
|
|
|
|
|
|
|
|
It was developed with the following goals:
|
|
|
|
|
|
2019-11-17 06:00:30 +00:00
|
|
|
- Simple
|
2019-11-14 02:01:38 +00:00
|
|
|
- Up to date with the latest driver versions
|
2019-11-17 06:00:30 +00:00
|
|
|
- Minimal layer that does not prevent access to the underlying driver
|
2019-11-14 02:01:38 +00:00
|
|
|
- Consistent API across all functions
|
|
|
|
|
- Configuration over macros
|
2019-11-17 06:00:30 +00:00
|
|
|
|
2019-11-14 02:01:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
## Status
|
|
|
|
|
|
2022-10-04 11:04:31 +00:00
|
|
|
mongo-driver-3 is used in production, and the existing public API will be maintained.
|
2019-11-14 02:01:38 +00:00
|
|
|
Please try it out and raise any issues you may find.
|
|
|
|
|
|
|
|
|
|
## Usage
|
|
|
|
|
|
|
|
|
|
For Leinengen, add this to your project.clj:
|
|
|
|
|
|
|
|
|
|
```clojure
|
|
|
|
|
;; The underlying driver -- any newer version can also be used
|
2024-02-07 03:07:55 +00:00
|
|
|
[org.mongodb/mongodb-driver-sync "4.11.1"]
|
2019-11-14 02:01:38 +00:00
|
|
|
|
|
|
|
|
;; This wrapper library
|
2024-02-07 03:09:13 +00:00
|
|
|
[mongo-driver-3 "0.8.0"]
|
2019-11-14 02:01:38 +00:00
|
|
|
```
|
|
|
|
|
|
2019-11-17 06:00:30 +00:00
|
|
|
## Getting started
|
|
|
|
|
|
|
|
|
|
```clojure
|
|
|
|
|
(ns my.app
|
|
|
|
|
(:require [mongo-driver-3.client :as mcl]))
|
2019-11-19 05:28:46 +00:00
|
|
|
```
|
2019-11-17 06:00:30 +00:00
|
|
|
|
2019-11-19 05:28:46 +00:00
|
|
|
We usually start by creating a client and connecting to a database with a connection string.
|
|
|
|
|
`connect-to-db` is a convenience function that allows you to do this directly.
|
|
|
|
|
|
|
|
|
|
```clojure
|
2019-11-17 06:00:30 +00:00
|
|
|
(mcl/connect-to-db "mongodb://localhost:27017/my-db")
|
|
|
|
|
; =>
|
|
|
|
|
; {
|
|
|
|
|
; :client - a MongoClient instance
|
|
|
|
|
; :db - a Database that you can pass to all the collection functions
|
|
|
|
|
; }
|
|
|
|
|
```
|
|
|
|
|
|
2019-11-19 05:28:46 +00:00
|
|
|
You can also create a client and get a DB separately:
|
2019-11-17 06:00:30 +00:00
|
|
|
|
|
|
|
|
```clojure
|
|
|
|
|
;; Calling create without an arg will try and connect to the default host/port.
|
|
|
|
|
(def client (mcl/create "mongodb://localhost:27017"))
|
|
|
|
|
|
|
|
|
|
;; Create a db that you can pass around.
|
2019-11-17 06:06:57 +00:00
|
|
|
(def db (mcl/get-db client "my-db"))
|
2019-11-17 06:00:30 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Collection functions
|
|
|
|
|
|
2019-11-17 07:36:10 +00:00
|
|
|
All the collection functions closely mirror the naming in the corresponding java driver
|
2019-11-17 06:00:30 +00:00
|
|
|
[module](https://mongodb.github.io/mongo-java-driver/3.11/javadoc/com/mongodb/client/MongoCollection.html).
|
|
|
|
|
|
|
|
|
|
They always take a db as the first argument, collection name as the second,
|
|
|
|
|
and an optional map of options as the last. Full documentation of options can be found on
|
|
|
|
|
[cljdoc](https://cljdoc.org/d/mongo-driver-3/mongo-driver-3/CURRENT/api/mongo-driver-3.collection).
|
|
|
|
|
|
|
|
|
|
As an example:
|
|
|
|
|
|
|
|
|
|
```clojure
|
|
|
|
|
(ns my.app
|
|
|
|
|
(:require [mongo-driver-3.collection :as mc]))
|
|
|
|
|
|
|
|
|
|
;; Insert some documents
|
|
|
|
|
(mc/insert-many db "test" [{:v "hello"} {:v "world"}])
|
|
|
|
|
|
|
|
|
|
;; Count all documents
|
|
|
|
|
(mc/count-documents db "test")
|
|
|
|
|
; => 2
|
|
|
|
|
|
|
|
|
|
;; Count with a query
|
|
|
|
|
(mc/count-documents db "test" {:v "hello"})
|
|
|
|
|
; => 1
|
|
|
|
|
|
|
|
|
|
;; Find the documents, returning a seq
|
2019-11-17 07:36:10 +00:00
|
|
|
(mc/find db "test" {} {:limit 1 :projection {:_id 0}})
|
2019-11-17 06:00:30 +00:00
|
|
|
; => ({:v "hello"})
|
|
|
|
|
|
|
|
|
|
;; Find the documents, returning the raw FindIterable response
|
2019-11-17 07:36:10 +00:00
|
|
|
(mc/find db "test" {} {:raw? true})
|
2019-11-17 06:00:30 +00:00
|
|
|
; => a MongoIterable
|
|
|
|
|
|
|
|
|
|
;; Find a single document or return nil
|
2019-11-17 06:06:57 +00:00
|
|
|
(mc/find-one db "test" {:v "world"} {:keywordize? false})
|
2019-11-17 06:00:30 +00:00
|
|
|
; => {"v" "world"}
|
2024-02-07 03:07:55 +00:00
|
|
|
|
|
|
|
|
;; Avoid laziness in queries
|
|
|
|
|
(mc/find db "test" {} {:realise-fn (partial into [])}
|
|
|
|
|
; => [...]
|
2019-11-17 06:00:30 +00:00
|
|
|
```
|
|
|
|
|
|
2019-11-17 06:21:52 +00:00
|
|
|
While most options are supported directly, sometimes you may need to some extra control.
|
2019-11-19 05:28:46 +00:00
|
|
|
In such cases, you can pass in a configured java options object. Any other
|
2019-11-17 06:21:52 +00:00
|
|
|
options will be applied on top of this object.
|
2019-11-17 06:00:30 +00:00
|
|
|
|
|
|
|
|
```clojure
|
|
|
|
|
;; These are equivalent
|
|
|
|
|
(mc/rename db "test" "new-test" {:drop-target? true})
|
|
|
|
|
|
|
|
|
|
(mc/rename db "test" "new-test" {:rename-collection-options (.dropTarget (RenameCollectionOptions.) true)})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Again, read the [docs](https://cljdoc.org/d/mongo-driver-3/mongo-driver-3/CURRENT/api/mongo-driver-3.collection)
|
|
|
|
|
for full API documentation.
|
|
|
|
|
|
2019-11-22 05:49:46 +00:00
|
|
|
### Using operators
|
|
|
|
|
|
|
|
|
|
Many mongo queries take operators like `$eq` and `$gt`. These are exposed in the `mongo-driver-3.operator` namespace.
|
|
|
|
|
|
2019-11-22 09:55:03 +00:00
|
|
|
```clojure
|
2019-11-22 05:49:46 +00:00
|
|
|
(ns my.app
|
|
|
|
|
(:require [mongo-driver-3.collection :as mc]
|
|
|
|
|
[mongo-driver-3.operator :refer [$gt]))
|
|
|
|
|
|
|
|
|
|
(mc/find db "test" {:a {$gt 3}})
|
|
|
|
|
|
|
|
|
|
;; This is equivalent to, but with less chance of error than:
|
2019-11-22 05:50:54 +00:00
|
|
|
(mc/find db "test" {:a {"$gt" 3}})
|
2019-11-22 05:49:46 +00:00
|
|
|
```
|
|
|
|
|
|
2020-01-09 15:56:48 +00:00
|
|
|
### Bulk operations
|
|
|
|
|
|
|
|
|
|
The bulk API is similar to the [mongo shell](https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/),
|
|
|
|
|
except each operation is defined as a 2-tuple rather than a map.
|
|
|
|
|
|
|
|
|
|
```clojure
|
|
|
|
|
;; Execute a mix of operations in one go
|
2021-02-12 05:24:16 +00:00
|
|
|
(bulk-write [[:insert-one {:document {:a 1}}]
|
|
|
|
|
[:delete-one {:filter {:a 1}}]
|
|
|
|
|
[:delete-many {:filter {:a 1}}]
|
|
|
|
|
[:update-one {:filter {:a 1} :update {:$set {:a 2}}}]
|
|
|
|
|
[:update-many {:filter {:a 1} :update {:$set {:a 2}}}]
|
|
|
|
|
[:replace-one {:filter {:a 1} :replacement {:a 2}}]])
|
2020-01-09 15:56:48 +00:00
|
|
|
; => a BulkWriteResult
|
|
|
|
|
|
|
|
|
|
;; Each operation can take the same options as their respective functions
|
2021-02-12 05:24:16 +00:00
|
|
|
(bulk-write [[:update-one {:filter {:a 1} :update {:$set {:a 2}} :upsert? true}]
|
|
|
|
|
[:update-many {:filter {:a 1} :update {:$set {:a 2}} :upsert? true}]
|
|
|
|
|
[:replace-one {:filter {:a 1} :replacement {:a 2} :upsert? true}]])
|
2020-01-09 15:56:48 +00:00
|
|
|
```
|
|
|
|
|
|
2019-11-22 09:55:03 +00:00
|
|
|
### Using transactions
|
|
|
|
|
|
|
|
|
|
You can create a session to perform multi-document transactions, where all operations either
|
|
|
|
|
succeed or none are persisted.
|
|
|
|
|
|
|
|
|
|
It is important to
|
|
|
|
|
use `with-open` so the session is closed after both successful and failed transactions.
|
|
|
|
|
|
|
|
|
|
```clojure
|
|
|
|
|
;; Inserts 2 documents into a collection
|
|
|
|
|
(with-open [s (mg/start-session client)]
|
|
|
|
|
(mg/with-transaction s
|
|
|
|
|
(fn []
|
|
|
|
|
(mc/insert-one my-db "coll" {:name "hello"} {:session s})
|
|
|
|
|
(mc/insert-one my-db "coll" {:name "world"} {:session s}))))
|
2022-10-04 11:04:31 +00:00
|
|
|
|
|
|
|
|
;; There is also a helper method to make this easier,
|
|
|
|
|
;; where it is not necessary to manually open or pass a session:
|
|
|
|
|
(mg/with-implicit-transaction
|
|
|
|
|
{:client client}
|
|
|
|
|
(fn []
|
|
|
|
|
(mc/insert-one my-db "coll" {:name "hello"})
|
|
|
|
|
(mc/insert-one my-db "coll" {:name "world"})))
|
2019-11-22 09:55:03 +00:00
|
|
|
```
|
|
|
|
|
|
2024-02-07 03:07:55 +00:00
|
|
|
## Development
|
|
|
|
|
|
|
|
|
|
1. Run mongo (e.g. via docker):
|
|
|
|
|
- `docker run -it --rm -p 27017:27017 mongo`
|
|
|
|
|
2. Run tests
|
|
|
|
|
- `lein test`
|
|
|
|
|
|
2019-11-14 02:01:38 +00:00
|
|
|
## License
|
|
|
|
|
|
|
|
|
|
Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
|