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
|
# 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/).
|
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]
|
## [0.3.1]
|
||||||
### Added
|
### Added
|
||||||
- More documentation
|
- More documentation
|
||||||
|
|
|
||||||
12
README.md
12
README.md
|
|
@ -40,13 +40,15 @@ For Leinengen, add this to your project.clj:
|
||||||
|
|
||||||
## Getting started
|
## 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.
|
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.
|
`connect-to-db` is a convenience function that allows you to do this directly.
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(ns my.app
|
|
||||||
(:require [mongo-driver-3.client :as mcl]))
|
|
||||||
|
|
||||||
(mcl/connect-to-db "mongodb://localhost:27017/my-db")
|
(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
|
```clojure
|
||||||
;; Calling create without an arg will try and connect to the default host/port.
|
;; 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.
|
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.
|
options will be applied on top of this object.
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
(ns mongo-driver-3.client
|
(ns mongo-driver-3.client
|
||||||
(:refer-clojure :exclude [find])
|
(:refer-clojure :exclude [find])
|
||||||
|
(:require [mongo-driver-3.collection :as mc])
|
||||||
(:import (com.mongodb.client MongoClients MongoClient)
|
(:import (com.mongodb.client MongoClients MongoClient)
|
||||||
(com.mongodb ConnectionString)))
|
(com.mongodb ConnectionString ClientSessionOptions TransactionOptions)
|
||||||
|
(java.util.concurrent TimeUnit)))
|
||||||
|
|
||||||
;;; Core
|
;;; Core
|
||||||
|
|
||||||
|
|
@ -28,6 +30,93 @@
|
||||||
[^MongoClient client]
|
[^MongoClient client]
|
||||||
(.close 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
|
;;; Utility
|
||||||
|
|
||||||
(defn connect-to-db
|
(defn connect-to-db
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,77 @@
|
||||||
(ns mongo-driver-3.client-test
|
(ns mongo-driver-3.client-test
|
||||||
(:require [clojure.test :refer :all]
|
(:require [clojure.test :refer :all]
|
||||||
[mongo-driver-3.client :as mg])
|
[mongo-driver-3.client :as mg]
|
||||||
(:import (com.mongodb.client MongoClient MongoDatabase)))
|
[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
|
;;; 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)))
|
||||||
(is (instance? MongoClient (mg/create mongo-host))))
|
(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)))
|
(is (thrown? IllegalArgumentException (mg/connect-to-db mongo-host)))
|
||||||
(let [res (mg/connect-to-db (str mongo-host "/my-db"))]
|
(let [res (mg/connect-to-db (str mongo-host "/my-db"))]
|
||||||
(is (instance? MongoClient (:client res)))
|
(is (instance? MongoClient (:client res)))
|
||||||
(is (instance? MongoDatabase (:db 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
|
;;; 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))
|
(def client (atom nil))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue