list collections, start session
This commit is contained in:
parent
8c206f626d
commit
bd60183e1c
5 changed files with 166 additions and 14 deletions
|
|
@ -1,6 +1,11 @@
|
|||
# Change Log
|
||||
All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- list collections
|
||||
- start session
|
||||
|
||||
## [0.3.1]
|
||||
### Added
|
||||
- More documentation
|
||||
|
|
|
|||
12
README.md
12
README.md
|
|
@ -40,13 +40,15 @@ For Leinengen, add this to your project.clj:
|
|||
|
||||
## Getting started
|
||||
|
||||
```clojure
|
||||
(ns my.app
|
||||
(:require [mongo-driver-3.client :as mcl]))
|
||||
```
|
||||
|
||||
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
|
||||
(ns my.app
|
||||
(:require [mongo-driver-3.client :as mcl]))
|
||||
|
||||
(mcl/connect-to-db "mongodb://localhost:27017/my-db")
|
||||
; =>
|
||||
; {
|
||||
|
|
@ -55,7 +57,7 @@ We usually start by creating a client and connecting to a database with a connec
|
|||
; }
|
||||
```
|
||||
|
||||
You can also create a client and get a DB manually:
|
||||
You can also create a client and get a DB separately:
|
||||
|
||||
```clojure
|
||||
;; Calling create without an arg will try and connect to the default host/port.
|
||||
|
|
@ -105,7 +107,7 @@ As an example:
|
|||
```
|
||||
|
||||
While most options are supported directly, sometimes you may need to some extra control.
|
||||
In such cases, you can pass in a configured java options object as option. Any other
|
||||
In such cases, you can pass in a configured java options object. Any other
|
||||
options will be applied on top of this object.
|
||||
|
||||
```clojure
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
(ns mongo-driver-3.client
|
||||
(:refer-clojure :exclude [find])
|
||||
(:require [mongo-driver-3.collection :as mc])
|
||||
(:import (com.mongodb.client MongoClients MongoClient)
|
||||
(com.mongodb ConnectionString)))
|
||||
(com.mongodb ConnectionString ClientSessionOptions TransactionOptions)
|
||||
(java.util.concurrent TimeUnit)))
|
||||
|
||||
;;; Core
|
||||
|
||||
|
|
@ -28,6 +30,93 @@
|
|||
[^MongoClient client]
|
||||
(.close client))
|
||||
|
||||
(defn ->TransactionOptions
|
||||
"Coerces options map into a TransactionOptions."
|
||||
[{:keys [read-concern read-preference max-commit-time-ms] :as opts}]
|
||||
(-> (TransactionOptions/builder)
|
||||
(#(if max-commit-time-ms (.maxCommitTime % max-commit-time-ms (TimeUnit/MILLISECONDS)) %))
|
||||
(#(if-let [rp (mc/->ReadPreference read-preference)] (.readPreference % rp) %))
|
||||
(#(if-let [rc (mc/->ReadConcern read-concern)] (.readConcern % rc) %))
|
||||
(#(if-let [wc (mc/->WriteConcern opts)] (.writeConcern % wc) %))
|
||||
(.build)))
|
||||
|
||||
(defn ->ClientSessionOptions
|
||||
"Coerces an options map into a ClientSessionOptions.
|
||||
|
||||
See `start-session` for usage"
|
||||
[{:keys [client-session-options causally-consistent?] :as opts}]
|
||||
(let [trans-opts (->TransactionOptions opts)]
|
||||
(-> (if client-session-options (ClientSessionOptions/builder client-session-options) (ClientSessionOptions/builder))
|
||||
(.defaultTransactionOptions trans-opts)
|
||||
(#(if (some? causally-consistent?) (.causallyConsistent % causally-consistent?) %))
|
||||
(.build))))
|
||||
|
||||
(defn start-session
|
||||
"Creates a client session.
|
||||
|
||||
Arguments:
|
||||
|
||||
- `client` a MongoClient
|
||||
- `opts` (optional), a map of:
|
||||
- `:max-commit-time-ms` Max execution time for commitTransaction operation, in milliseconds
|
||||
- `:causally-consistent?` whether operations using session should be causally consistent with each other
|
||||
- `:read-preference` Accepts a ReadPreference or a kw corresponding to one:
|
||||
[:primary, :primaryPreferred, :secondary, :secondaryPreferred, :nearest]
|
||||
Invalid values will throw an exception.
|
||||
- `:read-concern` Accepts a ReadConcern or kw corresponding to one:
|
||||
[:available, :default, :linearizable, :local, :majority, :snapshot]
|
||||
Invalid values will throw an exception.
|
||||
- `:write-concern` A WriteConcern or kw corresponding to one:
|
||||
[:acknowledged, :journaled, :majority, :unacknowledged, :w1, :w2, :w3],
|
||||
defaulting to :acknowledged, if some invalid option is provided.
|
||||
- `:write-concern/w` an int >= 0, controlling the number of replicas to acknowledge
|
||||
- `:write-concern/w-timeout-ms` How long to wait for secondaries to acknowledge before failing,
|
||||
in milliseconds (0 means indefinite).
|
||||
- `:write-concern/journal?` If true, block until write operations have been committed to the journal.
|
||||
- `:client-session-options` a ClientSessionOptions, for configuring directly. If specified, any
|
||||
other [preceding] query options will be applied to it."
|
||||
([client] (start-session client {}))
|
||||
([client opts]
|
||||
(.startSession client (->ClientSessionOptions opts))))
|
||||
|
||||
(defn collections
|
||||
"Lists collections in a database, returning as a seq of maps unless otherwise configured.
|
||||
|
||||
Arguments:
|
||||
|
||||
- `db` a MongoDatabase
|
||||
- `opts` (optional), a map of:
|
||||
- `:name-only?` returns just the string names
|
||||
- `:keywordize?` keywordize the keys of return results, default: true. Only applicable if `:name-only?` is false.
|
||||
- `:raw?` return the mongo iterable directly instead of processing into a seq, default: false
|
||||
- `:session` a ClientSession"
|
||||
([db] (collections db {}))
|
||||
([db {:keys [raw? keywordize? session] :or {keywordize? true}}]
|
||||
(let [it (if session
|
||||
(.listCollections db session)
|
||||
(.listCollections db))]
|
||||
(if-not raw?
|
||||
(map #(mc/from-document % keywordize?) (seq it))
|
||||
it))))
|
||||
|
||||
(defn collection-names
|
||||
"Lists collection names in a database, returning as a seq of strings unless otherwise configured.
|
||||
|
||||
Arguments:
|
||||
|
||||
- `db` a MongoDatabase
|
||||
- `opts` (optional), a map of:
|
||||
- `:raw?` return the mongo MongoIterable directly instead of processing into a seq, default: false
|
||||
- `:session` a ClientSession"
|
||||
([db] (collection-names db {}))
|
||||
([db opts]
|
||||
(let [it (if-let [session (:session opts)]
|
||||
(.listCollectionNames db session)
|
||||
(.listCollectionNames db))]
|
||||
(if-not (:raw? opts)
|
||||
(seq it)
|
||||
it))))
|
||||
|
||||
;;; Utility
|
||||
|
||||
(defn connect-to-db
|
||||
|
|
|
|||
|
|
@ -1,21 +1,77 @@
|
|||
(ns mongo-driver-3.client-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[mongo-driver-3.client :as mg])
|
||||
(:import (com.mongodb.client MongoClient MongoDatabase)))
|
||||
[mongo-driver-3.client :as mg]
|
||||
[mongo-driver-3.collection :as mc])
|
||||
(:import (com.mongodb.client MongoClient MongoDatabase MongoIterable ListCollectionsIterable ClientSession)
|
||||
(java.util UUID)
|
||||
(com.mongodb ClientSessionOptions ReadConcern ReadPreference)
|
||||
(java.util.concurrent TimeUnit)))
|
||||
|
||||
;;; Unit
|
||||
|
||||
(deftest test->ClientSessionOptions
|
||||
(is (instance? ClientSessionOptions (mg/->ClientSessionOptions {})))
|
||||
(are [expected arg]
|
||||
(= expected (.isCausallyConsistent (mg/->ClientSessionOptions {:causally-consistent? arg})))
|
||||
true true
|
||||
false false
|
||||
nil nil)
|
||||
(is (= 7 (.getMaxCommitTime (.getDefaultTransactionOptions
|
||||
(mg/->ClientSessionOptions {:max-commit-time-ms 7})) (TimeUnit/MILLISECONDS))))
|
||||
(is (= (ReadConcern/AVAILABLE) (.getReadConcern (.getDefaultTransactionOptions
|
||||
(mg/->ClientSessionOptions {:read-concern :available})))))
|
||||
(is (= (ReadPreference/primary) (.getReadPreference (.getDefaultTransactionOptions (mg/->ClientSessionOptions {:read-preference :primary})))))
|
||||
(is (nil? (.getWriteConcern (.getDefaultTransactionOptions (mg/->ClientSessionOptions {})))))
|
||||
(is (= 1 (.getW (.getWriteConcern (.getDefaultTransactionOptions (mg/->ClientSessionOptions {:write-concern/w 1}))))))
|
||||
(let [opts (.build (.causallyConsistent (ClientSessionOptions/builder) true))]
|
||||
(is (= opts (mg/->ClientSessionOptions {:client-session-options opts})) "configure directly")))
|
||||
|
||||
;;; Integration
|
||||
|
||||
; docker run -it --rm -p 27017:27017 mongo:4.2
|
||||
; docker run -it --rm -p 27017:27017 mongo:4.2 --replset rs1
|
||||
|
||||
(def mongo-host (or (System/getenv "MONGO_HOST") "mongodb://localhost:27017"))
|
||||
(def mongo-host "mongodb://localhost:27017")
|
||||
|
||||
(deftest test-create
|
||||
(deftest ^:integration test-create
|
||||
(is (instance? MongoClient (mg/create)))
|
||||
(is (instance? MongoClient (mg/create mongo-host))))
|
||||
|
||||
(deftest test-connect-to-db
|
||||
(deftest ^:integration test-connect-to-db
|
||||
(is (thrown? IllegalArgumentException (mg/connect-to-db mongo-host)))
|
||||
(let [res (mg/connect-to-db (str mongo-host "/my-db"))]
|
||||
(is (instance? MongoClient (:client res)))
|
||||
(is (instance? MongoDatabase (:db res)))
|
||||
(is (= "my-db" (.getName (:db res))))))
|
||||
(is (= "my-db" (.getName (:db res))))))
|
||||
|
||||
(def client (atom nil))
|
||||
|
||||
(defn- setup-connections [f]
|
||||
(reset! client (mg/create mongo-host))
|
||||
;; Ensure we have a replica set so we can run session tests
|
||||
(let [admin-db (mg/get-db @client "admin")]
|
||||
(try (.runCommand admin-db (mc/document {:replSetInitiate {}}))
|
||||
(catch Exception _ "already initialized")))
|
||||
(f)
|
||||
(mg/close @client))
|
||||
|
||||
(use-fixtures :once setup-connections)
|
||||
|
||||
(defn new-db
|
||||
[client]
|
||||
(mg/get-db client (.toString (UUID/randomUUID))))
|
||||
|
||||
(deftest ^:integration test-collection-names
|
||||
(let [db (new-db @client)
|
||||
_ (mc/create db "test")]
|
||||
(is (= ["test"] (mg/collection-names db)))
|
||||
(is (instance? MongoIterable (mg/collection-names db {:raw? true})))))
|
||||
|
||||
(deftest ^:integration test-collections
|
||||
(let [db (new-db @client)
|
||||
_ (mc/create db "test")]
|
||||
(is (= ["test"] (map :name (mg/collections db))))
|
||||
(is (= ["test"] (map #(get % "name") (mg/collections db {:keywordize? false}))))
|
||||
(is (instance? ListCollectionsIterable (mg/collections db {:raw? true})))))
|
||||
|
||||
(deftest ^:integration test-start-session
|
||||
(is (instance? ClientSession (mg/start-session @client))))
|
||||
|
|
@ -184,7 +184,7 @@
|
|||
|
||||
;;; Integration
|
||||
|
||||
; docker run -it --rm -p 27017:27017 mongo:4.2
|
||||
; docker run -it --rm -p 27017:27017 mongo:4.2 --replset rs1
|
||||
|
||||
(def client (atom nil))
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue