{"id":22039935,"url":"https://github.com/bitemyapp/revise","last_synced_at":"2025-05-07T04:10:45.800Z","repository":{"id":8544898,"uuid":"10166235","full_name":"bitemyapp/revise","owner":"bitemyapp","description":"RethinkDB client for Clojure","archived":false,"fork":false,"pushed_at":"2015-02-04T21:14:41.000Z","size":601,"stargazers_count":145,"open_issues_count":12,"forks_count":7,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-04-22T07:21:50.078Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bitemyapp.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-05-20T04:55:25.000Z","updated_at":"2025-03-04T11:07:41.000Z","dependencies_parsed_at":"2022-08-24T13:39:56.713Z","dependency_job_id":null,"html_url":"https://github.com/bitemyapp/revise","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitemyapp%2Frevise","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitemyapp%2Frevise/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitemyapp%2Frevise/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitemyapp%2Frevise/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bitemyapp","download_url":"https://codeload.github.com/bitemyapp/revise/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252810273,"owners_count":21807759,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-30T11:12:54.248Z","updated_at":"2025-05-07T04:10:45.780Z","avatar_url":"https://github.com/bitemyapp.png","language":"Clojure","funding_links":[],"categories":["Database"],"sub_categories":[],"readme":"# Revise\n\nClojure RethinkDB client. Asynchronous, lock-free, efficient, and easy to use!\n\nQuery RethinkDB using semantics familiar to any Clojure programmer.\n\n## Stability\n\nAlpha-grade at this point, we're seeking people who want to use Clojure and RethinkDB to help us harden it up.\n\nWe're confident this is already one of the more feature-complete community-maintained libraries.\n\n## Leiningen\n\n![\"Leiningen version\"](https://clojars.org/revise/latest-version.svg)\n\n## Connection Management\n\n[A brief explanation is here](connections.md)\n\n## Introduction\n\nThese docs are - for now - loosely based on the python api docs. The driver\nworks on version `1.9` and `1.10` (in our testing so far) of RethinkDB.\n\n## Usage\n\n```clojure\n(require '[bitemyapp.revise.connection :refer [connect close]])\n(require '[bitemyapp.revise.query :as r])\n(require '[bitemyapp.revise.core :refer [run run-async]])\n\n;; connect returns the connection agent\n(let [local-conn  (connect) ;; defaults to localhost\n      ;; pass in connection options map to specify beyond the defaults\n      remote-conn (connect {:host \"99.99.99.1\"\n                            :port 28015\n                            :auth-key \"\"})\n      ;; Run a query and return the result. Blocks as long as it needs to\n      ;; get a result (or an error)\n      response1 (-\u003e (r/db \"test\") (r/table-create-db \"authors\") (run local-conn))\n      ;; We may be having issues so we specify a timeout to run\n      response2 (-\u003e (r/db \"test\") (r/table-list-db) (run remote-conn 15000))\n      ;; We want to run a query asynchronously - giving up the error handling\n      response3 (-\u003e (r/db-list) (run-async local-conn))]\n  ;; dereference the promise to block on it.\n  (println @response3)\n  ;; We are done using the local connection\n  (close local-conn))\n```\n\n## Connecting to RethinkDB\n\nInside the namespace `bitemyapp.revise.connection` there are 2 functions we need:\n\n* `connect` `([\u0026 [conn-map]])`\n* `close` `([conn])`\n\n`connect` takes an optional connection map to override any or all of the default\nvalues:\n\n* `:host` `\"127.0.0.1\"`\n* `:port` `28015`\n* `:token` `0` The token of the first query to the connection. Autoincrements.\n* `:auth-key` `\"\"` The authentication key.\n\nConnect will return an agent to which you can send queries.\n\nTo close the connection use the function `close` with the agent as argument.\n\n## Sending queries\n\nInside the namespace `bitemyapp.revise.core` there are again 2 functions we need:\n\n* `run` `([query connection \u0026 [timeout]])`\n* `run-async` `([query connection])`\n\nOur queries are compiled and sent to the connection using those two functions.\n\n`run` takes an optional timeout in milliseconds (default `10000`) and will block\nuntil it has a response or it times out. It will throw when it times out or the\nagent dies due to an exception when sending a query.\n\n`run` will return a map which includes the autoincrementing `token` that was\nimplicitly sent to the agent and either a `:response` in case the query was\nsuccessful or an `:error`, `:response` and `:backtrace` in case there was\nan error with our request (in this case the driver doesn't throw an exception).\n\nAlternatively we might decide to use `run-async` to send and run queries\nasynchronously. This will return us a promise which we can dereference.\n\nNote that `run-async` gives up the error handling of `run`. The agent _might_\ndie and you will have to check for it manually.\n\nAfter dereferencing the promise the return value will be the same as `run`.\n\n## Compiling a query manually\n\n`run` and `run-async` have an implicit call to `bitemyapp.revise.protoengine/compile-term`.\nThis compiles the query into protocol buffers. If you know about the official\nRethinkDB API and you want to inspect the protocol buffers Revise gives you, you\ncan compile a query using that function. To send manually compiled queries to the\ndatabase, use `send-term` in the `bitemyapp.revise.connection` namespace.\nThat will be the equivalent of using `run-async`.\n\n## API\n\nThe api is under the namespace bitemyapp.revise.query.\n\n```clojure\n(require '[bitemyapp.revise.query :as r])\n```\n\nNote: rethinkdb doesn't let you use hyphens (`-`) as part of database or table\nnames. Revise won't 'fix' those names for you.\n\nAlso note that keywords and strings are interchangeable.\n\n### Lambdas\n\nMany queries such as `map`, `filter`, etc. support lambdas. Lambdas are anonymous\nfunctions with syntax like clojure's `fn`.\n\nExample:\n\n```clojure\n(-\u003e [1 2 3 4 5 6 7]\n    (r/map (r/lambda [n]\n             (r/* n 2)))\n    (run conn))\n```\n\nThis will give you the response `([2 4 6 8 10 12 14])`\n\n### Manipulating databases\n\n#### db-create\n`([db-name])`\n\nCreate a database.\n\n```clojure\n(-\u003e (r/db-create \"my_db\") (run conn))\n```\n\n#### db-drop\n`([db-name])`\n\nDrop a database.\n\n```clojure\n(-\u003e (r/db-drop \"my_db\") (run conn))\n```\n\n#### db-list\n`([])`\n\nList the database names in the system.\n\n```clojure\n(-\u003e (r/db-list) (run conn))\n```\n\n### Manipulating tables\n\n#### table-create-db\n\n`([db table-name \u0026 {:as optargs}])`\n\nCreate a table on the specified database. The following options are available:\n\n* `:primary-key` The name of the primary key. Default: `:id`.\n* `:durability` If set to `:soft`, this enables soft durability on this table:\nwrites will be acknowledged by the server immediately and flushed to disk in the\nbackground. Default is `:hard` (acknowledgement of writes happens after data has been\nwritten to disk).\n* `:cache-size` Set the cache size (in bytes) to be used by the table.\nThe default is 1073741824 (1024MB).\n* `:datacenter` The name of the datacenter this table should be assigned to.\n\n```clojure\n(-\u003e (r/db \"test\") (r/table-create-db \"authors\") (run conn))\n(-\u003e (r/db \"test\") (r/table-create-db \"users\" :primary-key :email) (run conn))\n```\n\n#### table-create\n\n`([table-name \u0026 {:as optargs}])`\n\nLike `table-create-db` except that the db is the default db.\n\n```clojure\n(-\u003e (r/table-create \"authors\") (run conn))\n```\n\n#### table-drop-db\n\n`([db table-name])`\n\nDrop a table from a specific db. The table and all its data will be deleted.\n\n```clojure\n(-\u003e (r/db \"test\") (r/table-drop-db \"authors\") (run conn))\n```\n\n#### table-drop\n\n`([table-name])`\n\nLike `table-drop-db` except the default db is used.\n\n```clojure\n(-\u003e (r/table-drop \"authors\") (run conn))\n```\n\n#### index-create\n\n`([table index-name lambda1 \u0026 [multi?]])`\n\nCreate a new secondary index with a given name on the specified table.\n\n```clojure\n(-\u003e (r/table \"authors\")\n    (r/index-create :author\n                    (r/lambda [author]\n                      (r/get-field author :name)))\n    (run conn))\n;; Compound index\n(-\u003e (r/table \"authors\")\n    (r/index-create :name-tv-show\n                    (r/lambda [author]\n                      [(r/get-field author :name)\n                       (r/get-field author :tv-show)]))\n    (run conn))\n;; A multi index. The r/lambda of a multi index should return an array. It will allow\n;; you to query based on whether a value is present in the returned array\n(-\u003e (r/table \"authors\")\n    (r/index-create :posts\n                    (r/lambda [author]\n                      (r/get-field author :posts)) ; returns an array\n      true) ; :multi -\u003e true\n    (run conn))\n```\n\n#### index-drop\n\n`([table index-name])`\n\nDelete a previously created secondary index of this table.\n\n```clojure\n(-\u003e (r/table \"authors\") (r/index-drop :posts) (run conn))\n```\n\n#### index-list\n\n`([table])`\n\nList all the secondary indexes of this table.\n\n```clojure\n(-\u003e (r/table \"authors\") (r/index-list) (run conn))\n```\n\n### Writing data\n#### insert\n\n`([table data \u0026 {:as optargs}])`\n\nInsert json documents into a table. Accepts a single json document (a clojure map)\nor an array of documents (a clojure vector of clojure maps).\n\nAccepts the following options:\n\n* `:upsert` A `bool`. Default is true. If true it will overwrite documents that\nalready exist.\n* `:durability` `:soft` or `:hard`. Override the durability of the table for this\noperation.\n* `:return-vals` A `bool`. Only valid for single object inserts. If `true` you\nget back the row you inserted on the key `:nev_val`. And if you overwrote a row\nit will be in `:old_val`\n\n\n```clojure\n(def authors [{:name \"William Adama\" :tv-show \"Battlestar Galactica\"\n               :posts [{:title \"Decommissioning speech\",\n                        :rating 3.5\n                        :content \"The Cylon War is long over...\"},\n                       {:title \"We are at war\",\n                        :content \"Moments ago, this ship received word...\"},\n                       {:title \"The new Earth\",\n                        :content \"The discoveries of the past few days...\"}]}\n\n              {:name \"Laura Roslin\", :tv-show \"Battlestar Galactica\",\n               :posts [{:title \"The oath of office\",\n                        :rating 4\n                        :content \"I, Laura Roslin, ...\"},\n                       {:title \"They look like us\",\n                        :content \"The Cylons have the ability...\"}]}])\n\n(def jean-luc {:name \"Jean-Luc Picard\", :tv-show \"Star Trek TNG\",\n               :posts [{:title \"Civil rights\",\n                        :content \"There are some words I've known since...\"}]})\n\n(-\u003e (r/table \"authors\")\n    (r/insert authors)\n    (run conn))\n\n(-\u003e (r/table \"authors\")\n    (r/insert jean-luc :return-vals true)\n    (run conn))\n```\n\nInsert returns a map with the following attributes:\n\n* `:inserted` The number of documents that were succesfully inserted.\n* `:replaced` The number of documents that were updated when upsert is used.\n* `:unchanged` The number of documents that would have been modified, except that\nthe new value was the same as the old value when doing an upsert.\n* `:errors` The number of errors encountered while inserting; if errors were\nencountered while inserting, first_error contains the text of the first error.\n* `:generated_keys` A list of generated primary key values deleted and skipped:\n0 for an insert operation.\n\nIf you specified :return-vals true you will also get the following keys:\n* `:nev_val` The value of the object you inserted\n* `:old_val` The value of the object you overwrote (`nil` if you didn't)\n\n#### update\n\n`([stream-or-single-selection lambda1-or-obj])`\n\nUpdate JSON documents in a table. Accepts a JSON document (clojure map), a RQL\nexpression or a combination of the two. Accepts the following optional keys:\n\n* `:durability` `:soft` or `:hard` - Override the table's durability for this\noperation.\n* `:return-vals` A `bool`. Only valid for single-row modifications. If `true`\nreturn the new value in `:new_val` and the old value in `:old_val`.\n* `non-atomic` A `bool`. Allow the server to run non-atomic operations.\n\n```clojure\n;; Make all authors be fictional\n(-\u003e (r/table \"authors\") (r/update {:type \"fictional\"}))\n;; Add the rank of admiral to William Adama\n(-\u003e (r/table \"authors\")\n  (r/filter (r/lambda [row]\n              (r/= \"William Adama\"\n                (r/get-field row :name))))\n  (r/update {:rank \"Admiral\"})\n  (run conn))\n;; Add a post to Jean-Luc\n(-\u003e (r/table \"authors\")\n    (r/filter (r/lambda [row]\n                (r/= \"Jean-Luc Picard\"\n                  (r/get-field row :name))))\n    (r/update\n      (r/lambda [row]\n        {:posts\n         (r/append (r/get-field row :posts)\n           {:title \"Shakespeare\"\n            :content \"What a piece of work is man..\"})}))\n    (run conn))\n```\n\nUpdate returns a map that contains the following attributes:\n\n* `:replaced` The number of documents that were updated.\n* `:unchanged` The number of documents that would have been modified except the new\nvalue was the same as the old value.\n* `:skipped` The number of documents that were left unmodified because there was\nnothing\nto do: either the row didn't exist or the new value is null.\n* `:errors` The number of errors encountered while performing the update; if errors\noccured, first_error contains the text of the first error.\n* `:deleted` and `:inserted` Are 0 for an update operation.\n\n#### replace\n\n`([stream-or-single-selection lambda1-or-obj \u0026 {:as optargs}])`\n\nReplace documents in a table. The new document must have the same primary key as the\noriginal document. Accepts the following optional arguments:\n\n* `:non-atomic` Allow non-atomic updates.\n* `:durability` `:soft` or `:hard`. Override the table or query's default\ndurability setting.\n* `:return-vals` A `bool` Return the old and new values of the row you're\nmodifying when set to true (only valid for single row replacements).\n\n```clojure\n;; Assuming :name is the primary key on the table\n(-\u003e (r/table \"authors\") (r/get \"Wooster\")\n    (r/replace {:tv-show \"Jeeves\"} :return-vals true)\n    (run conn))\n```\n\n#### delete\n\n`([stream-or-single-selection \u0026 {:as optargs}])`\n\nDelete the rows in a selection. Accepts the following optional arguments:\n\n* `:durability` Default: `:soft`; Override the table or query's default durability\nsetting. Other possible values: `:hard`\n* `:return-vals` Default: `true`; Return the old value of the row you're deleting\nwhen set to true (only valid for single row deletes) on the key `:old_val`\n\n```clojure\n(-\u003e (r/table \"authors\")\n    (r/filter (r/lambda [row]\n                (r/\u003c (r/count (r/get-field row :posts))\n                     3)))\n    (r/delete)\n    (run conn))\n```\n\n`delete` returns a map with the following attributes:\n\n* `:deleted` The number of documents that were deleted.\n* `:skipped` The number of documents from the selection that were left unmodified\nbecause there was nothing to do. For example, if you delete a row that has already\nbeen deleted, that row will be skipped.\n* `:errors` The number of errors encountered while deleting if errors occured,\nfirst_error contains the text of the first error.\n* `:inserted` Replaced, and unchanged: all 0 for a delete operation.\n\nIf you deleted only one row and `return-vals` is `true` then you also get the\nfollowing keys:\n\n* `:new_val` Is nil.\n* `:old_val` Contains the value of the document you deleted\n\n### Selecting data\n\n#### db\n\n`([db-name])`\n\nReference a database. This will give you an error if you try to run it. If you want\na list of tables use `r/table-list-db`\n\n```clojure\n(r/db \"test\")\n(r/db :test)\n```\n\n#### table-db\n\n`([db table-name])`\n\nSelect all documents on a table. This command can be chained with other commands to\ndo further processing on the data\n\n```clojure\n(-\u003e (r/db \"test\") (r/table-db \"authors\") (run conn))\n```\n\n#### table\n\n`([table-name])`\n\nLike table-db except that it uses the default database.\n\n```clojure\n(-\u003e (r/table \"authors\") (run conn))\n```\n\n#### get\n\n`([table key])`\n\nGet a document by its primary key.\n\n```clojure\n;; After setting the secondary index :name on the table \"authors\"\n(-\u003e (r/table \"authors\") (r/get \"7644aaf2-9928-4231-aa68-4e65e31bf219\")\n    (run conn))\n```\n\n#### get-all\n\n`([table keys-vec \u0026 [index]])`\n\nGet all documents where the given value matches the value of the requested index\n\n```clojure\n;; After setting the secondary key :name on the table :authors\n(-\u003e (r/table \"authors\")\n    (r/get-all [\"William Adama\"] :name)\n    (run conn))\n```\n\n#### between\n\n`([stream-selection lower-key upper-key \u0026 [index]])`\n\nGet all documents between two keys. index can be the name of a secondary index.\n`[lower-key upper-key)`\n\n```clojure\n;; Assuming the primary key on our table is a number.\n(-\u003e (r/table \"authors\") (r/between 10 20) (run conn))\n```\n\n#### filter\n\n`([sequence lambda1-or-obj \u0026 [default-val]])`\n\nFilter a sequence with either a function or a shortcut object.\nThe body of `filter` is wrapped in an implicit `(default .. false)`  and you\ncan change the default value by specifying the `default-val` optarg. If you\nmake the default `(error)`, all errors caught by default will be rethrown\nas if the default did not exist\n\n```clojure\n(-\u003e (r/table \"authors\")\n    (r/filter (r/lambda [row]\n                (r/= (r/get-field row :name) \"William Adama\")))\n    (run conn))\n```\n\n### Joins\n\n#### inner-join\n\n`([sequence1 sequence2 predicate])`\n\nReturns the inner product of two sequences (e.g. a table and a filter result) filtered\nby the predicate. The query compares each row of the left sequence with each row of\nthe right sequence to find all pairs of rows which satisfy the predicate (a `lambda`\nof two arguments). When the predicate is satisfied, each matched pair of rows of both\nsequences are combined into a result row.\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/inner-join (r/table \"dc\")\n      (lambda [marvel-row dc-row]\n        (r/\u003c (get-field marvel-row :strength)\n             (get-field dc-row :strength))))\n    (run conn))\n```\n\n#### outer-join\n\n`([sequence1 sequence2 predicate])`\n\nComputes a left outer join by retaining each row in the left table even if no match\nwas found in the right table.\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/outer-join (r/table \"dc\")\n      (r/lambda [marvel-row dc-row]\n        (r/\u003c (get-field marvel-row :strength)\n             (get-field dc-row :strength))))\n    (run conn))\n```\n\n#### eq-join\n\n`([sequence1 left-attr sequence2 \u0026 [index]])`\n\nAn efficient join that looks up elements in the right table by primary key.\n`index` defaults to `:id`\n\n```clojure\n(-\u003e (r/table \"marvel\") (r/eq-join \"main_dc_collaborator\" (r/table \"dc\"))\n    (run conn))\n```\n\n#### zip\n\n`([sequence])`\n\nUsed to 'zip' up the result of a join by merging the 'right' fields into 'left' fields\nof each member of the sequence.\n\n```clojure\n(-\u003e (r/table \"marvel\") (r/eq-join \"main_dc_collaborator\" (r/table \"dc\"))\n    (r/zip)\n    (run conn))\n```\n\n### Transformations\n\n#### map\n\n`([sequence lambda1])`\n\nTransform each element of the sequence by applying the given mapping function.\n\n```clojure\n(-\u003e (r/table \"authors\")\n    (r/map (r/lambda [author]\n             (r/count (r/get-field author :posts))))\n    (run conn))\n```\n\n#### with-fields\n\n`([sequence \u0026 pathspecs])`\n\nTakes a sequence of objects and a variable number of fields. If any objects in the\nsequence don't have all of the specified fields, they're dropped from the sequence.\nThe remaining objects have the specified fields plucked out. Identical to has-fields\nfollowed by pluck.\n\n```clojure\n;; Get a list of authors and their posts, excluding any authors that lack one.\n(-\u003e (r/table \"authors\") (r/with-fields :name :posts)\n    (run conn))\n```\n\n#### mapcat\n\n`([sequence lambda1])`\n\nMap a function over a sequence and then concatenate the results together\n\n```clojure\n;; Get all of the posts of all authors\n(-\u003e (r/table \"authors\")\n    (r/mapcat (r/lambda [author]\n                (r/get-field author :posts)))\n    (run conn))\n```\n\n#### order-by\n\n`([sequence \u0026 keys-or-orderings])`\n\nSort the sequence by document values of the given key(s). Defaults to ascending\nordering. To specify order, wrap the key with `(r/asc ..)` or `(r/desc ..)`\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/order-by :enemies_vanquished :damsels_saved)\n    (run conn))\n```\n\n#### skip\n\n`([sequence n])`\n\nSkip a number of elements from the head of the sequence\n\n```clojure\n;; Ignore the first authors sorted alphabetically\n(-\u003e (r/table \"authors\")\n    (r/order-by :name)\n    (r/skip 2)\n    (run conn))\n```\n\n#### limit\n\n`([sequence n])`\n\nEnd the sequence after the given number of elements\n\n```clojure\n;; Get 10 posts from all of our authors\n(-\u003e (r/table \"authors\")\n    (r/mapcat (r/lambda [author]\n                (r/get-field author :posts)))\n    (r/limit 10)\n    (run conn))\n```\n\n#### slice\n\n`([sequence start-index end-index])`\n\nTrim the sequence to within the bounds provided.\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/order-by :strength)\n    (r/slice 5 10)\n    (run conn))\n```\n\n#### nth\n\n`([sequence idx])`\n\nGet the nth element of a sequence. Zero indexed.\n\n```clojure\n(-\u003e (r/table \"authors\")\n    (r/nth 1)\n    (run conn))\n```\n\n#### indexes-of\n\n`([sequence item-or-predicate])`\n\nGet the indexes of an element in a sequence. If the argument is a predicate, get the\nindexes of all elements matching it.\n\n```clojure\n(-\u003e (r/indexes-of [\"a\" \"b\" \"c\"] \"c\") (run conn))\n```\n\n#### empty?\n\n`([sequence])`\n\nTest if a sequence is empty.\n\n```clojure\n(-\u003e (r/table \"authors\")\n    (r/empty?)\n    (run conn))\n```\n\n#### union\n\n`([sequence1 sequence2])`\n\nConcatenate 2 sequences\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/union\n      (r/table \"dc\"))\n    (run conn))\n```\n\n#### sample\n\n`([sequence n])`\n\nSelect a number of elements from the sequence with uniform random distribution.\n\n```clojure\n(-\u003e (r/table \"authors\")\n    (r/sample 2)\n    (run conn))\n```\n\n### Aggregation\n\nCompute smaller values from large sequences.\n\n#### reduce\n\n`([sequence lambda2 \u0026 [init-val]])`\n\nProduce a single value from a sequence through repeated application of a reduction\nfunction.\n\n```clojure\n;; How many posts are there?\n(-\u003e (r/table \"authors\")\n    (r/map (r/lambda [author] (r/count (r/get-field :posts))))\n    (r/reduce (r/lambda [acc next] (r/+ acc next)) 0)\n    (run conn))\n```\n\n#### count\n\n`([sequence \u0026 [filter]])`\n\nCount the number of elements in the sequence. With a single argument, count the number\nof elements equal to it. If the argument is a function, it is equivalent to calling\nfilter before count.\n\n```clojure\n(-\u003e (r/table \"authors\")\n    (r/count)\n    (run conn))\n```\n\n#### distinct\n\n`([sequence])`\n\nRemove duplicates from the sequence.\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/mapcat (r/lambda [hero]\n                (r/get-field hero :villain-list)))\n    (r/distinct)\n    (run conn))\n```\n\n#### grouped-map-reduce\n\n`([sequence grouping mapping reduction \u0026 [base]])`\n\nPartition the sequence into groups based on the `grouping` function. The elements of\neach group are then mapped using the `mapping` function and reduced using the\n`reduction` function. Generalized form of group-by.\n\n```clojure\n;; Compare heroes against their weight class\n(-\u003e (r/table \"marvel\")\n    (r/grouped-map-reduce\n      (r/lambda [hero] (r/get-field :weight-class)) ; grouping\n      (r/lambda [hero] (r/pluck hero :name :strength)) :mapping\n      (r/lambda [acc hero]\n        (r/branch (r/\u003c (r/get-field acc :strength) ; if\n                       (r/get-field hero :strength))\n          hero ; then\n          acc ; else\n          ))\n      {:name \"none\" :strength 0}) ; base\n    (run conn))\n```\n\n#### group-by\n\n`([sequence keys-array operation-map])`\n\nGroups a sequence by one or more attributes and then applies a reduction.\nThe third argument is a special literal giving the kind of operation\nto be performed and anay necessary arguments.\n\nAt present group-by supports the following operations\n* :count - count the size of the group\n* {:sum attr} - sum the values of the given attribute accross the group\n* {:avg attr} - average the values of the given attribute accross the group\"\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/group-by [:weight-class] {:avg :strength})\n    (run conn))\n\n(-\u003e (r/table \"marvel\")\n    (r/group-by [:age :weight-class] :count)\n    (run conn))\n\n(-\u003e (r/table \"marvel\")\n    (r/group-by [:weight-class] {:sum :foes-defeated})\n    (run conn))\n```\n\n#### contains?\n\n`([sequence item-or-lambda1])`\n\nReturns whether or not a sequence contains the specified value, or if functions are\nprovided instead, returns whether or not a sequence contains values matching all the\nspecified functions.\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/get \"ironman\")\n    (r/get-field \"opponents\")\n    (r/contains? \"superman\")\n    (run conn))\n```\n\n### Document manipulation\n\n#### pluck\n\n`([object-or-sequence \u0026 selectors])`\n\nGet a subset of an object by selecting some attributes to preserve,\nor map that over a sequence\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/get \"IronMan\")\n    (r/pluck :reactor-state :reactor-power)\n    (run conn))\n```\n\n#### without\n\n`([object-or-sequence \u0026 pathspecs])`\n\nThe opposite of pluck. Get a subset of an object by selecting some attributes to\ndiscard, or map that over a sequence.\n\n```clojure\n(-\u003e (r/table \"marvel\") (r/get \"IronMan\") (without :personal-victories-list)\n    (run conn))\n```\n\n#### merge\n\n`([\u0026 objects])`\n\nMerge objects. Right-preferential.\n\n```clojure\n(-\u003e (r/table \"marvel\") (r/get \"IronMan\")\n    (r/merge (-\u003e (r/table \"loadouts\")\n                 (r/get :alien-invasion-kit)))\n    (run conn))\n```\n\nThe query `literal` takes a single argument and it can be used to indicate merge\nto replace the other object rather than merge it.\n\n#### append\n\n`([sequence item])`\n\nAppend a value to an array\n\n```clojure\n(-\u003e (r/table \"authors\")\n    (r/filter (r/lambda [author]\n                (r/= \"William Adama\"\n                     (r/get-field author name))))\n    (r/update (r/lambda [author]\n                {:posts\n                 (r/append (r/get-field row :posts)\n                           ;; Appending a new post\n                           {:title \"Earth\"\n                            :content \"Earth is a dream..\"}))))\n    (run conn))\n```\n\n#### prepend\n\n`([array item])`\n\nPrepend a value to an array\n\n```clojure\n(-\u003e (r/table \"authors\")\n    (r/filter (r/lambda [author]\n                (r/= \"William Adama\"\n                     (r/get-field author name))))\n    (r/update (r/lambda [author]\n                {:posts\n                 (r/prepend (r/get-field row :posts)\n                              ;; Prepend a post\n                              {:title \"Cylons\"\n                               :content \"The cylon war is long over\"}))))\n    (run conn))\n```\n\n#### difference\n\n`([array1 array2])`\n\nRemove the elements of one array from another array.\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/get \"IronMan\")\n    (r/get-field :equipment)\n    (r/difference \"Boots\")\n    (run conn))\n```\n\n#### set-insert\n\n`([array item])`\n\nAdd a value to an array as if the array was a set.\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/get \"IronMan\")\n    (r/get-field \"equipment\")\n    (r/set-insert \"new-boots\")\n    (run conn))\n```\n\n#### set-union\n\n`([array1 array2])`\n\nAdd several values to an array as if it was a set\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/get \"IronMan\")\n    (r/get-field \"equipment\")\n    (r/set-union [\"new-boots\" \"arc-reactor\"])\n    (run conn))\n```\n\n#### set-intersection\n\n`([array1 array2])`\n\nIntersect 2 arrays returning values that occur in both of them as a set.\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/get \"IronMan\")\n    (r/get-field \"equipment\")\n    (r/set-intersection [\"new-boots\" \"arc-reactor\"])\n    (run conn))\n```\n\n#### get-field\n\n`([sequence-or-object])`\n\nGet a single field from an object. If called on a sequence, gets that field from\nevery object in the sequence, skipping objects that lack it.\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/get \"IronMan\")\n    (r/get-field \"first-appearance\")\n    (run conn))\n```\n\n#### has-fields?\n\n`([object \u0026 pathspecs])`\n\nCheck whether an object contains all the specified fields or filters a\nsequence so that al objects inside of it contain all the specified fields\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/has-fields \"spouse\")\n    (run conn))\n```\n\n#### insert-at\n\n`([array idx item])`\n\nInsert a value in to an array at a given index.\n\n```clojure\n(-\u003e [\"IronMan\" \"SpiderMan\"]\n    (r/insert-at 1 \"Hulk\")\n    (run conn))\n```\n\n#### splice-at\n\n`([array1 idx array2])`\n\nInsert several values into an array at a given index.\n\n```clojure\n(-\u003e [\"IronMan\" \"SpiderMan\"]\n    (r/splice-at 1 [\"Hulk\" \"Thor\"])\n    (run conn))\n```\n\n#### delete-at\n\n`([array idx \u0026 [end-idx]])`\n\nRemove an element from an array at a given index.\n\n```clojure\n(-\u003e [\"IronMan\" \"Hulk\" \"SpiderMan\"]\n    (r/delete-at 1)\n    (run conn))\n```\n\n#### change-at\n\n`([array idx item])`\n\nChange a value in an array at a given index.\n\n```clojure\n(-\u003e [\"IronMan\" \"Bruce\" \"SpiderMan\"]\n    (r/change-at 1 \"Hulk\")\n    (run conn))\n```\n\n#### keys\n\n`([object-or-single-selection])`\n\nReturn an array containing all of the object's keys\n\n```clojure\n(-\u003e (r/table \"authors\")\n    (r/get \"7644aaf2-9928-4231-aa68-4e65e31bf219\")\n    (r/keys)\n    (run conn))\n```\n\n### String manipulation\n\n#### match\n\n`([str regexp])`\n\nReturns a match object if the string matches the regexp. Accepts RE2 syntax\nhttps://code.google.com/p/re2/wiki/Syntax Accepts clojure regexp.\n\n```clojure\n(-\u003e (r/table \"users\")\n    (r/filter (r/lambda [user]\n                (r/match (r/get-field user :name)\n                         #\"^A\")))\n    (run conn))\n```\n\n### Math and logic\n\nThe following symbols are also part of the api and they should be properly namespace\nqualified:\n\n`r/+` Add numbers or concatenate strings or arrays.\n\n`r/-` Substract numbers.\n\n`r/*` Multiply numbers or make a periodic array.\n\n`r/div` Divide numbers. **Note that it's not r//**\n\n`r/mod` Find the remainder of two numbers.\n\n\u003c!-- `r/and` Logical and.  --\u003e\n\n\u003c!-- `r/or` Logical or. --\u003e\n\n`r/=` Test for equality.\n\n`r/not=` Test for inequality.\n\n`r/\u003e` Greater than.\n\n`r/\u003e=` Greater equal.\n\n`r/\u003c` Lower than.\n\n`r/\u003c=` Lower equal.\n\n`r/not` Logical inverse.\n\n### Dates and times\n\n#### now\n\n`([])`\n\nReturn a time object representing the time in UTC. The command now() is computed once\nwhen the server receives the query, so multiple instances of r.now() will always\nreturn the same time inside a query.\n\n```clojure\n(-\u003e (r/table \"users\")\n    (r/insert {:name \"John\"\n               :subscription-date (r/now)})\n    (run conn))\n```\n\n#### time\n\n`([year month day \u0026 [timezone]] [year month day hour minute second \u0026 [timezone])`\n\nCreate a time object for a specific time. Timezone is a string like: `\"-06:00\"`\n\n```clojure\n;; Update the birthdate of the user \"John\" to November 3rd, 1986 UTC\n(-\u003e (r/table \"user\")\n    (r/get \"John\")\n    (r/update {:birthdate (r/time 1986 11 3 \"Z\")])\n    (run conn))\n```\n\n#### epoch-time\n\n`([epoch-time])`\n\nCreate a time object based on seconds since epoch.\n\n```clojure\n;; Update the birthdate of the user \"John\" to November 3rd, 1986\n(-\u003e (r/table \"user\")\n    (r/get \"john\")\n    (r/update {:birthdate (r/epoch-time 531360000)})\n    (run conn))\n```\n\n#### iso8601\n\n`([iso8601-date])`\n\nCreate a time object based on an iso8601 date-time string.\n\n```clojure\n(-\u003e (r/table \"user\")\n    (r/get \"John\")\n    (r/update {:birth (r/iso8601 \"1986-11-03T08:30:00-07:00\")})\n    (run conn))\n```\n\n#### in-timezone\n\n`([time timezone])`\n\nReturn a new time object with a different timezone. Results returned by functions\nthat take the timezone into account will be different.\n\n```clojure\n(-\u003e (r/now)\n    (r/in-timezone \"-08:00)\n    (r/hours)\n    (run conn))\n```\n\n#### timezone\n\n`([time])`\n\nReturn the timezone of the time object\n\n```clojure\n(-\u003e (r/table \"user\")\n    (r/filter (r/lambda [user]\n                (r/= \"-07:00\"\n                  (-\u003e (r/get-field user :subscription-date)\n                      (r/timezone)))))\n    (run conn))\n```\n\n#### during\n\n`([time start-time end-time])`\n\nReturns whether the time is in the range [start-time end-time)\n\n```clojure\n(-\u003e (r/table \"posts\")\n    (r/filter (r/lambda [post]\n                (-\u003e (r/get-field :date)\n                    (r/during (r/time 2013 12 1) (r/time 2013 12 10)))))\n    (run conn))\n```\n\n#### date\n\n`([time])`\n\nReturn a new time object only based on the day, month and year\n\n```clojure\n(-\u003e (r/table \"users\")\n    (r/filter (r/lambda [user]\n                (r/= (-\u003e (r/now) (r/date))\n                  (r/get-field user :birthday))))\n    (run conn))\n```\n\n#### time-of-day\n\n`([time])`\n\nReturn the number of seconds elapsed since the beginning of the day stored in the\ntime object.\n\n```clojure\n;; Posts submitted before noon\n(-\u003e (r/table \"posts\")\n    (r/filter (r/lambda [post]\n               (r/\u003e (* 12 60 60) ; Can be left as clojure.core/*\n                 (-\u003e (r/get-field post :date)\n                     (r/time-of-day)))))\n    (run conn))\n```\n\n### Access time fields\n\nAll of these take a `time` as the only argument.\n\n`r/year` Return the year of a time object.\n\n`r/month` Return the month as a number between 1 and 12.\n\n`r/day` Return the day as a number between 1 and 31.\n\n`r/day-of-week` Return the day of week as a number between 1 and 7 (ISO 8601).\n\n`r/day-of-year` Return the day of the year as a number between 1 and 366 (ISO 8601).\n\n`r/hours` Return the hour as a number between 0 and 23.\n\n`r/minutes` Return the minute in a time object as a number between 0 and 59.\n\n`r/seconds` Return the seconds in a time object as a number between 0 and 59.999 (double precision).\n\n#### -\u003eiso8601\n\n`([time])`\n\nConvert a time object to its ISO 8601 format.\n\n```clojure\n(-\u003e (r/now)\n    (r/to-iso8601)\n    (run conn))\n```\n\n#### -\u003eepoch-time\n\n`([time])`\n\nConvert a time to its epoch time.\n\n```clojure\n(-\u003e (r/now)\n    (r/-\u003eepoch-time)\n    (run conn))\n```\n\n### Control structures\n\n#### branch\n\n`([test then else])`\n\nLike an if. The test can be any value. Truthiness appears to be similar to\nclojure's (`false` and `nil` are falsey, everything else is truthy).\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/map (r/lambda [hero]\n             (r/branch (r/\u003c= 100\n                         (r/get-field hero :victories))\n                       (r/+ (r/get-field hero :name) \" is a superhero\") ; then\n                       (r/+ (r/get-field hero :name) \" is a hero\"))))   ; else\n    (run conn))\n```\n\n#### or\n\n`([\u0026 bools])`\n\nLike clojure's short-circuiting `or` except that: It short circuits _inside_\nRethinkDB and it is a little inneficient in that if your \"booleans\" are queries\nit will probably run them twice.\n\nThe same truthy/falsey rules apply as with `branch`.\n\n```clojure\n(-\u003e (r/or false false false true) (run conn))\n(-\u003e (r/or false nil false \"hello!\" nil) (run conn))\n```\n\n#### and\n\n`([\u0026 bools])`\n\nLike clojure's short-circuiting `and` except that: It short circuits _inside_\nRethinkDB and it is a little inneficient in that if your \"booleans\" are queries\nit will probably run them twice.\n\nThe same truthy/falsey rules apply as with `branch`.\n\n```clojure\n(-\u003e (r/and true true true) (run conn))\n(-\u003e (r/and 1 2 3 nil) (run conn))\n```\n\n\n#### any\n\n`([\u0026 bools])`\n\nA short circuiting or that returns a boolean\n\n```clojure\n(-\u003e (r/any false false true) (run conn))\n```\n\n#### all\n\n`([\u0026 bools])`\n\nReturns true if all of its arguments are true (short-circuiting).\n\n```clojure\n(-\u003e (r/all true true true) (run conn))\n```\n\n#### foreach\n\n`([sequence lambda1])`\n\nCalls its function with each entry in the sequence and executes the array of\nterms that function returns.\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/foreach (r/lambda [hero]\n                 (-\u003e (r/table \"villains\")\n                     (r/get (r/get-field hero :villain-defeated)))))\n    (r/delete)\n    (run conn))\n```\n\n#### error\n\n`([\u0026 [s]])`\n\nThrow a runtime error. If called with no arguments inside the second argument to\ndefault, re-throw the current error.\n\n```clojure\n(-\u003e (r/error \"kaput\") (run conn))\n```\n\n#### default\n\n`([item-to-check item-or-lambda1])`\n\nEvaluates its first argument. If that argument returns NULL or throws an error\nrelated to the absence of an expected value, default will either return its\nsecond argument or execute it if it's a function. If the second argument is a\nfunction it will be passed either the text of the error or NULL as its argument.\n\n```clojure\n(-\u003e (r/table \"projects\")\n    (r/map (r/lambda [p]\n             (r/+ (r/default (r/get-field p :staff) 0)\n                  (r/default (r/get-field p :management) 0))))\n    (run conn))\n```\n\n#### parse-val\n\n`([item])`\n\nParse a clojure value to construct a json value. Strings, keywords, numbers,\nvectors, maps and booleans are allowed. This is the equivalent of `expr` in\npython. **Note that since these queries are functions and not methods, this\nfunction is hardly ever needed since it is already implicit.**\n\n```clojure\n(r/parse-val [1 false \"hello\" :goodbye])\n```\n\n#### js\n\n`([js-string])`\n\nCreate a javascript expression.\n\n```clojure\n(-\u003e (r/js \"1 + 1\") (run conn))\n```\n\n#### coerce-to\n\n`([item type-string])`\n\nConvert a value of one type into another.\n\nYou can convert: a selection, sequence, or object into an ARRAY, an array of pairs\ninto an OBJECT, and any DATUM into a STRING.\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/coerce-to :array)\n    (run conn))\n```\n\n#### type\n\n`([item])`\n\nGet the type of a value.\n\n```clojure\n(-\u003e (r/parse-val \"hello!\")\n    (r/type)\n    (run conn))\n```\n\n#### info\n\n`([any])`\n\nGet information about a rql value\n\n```clojure\n(-\u003e (r/table \"marvel\")\n    (r/info)\n    (run conn))\n```\n\n#### json\n\n`([json-str])`\n\nParse a JSON string on the server.\n\n```clojure\n(-\u003e (r/json \"[1,2,3]\") (run conn))\n```\n\n### Time constants\n\nTime constants are already evaluated and so they don't have to be called as fns.\n\n`r/monday`    =\u003e 1\n\n`r/tuesday`   =\u003e 2\n\n`r/wednesday` =\u003e 3\n\n`r/thursday`  =\u003e 4\n\n`r/friday`    =\u003e 5\n\n`r/saturday`  =\u003e 6\n\n`r/sunday`    =\u003e 7\n\n\n`r/january`   =\u003e 1\n\n`r/february`  =\u003e 2\n\n`r/march`     =\u003e 3\n\n`r/april`     =\u003e 4\n\n`r/may`       =\u003e 5\n\n`r/june`      =\u003e 6\n\n`r/july`      =\u003e 7\n\n`r/august`    =\u003e 8\n\n`r/september` =\u003e 9\n\n`r/october`   =\u003e 10\n\n`r/november`  =\u003e 11\n\n`r/december`  =\u003e 12\n\n\n\n## License\n\nCopyright © 2013 Chris Allen, César Bolaños\n\nDistributed under the Eclipse Public License, the same as Clojure.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitemyapp%2Frevise","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbitemyapp%2Frevise","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitemyapp%2Frevise/lists"}