SkillAgentSearch skills...

Revise

RethinkDB client for Clojure

Install / Use

/learn @bitemyapp/Revise
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

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

"Leiningen version"

Connection Management

A brief explanation is here

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"
  • :port 28015
  • :token 0 The 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-key The name of the primary key. Default: :id.
  • :durability If 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-size Set the cache size (in bytes) to be used by the table. The default is 1073741824 (1024MB).
  • :datacenter The 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:

  • :upsert A bool. Default is true. If true it will overwrite documents that already exist.
  • :durability :soft or :hard. Override the durability of the table for this operation.
  • :return-vals A bool. Only valid for single object inserts. If true you 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:

  • :inserted The number of documents that were succesfully inserted.
  • :replaced The number of documents that were updated when upsert is used.
  • :unchanged The number of documents that would have been modified, except that the new value was the same as the old value when doing an upsert.
  • :errors The number of errors encountered while inserting; if errors were encountered while inserting, first_error contains the text of the first error.
  • :generated_keys A 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_val The value of t

Related Skills

View on GitHub
GitHub Stars146
CategoryDevelopment
Updated11d ago
Forks7

Languages

Clojure

Security Score

80/100

Audited on Mar 23, 2026

No findings