Triton
Pure Elixir Cassandra ORM built on top of Xandra.
Configure Triton
Single Cluster
config :triton,
clusters: [
[
conn: Triton.Conn,
nodes: ["127.0.0.1"],
pool: Xandra.Cluster,
underlying_pool: DBConnection.Poolboy,
pool_size: 10,
keyspace: "my_keyspace"
]
]
Multi-Cluster
config :triton,
clusters: [
[
conn: Cluster1.Conn,
nodes: ["127.0.0.1"],
pool: Xandra.Cluster,
underlying_pool: DBConnection.Poolboy,
pool_size: 10,
keyspace: "cluster_1_keyspace"
],
[
conn: Cluster2.Conn,
nodes: ["127.0.0.1"],
pool: Xandra.Cluster,
underlying_pool: DBConnection.Poolboy,
pool_size: 10,
keyspace: "cluster_2_keyspace"
]
]
Reconnect
Since v0.1.7, if DB gets disconnected, Triton will attempt to reconnect.
Defining a Keyspace
First, define your keyspace. Triton will create the keyspace for your at compile time if it does not exist.
defmodule Schema.Keyspace do
use Triton.Keyspace
keyspace :my_keyspace, conn: Triton.Conn do
with_options [
replication: "{'class' : 'SimpleStrategy', 'replication_factor': 3}"
]
end
end
Defining a Table
You can define as many tables as you want. Triton will create tables for you if they do not exist.
If you would like Triton to auto-create tables for you at compile time, you must require your Keyspace module.
defmodule Schema.User do
require Schema.Keyspace
use Triton.Table
table :users, keyspace: Schema.Keyspace do
field :user_id, :bigint, validators: [presence: true] # validators using vex
field :username, :text
field :display_name, :text
field :password, :text
field :email, :text
field :phone, :text
field :notifications, {:map, "<text, text>"}
field :friends, {:set, "<text>"}
field :posts, {:list, "<text>"}
field :updated, :timestamp
field :created, :timestamp, transform: &Schema.Helper.DateHelper.to_ms/1 # transform field data
partition_key [:user_id]
end
end
Defining a Materialized View
An example of a materialized view users_by_email with fields user_id, email, display_name, password.
Also demonstrates adding options like gc_grace_seconds and clustering_order_by.
defmodule Schema.UserByEmail do
require Schema.User # if you want to auto-create at compile time
use Triton.MaterializedView
materialized_view :users_by_email, from: Schema.User do
fields [
:user_id,
:email,
:display_name,
:password
]
partition_key [:email]
cluster_columns [:user_id]
with_options [
gc_grace_seconds: 172_800,
clustering_order_by: [
email: :asc,
user_id: :desc
]
]
end
end
An example of materialized view users_by_email with all fields
defmodule Schema.UserByEmail do
require Schema.User
use Triton.MaterializedView
materialized_view :users_by_email, from: Schema.User do
fields :all
partition_key [:email]
cluster_columns [:user_id]
end
end
Querying
First, import Triton.Query
alias Schema.User
import Triton.Query
Select a single user where user_id = using a prepared statement.
User
|> prepared(user_id: id)
|> select([:user_id, :username])
|> where(user_id: :user_id)
|> User.one
Select users with IDs of 1, 2, or 3
User
|> select([:user_id, :username])
|> where(user_id: [in: [1, 2, 3]])
|> limit(10)
|> User.all
Select user with email someone@gmail.com
UserByEmail
|> select([:display_name])
|> where(email: "someone@gmail.com")
|> User.one
Inserting, Updating, & Deleting
Again, lets import Triton.Query for the necessary macros.
alias Schema.User
import Triton.Query
Add a user (if it doesn't already exist) with username username using a prepared statement that substitutes user_id into :user_id
User
|> prepared(user_id: user_id, username: username)
|> insert(user_id: :user_id, username: :username)
|> if_not_exists
|> User.save
Update a user's username, and make sure to check that their previous username was what we expected.
User
|> update(username: username)
|> where(user_id: user_id)
|> constrain(username: previous_username)
|> User.save
Lets delete a user given a user_id
User
|> prepared(user_id: user_id)
|> delete(:all) # here :all refers to all fields
|> where(user_id: :user_id)
|> User.del
Lets delete that same user, with consistency: :quorum
User
|> prepared(user_id: user_id)
|> delete(:all) # here :all refers to all fields
|> where(user_id: :user_id)
|> User.del(consistency: :quorum)
Batch update 4 users in 1 Cassandra request.
[
User |> update(username: "username1") |> where(user_id: 1),
User |> update(username: "username2") |> where(user_id: 2),
User |> update(username: "username3") |> where(user_id: 3),
User |> update(username: "username4") |> where(user_id: 4)
] |> User.batch_execute
Working with Collections
Update the notifications map to {'mentions': '3', 'replies': '3'}. Overwrites the entire map.
User
|> update(notifications: "{'mentions': '5', 'replies': '3'}")
|> where(user_id: 10)
|> User.save
Update notification mentions to '5'.
User
|> update("notifications['mentions']": "5")
|> where(user_id: 10)
|> User.save
Update the friends set
User
|> update(friends: "{'jill', 'bob', 'emma'}")
|> where(user_id: 10)
|> User.save
Add a friend_id to friends set
User
|> update(friends: "friends + {'oscar'}")
|> where(user_id: 10)
|> User.save
Remove friend from set
User
|> update(friends: "friends - {'oscar'}")
|> where(user_id: 10)
|> User.save
Update the posts list
User
|> update(posts: "['post1', 'post2', 'post3']")
|> where(user_id: 10)
|> User.save
Append to posts list
User
|> update(posts: "posts + ['post4']")
|> where(user_id: 10)
|> User.save
Prepend to posts list
User
|> update(posts: "['post0'] + posts")
|> where(user_id: 10)
|> User.save