Jim Cheung

RethinkDB

Installation

Install RethinkDB on Ubuntu

/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

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

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)