A relative thin wrapper around the modern API of the 5.x MongoDB Java sync driver.
Status: alpha
Download from https://github.com/lassemaatta/murmeli.
Supports the majority of the modern API.
(Proof-of-Concept) Supports transactions.
(Proof-of-Concept) Supports transforming plumatic/schema structures into draft 4 JSON schemas.
The reactive streams API is not supported.
The legacy API is not supported.
Deprecated constructs of the modern API (e.g., MapReduce) are not supported.
Use :pre conditions to check mandatory parameters.
Avoid lazyness. Use IReduceInit as the root construct for queries. It gives us better control wrt. cleaning up cursors and offers better performance. Caller can use e.g. eduction to slap more processing steps before reducing the result.
(require '[murmeli.core :as m])
(def conn (-> {:uri "mongodb://localhost:27017"
:keywords? true}
m/connect-client!
(m/with-db "some-database")))
;; A random collection name for the examples below
(def coll :some-collection)
Mongo creates collections implicitly when you insert a document. But you can also be explicit.
;; A capped collection with a size and document count limits
(m/create-collection! conn
:example-capped-coll
{:capped? true
:size-in-bytes 1024
:max-document 10})
;; A collection with an encrypted field
(m/create-collection! conn
:example-encrypted-coll
{:encrypted-fields {:fields [{:path :password
:bsonType :string
:keyId (random-uuid)
:queries {:queryType :equality}}]}})
;; A random collection name for the examples below
(def coll :some-collection)
One-by-one:
(def first-result (m/insert-one! conn coll {:boolean false
:symbol 'foobar
:integer 42
:inst #inst "2025-01-02"
:string "hello"
:keyword :some-key
:vec [1 "2" 3]
:set #{1 2 3}
:map {:foo :bar}}))
(:acknowledged? first-result)
;; => true
(m/object-id? (:_id first-result))
;; => true
(-> (m/find-by-id conn coll (:_id first-result))
(dissoc :_id))
;; => {:boolean false
;; :symbol "foobar"
;; :integer 42
;; :inst #inst "2025-01-02T00:00:00.000-00:00"
;; :string "hello"
;; :keyword "some-key"
;; :vec [1 "2" 3]
;; :set [1 3 2]
;; :map {:foo "bar"}}
Multiple at once:
(m/insert-many! conn coll [{:counter 1
:name "bar"}
{:counter 2
:name "quuz"
:location "somewhere"}
{:counter 3
:name "asdf"
:location [123 456]}
{:name "no counter here"
:aliases ["foo" "bar"]}
{:foo "bar"}])
Query options can be supplied as keyword arguments
(require '[murmeli.operators :refer [$exists $jsonSchema]])
(-> (m/find-all conn coll :query {:counter {$exists 1}} :projection [:name :counter] :limit 10)
count)
;; => 3
or as a trailing map
(-> (m/find-all conn coll {:query {:counter {$exists 1}}
:projection [:name :counter]
:limit 10})
count)
;; => 3
prismatic/schema schemas for JSON schema validation / queries(require '[murmeli.validators.schema :as vs])
(require '[schema.core :as s :refer [defschema]])
(defschema MySchema
{(s/optional-key :_id) vs/ObjectId
:name s/Str
(s/optional-key :counter) s/Int
(s/optional-key :aliases) #{s/Str}
(s/optional-key :location) (s/cond-pre
s/Str
[s/Int])})
(def json-schema (vs/schema->json-schema MySchema))
(-> (m/find-all conn coll :query {$jsonSchema json-schema})
count)
;; => 4
(m/count-collection conn coll)
;; => 6
(m/with-session [conn (m/with-client-session-options conn {:read-preference :nearest})]
(m/insert-one! conn coll {:name "foo"})
(m/insert-one! conn coll {:name "quuz"}))
(m/count-collection conn coll)
;; => 8
(try
(m/with-session [conn (m/with-client-session-options conn {:read-preference :nearest})]
(m/insert-one! conn coll {:name "another"})
(m/insert-one! conn coll {:name "one"})
;; Something goes wrong within `with-session`
(throw (RuntimeException. "oh noes")))
(catch Exception _
;; Rollback occurs
nil))
(m/count-collection conn coll)
;; => 8
(require '[schema.coerce :as sc])
(def coerce-my-record! (sc/coercer! MySchema sc/json-coercion-matcher))
(def by-schema (->> (m/find-reducible conn coll {:query {$jsonSchema json-schema}})
(eduction (map coerce-my-record!)
(filter (comp seq :aliases)))
(into [])))
(count by-schema)
;; => 1
(every? (comp set? :aliases) by-schema)
;; => true
(m/drop-db! conn "some-database")
Initialize clj-kondo cache for the project:
bb run init-kondo!
Lint the project:
bb run lint
Unit tests (single threaded):
lein test
Unit tests (multithreaded):
lein eftest
Check code examples in this README.md:
docker compose -f docker/docker-compose.yml up -d
lein run-doc-tests
Copyright © 2024 Lasse Määttä
This program and the accompanying materials are made available under the terms of the European Union Public License 1.2 which is available at https://eupl.eu/1.2/en/
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |