Revise
RethinkDB client for Clojure
Install / Use
/learn @bitemyapp/ReviseREADME
Revise
Clojure RethinkDB client. Asynchronous, lock-free, efficient, and easy to use!
Query RethinkDB using semantics familiar to any Clojure programmer.
Stability
Alpha-grade at this point, we're seeking people who want to use Clojure and RethinkDB to help us harden it up.
We're confident this is already one of the more feature-complete community-maintained libraries.
Leiningen
Connection Management
Introduction
These docs are - for now - loosely based on the python api docs. The driver
works on version 1.9 and 1.10 (in our testing so far) of RethinkDB.
Usage
(require '[bitemyapp.revise.connection :refer [connect close]])
(require '[bitemyapp.revise.query :as r])
(require '[bitemyapp.revise.core :refer [run run-async]])
;; connect returns the connection agent
(let [local-conn (connect) ;; defaults to localhost
;; pass in connection options map to specify beyond the defaults
remote-conn (connect {:host "99.99.99.1"
:port 28015
:auth-key ""})
;; Run a query and return the result. Blocks as long as it needs to
;; get a result (or an error)
response1 (-> (r/db "test") (r/table-create-db "authors") (run local-conn))
;; We may be having issues so we specify a timeout to run
response2 (-> (r/db "test") (r/table-list-db) (run remote-conn 15000))
;; We want to run a query asynchronously - giving up the error handling
response3 (-> (r/db-list) (run-async local-conn))]
;; dereference the promise to block on it.
(println @response3)
;; We are done using the local connection
(close local-conn))
Connecting to RethinkDB
Inside the namespace bitemyapp.revise.connection there are 2 functions we need:
connect([& [conn-map]])close([conn])
connect takes an optional connection map to override any or all of the default
values:
:host"127.0.0.1":port28015:token0The token of the first query to the connection. Autoincrements.:auth-key""The authentication key.
Connect will return an agent to which you can send queries.
To close the connection use the function close with the agent as argument.
Sending queries
Inside the namespace bitemyapp.revise.core there are again 2 functions we need:
run([query connection & [timeout]])run-async([query connection])
Our queries are compiled and sent to the connection using those two functions.
run takes an optional timeout in milliseconds (default 10000) and will block
until it has a response or it times out. It will throw when it times out or the
agent dies due to an exception when sending a query.
run will return a map which includes the autoincrementing token that was
implicitly sent to the agent and either a :response in case the query was
successful or an :error, :response and :backtrace in case there was
an error with our request (in this case the driver doesn't throw an exception).
Alternatively we might decide to use run-async to send and run queries
asynchronously. This will return us a promise which we can dereference.
Note that run-async gives up the error handling of run. The agent might
die and you will have to check for it manually.
After dereferencing the promise the return value will be the same as run.
Compiling a query manually
run and run-async have an implicit call to bitemyapp.revise.protoengine/compile-term.
This compiles the query into protocol buffers. If you know about the official
RethinkDB API and you want to inspect the protocol buffers Revise gives you, you
can compile a query using that function. To send manually compiled queries to the
database, use send-term in the bitemyapp.revise.connection namespace.
That will be the equivalent of using run-async.
API
The api is under the namespace bitemyapp.revise.query.
(require '[bitemyapp.revise.query :as r])
Note: rethinkdb doesn't let you use hyphens (-) as part of database or table
names. Revise won't 'fix' those names for you.
Also note that keywords and strings are interchangeable.
Lambdas
Many queries such as map, filter, etc. support lambdas. Lambdas are anonymous
functions with syntax like clojure's fn.
Example:
(-> [1 2 3 4 5 6 7]
(r/map (r/lambda [n]
(r/* n 2)))
(run conn))
This will give you the response ([2 4 6 8 10 12 14])
Manipulating databases
db-create
([db-name])
Create a database.
(-> (r/db-create "my_db") (run conn))
db-drop
([db-name])
Drop a database.
(-> (r/db-drop "my_db") (run conn))
db-list
([])
List the database names in the system.
(-> (r/db-list) (run conn))
Manipulating tables
table-create-db
([db table-name & {:as optargs}])
Create a table on the specified database. The following options are available:
:primary-keyThe name of the primary key. Default::id.:durabilityIf set to:soft, this enables soft durability on this table: writes will be acknowledged by the server immediately and flushed to disk in the background. Default is:hard(acknowledgement of writes happens after data has been written to disk).:cache-sizeSet the cache size (in bytes) to be used by the table. The default is 1073741824 (1024MB).:datacenterThe name of the datacenter this table should be assigned to.
(-> (r/db "test") (r/table-create-db "authors") (run conn))
(-> (r/db "test") (r/table-create-db "users" :primary-key :email) (run conn))
table-create
([table-name & {:as optargs}])
Like table-create-db except that the db is the default db.
(-> (r/table-create "authors") (run conn))
table-drop-db
([db table-name])
Drop a table from a specific db. The table and all its data will be deleted.
(-> (r/db "test") (r/table-drop-db "authors") (run conn))
table-drop
([table-name])
Like table-drop-db except the default db is used.
(-> (r/table-drop "authors") (run conn))
index-create
([table index-name lambda1 & [multi?]])
Create a new secondary index with a given name on the specified table.
(-> (r/table "authors")
(r/index-create :author
(r/lambda [author]
(r/get-field author :name)))
(run conn))
;; Compound index
(-> (r/table "authors")
(r/index-create :name-tv-show
(r/lambda [author]
[(r/get-field author :name)
(r/get-field author :tv-show)]))
(run conn))
;; A multi index. The r/lambda of a multi index should return an array. It will allow
;; you to query based on whether a value is present in the returned array
(-> (r/table "authors")
(r/index-create :posts
(r/lambda [author]
(r/get-field author :posts)) ; returns an array
true) ; :multi -> true
(run conn))
index-drop
([table index-name])
Delete a previously created secondary index of this table.
(-> (r/table "authors") (r/index-drop :posts) (run conn))
index-list
([table])
List all the secondary indexes of this table.
(-> (r/table "authors") (r/index-list) (run conn))
Writing data
insert
([table data & {:as optargs}])
Insert json documents into a table. Accepts a single json document (a clojure map) or an array of documents (a clojure vector of clojure maps).
Accepts the following options:
:upsertAbool. Default is true. If true it will overwrite documents that already exist.:durability:softor:hard. Override the durability of the table for this operation.:return-valsAbool. Only valid for single object inserts. Iftrueyou get back the row you inserted on the key:nev_val. And if you overwrote a row it will be in:old_val
(def authors [{:name "William Adama" :tv-show "Battlestar Galactica"
:posts [{:title "Decommissioning speech",
:rating 3.5
:content "The Cylon War is long over..."},
{:title "We are at war",
:content "Moments ago, this ship received word..."},
{:title "The new Earth",
:content "The discoveries of the past few days..."}]}
{:name "Laura Roslin", :tv-show "Battlestar Galactica",
:posts [{:title "The oath of office",
:rating 4
:content "I, Laura Roslin, ..."},
{:title "They look like us",
:content "The Cylons have the ability..."}]}])
(def jean-luc {:name "Jean-Luc Picard", :tv-show "Star Trek TNG",
:posts [{:title "Civil rights",
:content "There are some words I've known since..."}]})
(-> (r/table "authors")
(r/insert authors)
(run conn))
(-> (r/table "authors")
(r/insert jean-luc :return-vals true)
(run conn))
Insert returns a map with the following attributes:
:insertedThe number of documents that were succesfully inserted.:replacedThe number of documents that were updated when upsert is used.:unchangedThe number of documents that would have been modified, except that the new value was the same as the old value when doing an upsert.:errorsThe number of errors encountered while inserting; if errors were encountered while inserting, first_error contains the text of the first error.:generated_keysA list of generated primary key values deleted and skipped: 0 for an insert operation.
If you specified :return-vals true you will also get the following keys:
:nev_valThe value of t
Related Skills
node-connect
347.6kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
108.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
347.6kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
347.6kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
