sibsibsib / triton

a Cassandra ORM for Elixir

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Triton

Pure Elixir Cassandra ORM built on top of Xandra.

Blog Post

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

About

a Cassandra ORM for Elixir

License:MIT License


Languages

Language:Elixir 100.0%