Getting Started With Phantom

Reading Time: 3 minutes

phantomPhantom is Reactive type-safe Scala driver for Apache Cassandra/Datastax Enterprise. So, first lets explore what Apache Cassandra is with some basic introduction to it.

Apache Cassandra

Apache Cassandra is a free, open source data storage system that was created at Facebook in 2008. It is highly scalable database designed to handle large amounts of data across many commodity servers, providing high availability with no single point of failure. It is a type of NoSQL database which is Schema-free. For more about Cassandra refer to this blog Getting Started With Cassandra.

Phantom-DSL

We wanted to integrate Cassandra into Scala ecosystem that’s why we used Phantom-DSL as one of the module of outworkers. So, if you are planning on using Cassandra with Scala, phantom is the weapon of choice because of :-

  • Ease of use and quality coding.
  • Reducing code and boilerplate by at least 90%.
  • Automated schema generation

You don’t need to switch to CQL(Cassandra Query Langguage) to create keyspace in Cassandra.

So, for that first you need to add up the dependency into yout project :-



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


libraryDependencies += "com.outworkers" % "phantom-dsl_2.11" % "2.14.5"

You need to import this by default in your code:-



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


import com.outworkers.phantom.dsl._

Connecting to a Cassandra cluster

Now, you can connect to Cassandra cluster by creating a ContactPoint called Connector which is basically required to run the application. when we start creating databases, we pass in a ContactPoint or what we call a connector in more plain English.



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


import com.outworkers.phantom.dsl._
object Defaults {
val Connector = ContactPoint.local.keySpace(KeySpace("my_keyspace")
.ifNotExists().`with`(replication eqs SimpleStategy.replication_factor(1)))
}
class CassandraDatabase(override val connector: CassandraConnection) extends Database[CassandraDatabase](connector) {
object users extends Users with Connector
}
object CassandraDatabase extends CassandraDatabase(Connector.default)
trait DbProvider extends DatabaseProvider[CassandraDatabase] {
override val database = CassandraDatabase
}
view raw

Defaults.scala

hosted with ❤ by GitHub

You have added the dependency and connected to your Cassandra cluster now, You are ready to create your very first table. Let’s assume we are modelling the CQL schema for a case class that looks like this:-



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


case class User(
id: String,
email: Set[String],
name: String
)
view raw

User.scala

hosted with ❤ by GitHub

Here’s how the Phantom DSL code looks like:-



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


import com.outworkers.phantom.dsl._
abstract class Users extends Table[Users, User] {
object id extends StringColumn with PartitionKey
object email extends SetColumn[String]
object name extends StringColumn with PartitionKey
}
view raw

Users.scala

hosted with ❤ by GitHub

where Users is the table and User is the case class. All the fields with their column type has been defined. There must be atleast one field which must be a Partition Key same as a Primary Key in SQL. Partition key tells how your data is partitioned across the cluster.

Earlier, we were using CassandraTable trait instead of Table trait that we have mentioned in the above Phantom DSL code. By extending Table trait you don’t need to extend the RootConnector explicitly as Table trait already extends CassandraTable with RootConnector.



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


trait Table[T <: Table[T, R], R] extends CassandraTable[T, R] with TableAliases[T, R] with RootConnector
view raw

Table.scala

hosted with ❤ by GitHub

So, the question is Why we needed to define RootConnector?

The simple answer for it is RootConnector defines the Keyspace and Session for you. It tells the table a connector with a `session` and `keySpace` will be injected later on.



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


package com.outworkers.phantom.connectors
trait RootConnector extends scala.AnyRef {
implicit def space : com.outworkers.phantom.connectors.KeySpace
implicit def session : com.datastax.driver.core.Session
}

You have created Keyspace and a table now, its time to insert data into the Cassandra table. You can do this by writing the below code snippet into the class Users.scala.



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


def save(user: User): Future[ResultSet] = {
insert
.value(_.id, user.id)
.value(_.name, user.name)
.value(_.email, user.email)
.future()
}
view raw

Save.scala

hosted with ❤ by GitHub

For quering your table, to get the data out of the table as needed by using various clauses and operators. Here, is the small code snippet :-



This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters


def getAllUsers: Future[List[User]] = {
select
.fetch()
}
def getByid(id: String): Future[Option[String]] = {
select(_.name)
.where(_.id eqs id)
.allowFiltering()
.one()
}
def getByQuery(email: String, name: String): Future[Option[(String, Set[String])]] = {
select(_.id, _.email)
.where(_.email contains email)
.and(_.name eqs name)
.allowFiltering()
.one()
}
def getByidAndName(id: String): Future[List[User]] = {
update
.where(_.id eqs id)
.modify(_.name setTo "newUser")
.future()
getAllUsers
}
view raw

Queries.scala

hosted with ❤ by GitHub

Hope you liked the blog. Happy coding!! 🙂

References


knoldus-advt-sticker


 

Written by 

Charmy is a Software Consultant having experience of more than 1.5 years. She is familiar with Object Oriented Programming Paradigms and has familiarity with Technical languages such as Scala, Lagom, Java, Apache Solr, Apache Spark, Apache Kafka, Apigee. She is always eager to learn new concepts in order to expand her horizon. Her hobbies include playing guitar and Sketching.

5 thoughts on “Getting Started With Phantom3 min read

  1. Awesome read. Just got here randomly and found this post very informative. Shared it with my friends.

Comments are closed.

Discover more from Knoldus Blogs

Subscribe now to keep reading and get access to the full archive.

Continue reading