Skip to content

Commit

Permalink
Merge pull request #10 from jimpil/master
Browse files Browse the repository at this point in the history
various optimisations/fixes
  • Loading branch information
gnarroway authored Feb 7, 2024
2 parents 28e054a + 1de8cab commit 9c2c899
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 88 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ pom.xml.asc
/.nrepl-port
.hgignore
.hg/
.lsp/
.clj-kondo/
30 changes: 17 additions & 13 deletions src/mongo_driver_3/client.clj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
(ns mongo-driver-3.client
(:refer-clojure :exclude [find])
(:require [mongo-driver-3.model :as m])
(:require [mongo-driver-3.model :as m]
[mongo-driver-3.iterable :as iterable])
(:import (com.mongodb.client MongoClients MongoClient ClientSession MongoDatabase TransactionBody)
(com.mongodb ConnectionString ClientSessionOptions TransactionOptions)
(java.util.concurrent TimeUnit)))
Expand All @@ -15,16 +16,15 @@
`connection-string` is a mongo connection string, e.g. mongodb://localhost:27107
If a connecting string is not passed in, it will connect to the default localhost instance."
([] (MongoClients/create))
([^String connection-string]
(MongoClients/create connection-string)))
(^MongoClient [] (MongoClients/create))
(^MongoClient [^String connection-string] (MongoClients/create connection-string)))

(defn get-db
"Gets a database by name
`client` is a MongoClient, e.g. resulting from calling `connect`
`name` is the name of the database to get."
[^MongoClient client ^String name]
^MongoDatabase [^MongoClient client ^String name]
(.getDatabase client name))

