RethinkDB
Installation
/etc/lsb-release
contains os info:
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.1 LTS"
start server
$ rethinkdb
or
$ rethinkdb --bind all
default port for client connection is 28015
and admin panel at localhost:8080
cluster
$ rethinkdb --port-offset 1 --directory rethinkdb_data2 --join localhost:29015
--port-offset
: increment all ports by1
so the two nodes don't try to use the same ports on one machine.--directory
: use a different data directory so the two nodes don't try to access the same files.--join
: tell our new RethinkDB instance to connect to another instance (in this case,localhost:29015
).
client
$ sudo gem install rethinkdb
$ irb
require 'rethinkdb'
include RethinkDB::Shortcuts
r.connect(:host => 'localhost', :port => 28015).repl
r.db('test').table_create('tv_shows').run
r.table('tv_shows').insert({ 'name'=>'Star Trek TNG' }).run
The repl
command is a convenience method that sets a default connection in your shell so you don't have to pass it to the run
command to run your queries.
Ten-minute guide
select
cursor = r.table("authors").run
cursor.each{|document| p document}
select by primary key
(by default, each table uses the id
attribute for primary keys)
r.db('test').table('authors').get('7644aaf2-9928-4231-aa68-4e65e31bf219').run
filter
cursor = r.table("authors").filter{|author| author["name"].eq("William Adama") }.run
cursor.each{|document| p document}
Realtime feeds
on terminal A:
cursor = r.table("authors").changes.run
cursor.each{|document| p document}
on terminal B:
do some update ...
update
r.table("authors").update({"type"=>"fictional"}).run
r.table("authors").
filter{|author| author["name"].eq("William Adama")}.
update({"rank"=>"Admiral"}).run
r.table('authors').filter{|author| author["name"].eq("Jean-Luc Picard")}.
update{|author| {"posts"=>author["posts"].append({
"title"=>"Shakespeare",
"content"=>"What a piece of work is man..."})
}}.run
delete
r.table("authors").
filter{ |author| author["posts"].count < 3 }.
delete.run
Introduction to ReQL
select
r.table('users').pluck('last_name').run
r.table('users').pluck('last_name').distinct.run
r.table('users').pluck('last_name').distinct.count.run
lazy
r.table('users').has_fields('age').limit(5).run
functional
r.table('authors').filter(lambda {|user| user['age'] > 30}).run
control block
r.table('users').filter(lambda {|user| r.branch(user['age'] > 30, True, False)).run
embedded V8 javascript engine
r.js('1 + 1').run
r.table('users').filter(r.js('(function (user) { return user.age > 30; })')).run
subqueries
r.table('authors').filter(lambda {|author|
r.table('users').pluck('last_name').contains(author.pluck('last_name'))).run
(call run
only once on the complex query when you're ready for it to be executed.)
expressions
r.table('users').filter(lambda {|user| user['salary'] + user['bonus'] < 90000)
.update(lambda {|user| {'salary': user['salary'] + user['salary'] * 0.1}).run
Using secondary indexes
RethinkDB supports different types of secondary indexes:
- Simple indexes based on the value of a single field.
- Compound indexes based on multiple fields.
- Multi indexes based on arrays of values.
- Indexes based on arbitrary expressions.
Simple indexes
creation:
# Create a secondary index on the last_name attribute
r.table("users").index_create("last_name").run(conn)
# Wait for the index to be ready to use
r.table("users").index_wait("last_name").run(conn)
querying:
# Get all users whose last name is "Smith"
r.table("users").get_all("Smith", :index => "last_name").run(conn)
# Get all users whose last names are "Smith" or "Lewis"
r.table("users").get_all("Smith", "Lewis", :index => "last_name").run(conn)
# Get all users whose last names are between "Smith" and "Wade"
r.table("users").between("Smith", "Wade", :index => "last_name").run(conn)
# Efficiently order users by last name using an index
r.table("users").order_by(:index => "last_name").run(conn)
# For each blog post, return the post and its author using the last_name index
r.table("posts").eq_join("author_last_name", r.table("users"), :index => "last_name").zip.run(conn)
Compound indexes
creation:
# Create a compound secondary index based on the first_name and last_name attributes
r.table("users").index_create("full_name"){ |row|
[row["last_name"], row["first_name"]]
}.run(conn)
# Wait for the index to be ready to use
r.table("users").index_wait("full_name").run(conn)
querying:
# Get all users whose full name is John Smith.
r.table("users").get_all(["Smith", "John"], :index => "full_name").run(conn)
# Get all users whose full name is between "John Smith" and "Wade Welles"
r.table("users").between(
["Smith", "John"], ["Welles", "Wade"], :index => "full_name"
).run(conn)
# Get all users whose last name is Smith.
r.table("users").between(
["Smith", r.minval], ["Smith", r.maxval], :index => "full_name"
).run(conn)
# Efficiently order users by first name and last name using an index
r.table("users").order_by(:index => "full_name").run(conn)
# For each blog post, return the post and its author using the full_name index
# (assume "author_full_name" is the name of a field in "posts")
r.table("posts").eq_join("author_full_name", r.table("users"), :index => "full_name").run(conn)
Multi indexes
schema of the table posts
would be something like:
{
:title => "...",
:content => "...",
:tags => [ <tag1>, <tag2>, ... ]
}
creation:
# Create the multi index based on the field tags
r.table("posts").index_create("tags", :multi => true)
# Wait for the index to be ready to use
r.table("posts").index_wait("tags").run(conn)
querying:
# Get all posts with the tag "travel" (where the field tags contains "travel")
r.table("posts").get_all("travel", :index => "tags").run(conn)
# For each tag, return the tag and the posts that have such tag
r.table("tags").eq_join("tag", r.table("posts"), :index => "tags").run(conn)
Indexes on arbitrary ReQL expressions
create an index on an arbitrary expression by passing an anonymous function to index_create
.
# A different way to do a compound index
r.table("users").index_create("full_name2"){ |user|
r.add(user["last_name"], "_", user["first_name"])
}.run(conn)
The function you give to index_create
must be deterministic. In practice this means that that you cannot use a function that contains a sub-query or the r.js command.
Administrative operations
# list indexes on table "users"
r.table("users").index_list().run(conn)
# drop index "last_name" on table "users"
r.table("users").index_drop("last_name").run(conn)
# return the status of all indexes
r.table("users").index_status().run(conn)
# return the status of the index "last_name"
r.table("users").index_status("last_name").run(conn)
# return only when the index "last_name" is ready
r.table("users").index_wait("last_name").run(conn)