(defn close
Expand All @@ -41,16 +41,20 @@
- `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
- `:raw?` return the mongo iterable directly instead of processing into a clj data-structure, default: false
- `:realise-fn` how to realise the MongoIterable, default: `clojure.core/sequence` (i.e. lazily)
- `:session` a ClientSession"
([^MongoDatabase db] (list-collections db {}))
([^MongoDatabase db {:keys [raw? keywordize? ^ClientSession session] :or {keywordize? true}}]
([^MongoDatabase db {:keys [raw? keywordize? ^ClientSession session realise-fn]
:or {keywordize? true
realise-fn sequence}}]
(let [it (if session
(.listCollections db session)
(.listCollections db))]
(if-not raw?
(map #(m/from-document % keywordize?) (seq it))
it))))
(if raw?
it
(realise-fn ;; accomodate users who don't want to use lazy-seqs
(iterable/documents it keywordize?))))))

(defn list-collection-names
"Lists collection names in a database, returning as a seq of strings unless otherwise configured.
Expand All @@ -66,9 +70,9 @@
(let [it (if-let [^ClientSession session (:session opts)]
(.listCollectionNames db session)
(.listCollectionNames db))]
(if-not (:raw? opts)
(seq it)
it))))
(if (:raw? opts)
it
(seq it)))))

(defn ->TransactionOptions
"Coerces options map into a TransactionOptions. See `start-session` for usage."
Expand Down
61 changes: 36 additions & 25 deletions src/mongo_driver_3/collection.clj
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
(ns mongo-driver-3.collection
(:refer-clojure :exclude [find empty? drop])
(:require [mongo-driver-3.model :refer :all]
[mongo-driver-3.client :refer [*session*]])
[mongo-driver-3.client :refer [*session*]]
[mongo-driver-3.iterable :as iterable])
(:import (com.mongodb MongoNamespace)
(com.mongodb.client MongoDatabase MongoCollection ClientSession)
(com.mongodb.client.model IndexModel)
Expand Down Expand Up @@ -62,23 +63,27 @@
- `:batch-size` Documents to return per batch, e.g. 1
- `:bypass-document-validation?` Boolean
- `:keywordize?` keywordize the keys of return results, default: true
- `:realise-fn` how to realise the MongoIterable, default: `clojure.core/sequence` (i.e. lazily)
- `:raw?` return the mongo AggregateIterable directly instead of processing into a seq, default: false
- `:session` a ClientSession"
([^MongoDatabase db coll pipeline]
(aggregate db coll pipeline {}))
([^MongoDatabase db coll pipeline opts]
(let [{:keys [^ClientSession session allow-disk-use? ^Integer batch-size bypass-document-validation? keywordize? raw?] :or {keywordize? true raw? false}} opts
(let [{:keys [^ClientSession session allow-disk-use? ^Integer batch-size bypass-document-validation? keywordize? raw? realise-fn]
:or {keywordize? true
realise-fn sequence}} opts
^ClientSession session (or session *session*)
it (cond-> (if session
(.aggregate (collection db coll opts) session ^List (map document pipeline))
(.aggregate (collection db coll opts) ^List (map document pipeline)))
(some? allow-disk-use?) (.allowDiskUse allow-disk-use?)
(some? bypass-document-validation?) (.bypassDocumentValidation bypass-document-validation?)
batch-size (.batchSize batch-size))]
(some? allow-disk-use?) (.allowDiskUse allow-disk-use?)
(some? bypass-document-validation?) (.bypassDocumentValidation bypass-document-validation?)
batch-size (.batchSize batch-size))]

(if-not raw?
(map (fn [x] (from-document x keywordize?)) (seq it))
it))))
(if raw?
it
(realise-fn ;; accomodate users who don't want to use lazy-seqs
(iterable/documents it keywordize?))))))

(defn bulk-write
"Executes a mix of inserts, updates, replaces, and deletes.
Expand Down Expand Up @@ -190,26 +195,30 @@
- `:sort` document representing sort order, e.g. {:timestamp -1}
- `:projection` document representing fields to return, e.g. {:_id 0}
- `:keywordize?` keywordize the keys of return results, default: true
- `:realise-fn` how to realise the MongoIterable, default: `clojure.core/sequence` (i.e. lazily)
- `:raw?` return the mongo FindIterable directly instead of processing into a seq, default: false
- `:session` a ClientSession
Additionally takes options specified in `collection`."
([^MongoDatabase db coll q]
(find db coll q {}))
([^MongoDatabase db coll q opts]
(let [{:keys [limit skip sort projection ^ClientSession session keywordize? raw?] :or {keywordize? true raw? false}} opts]
(let [^ClientSession session (or session *session*)
it (cond-> (if session
(.find (collection db coll opts) session (document q))
(.find (collection db coll opts) (document q)))
limit (.limit limit)
skip (.skip skip)
sort (.sort (document sort))
projection (.projection (document projection)))]

(if-not raw?
(map (fn [x] (from-document x keywordize?)) (seq it))
it)))))
([^MongoDatabase db coll q {:keys [limit skip sort projection ^ClientSession session keywordize? raw? realise-fn]
:or {keywordize? true
realise-fn sequence}
:as opts}]
(let [^ClientSession session (or session *session*)
it (cond-> (if session
(.find (collection db coll opts) session (document q))
(.find (collection db coll opts) (document q)))
limit (.limit limit)
skip (.skip skip)
sort (.sort (document sort))
projection (.projection (document projection)))]

(if raw?
it
(realise-fn ;; accomodate users who don't want to use lazy-seqs
(iterable/documents it keywordize?))))))

(defn find-one
"Finds a single document and returns it as a clojure map, or nil if not found.
Expand Down Expand Up @@ -493,13 +502,15 @@
(create-indexes db coll indexes {}))
([^MongoDatabase db coll indexes opts]
(->> indexes
(map (fn [x] (IndexModel. (document (:keys x)) (->IndexOptions x))))
(mapv (fn [x] (IndexModel. (document (:keys x)) (->IndexOptions x))))
(.createIndexes (collection db coll opts)))))

(defn list-indexes
"Lists indexes."
([^MongoDatabase db coll]
(list-indexes db coll {}))
([^MongoDatabase db coll opts]
(->> (.listIndexes (collection db coll opts))
(map #(from-document % true)))))
(let [it (.listIndexes (collection db coll opts))
realise-fn (:realise-fn opts sequence)]
(realise-fn
(iterable/documents it true)))))
36 changes: 31 additions & 5 deletions src/mongo_driver_3/data_literals.clj
Original file line number Diff line number Diff line change
@@ -1,10 +1,36 @@
(ns mongo-driver-3.data-literals
(:import (org.bson.types ObjectId)
(java.io Writer)))
(java.io Writer)
(java.util Date)
(java.nio ByteBuffer)))


(defmethod print-method ObjectId [c ^Writer w] (.write w ^String (str "#mongo/id \"" (.toHexString c) "\"")))
(defmethod print-dup ObjectId [c ^Writer w] (.write w ^String (str "#mongo/id \"" (.toHexString c) "\"")))
(defmethod print-method ObjectId [^ObjectId c ^Writer w] (.write w (str "#mongo/id " \" (.toHexString c) \")))
(defmethod print-dup ObjectId [^ObjectId c ^Writer w] (.write w (str "#mongo/id " \" (.toHexString c) \")))

(defn mongo-id [o]
(ObjectId. o))
(defprotocol AsObjectId
(oid-from [this]))

(extend-protocol AsObjectId
(Class/forName "[B")
(oid-from [this] (ObjectId. ^bytes this))
nil
(oid-from [_] (ObjectId.))
String
(oid-from [this] (ObjectId. this))
Date
(oid-from [this] (ObjectId. this))
ByteBuffer
(oid-from [this] (ObjectId. this))
)



(defn mongo-id ;; https://mongodb.github.io/mongo-java-driver/4.8/apidocs/bson/org/bson/types/ObjectId.html
(^ObjectId [] (ObjectId.))
(^ObjectId [o] (oid-from o))
(^ObjectId [o1 o2]
(if (and (int? o1)
(int? o2))
(ObjectId. (int o1) (int o2))
(ObjectId. ^Date o1 (int o2)))))
8 changes: 8 additions & 0 deletions src/mongo_driver_3/iterable.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(ns mongo-driver-3.iterable
(:require [mongo-driver-3.model :as m]))

(defn documents
"Given a MongoIterable <it>, returns an eduction which will
eventually yield all the documents (per `m/from-document`)."
[it keywordize?]
(eduction (map #(m/from-document % keywordize?)) it))
Loading

0 comments on commit 9c2c899

Please sign in to comment.