Skip to content

Querying

Flavian Alexandru edited this page May 23, 2016 · 3 revisions

back to top

The query syntax is inspired by the Foursquare Rogue library and aims to replicate CQL 3 as much as possible.

Phantom works with both Scala Futures and Twitter Futures, the later being available as a separate module dependency and import via "com.websudos" %% "phantom-finagle" % phantomVersion" and import com.websudos.phantom.finagle._.

back to top

The full list can be found in CQLQuery.scala.

Method name Description
                                       |

| consistencyLevel_= | Sets the consistency level to use. |

back to top

Method name Description
where The WHERE clause in CQL
and Chains several clauses, creating a WHERE ... AND query
orderBy Adds an ORDER_BY column_name to the query
allowFiltering Allows Cassandra to filter records in memory. This is an expensive operation.
limit Sets the exact number of records to retrieve.

Select queries are very straightforward and enforce most limitations at compile time.

back to top

Operator name Description
eqs The "equals" operator. Will match if the objects are equal
in The "in" operator. Will match if the object is found the list of arguments
gt The "greater than" operator. Will match a the record is greater than the argument and exists
gte The "greater than or equals" operator. Will match a the record is greater than the argument and exists
lt The "lower than" operator. Will match a the record that is less than the argument and exists
lte The "lower than or equals" operator. Will match a the record that is less than the argument and exists

back to top

All partial select queries will return Tuples and are therefore limited to 22 fields for Scala 2.10 compilation.

  def getNameById(id: UUID): Future[Option[String]] = {
    select(_.name).where(_.id eqs someId).one()
  }

  def getNameAndPropsById(id: UUID): Future[Option(String, Map[String, String])] {
    select(_.name, _.props).where(_.id eqs someId).one()
  }

back to top

Method name Description
value A type safe Insert query builder. Throws an error for null values.
valueOrNull This will accept a null without throwing an error.
ttl Sets the "Time-To-Live" for the record.

back to top

Method name Description
where The WHERE clause in CQL
and Chains several clauses, creating a WHERE ... AND query
modify The actual update query builder
onlyIf Addition update condition. Used on non-primary columns

Example:

ExampleRecord.update
  .where(_.id eqs myUuid)
  .modify(_.name setTo "Barack Obama")
  .and(_.props put ("title" -> "POTUS"))
  .future()

back to top

Method name Description
where The WHERE clause in CQL
and Chains several clauses, creating a WHERE ... AND query

Delete queries are very simple ways to either delete a row or alternatively set a column to null. For instance:

BasicTable.update.where(_.id eqs someId).modify(_.someSet setTo Set.empty[String])

// is actually equivalent to

BasicTable.delete(_.someSet).where(_.id eqs someId)

Phantom offers a dual query API based on Scala concurrency primitives, which makes it trivial to use phantom in most known frameworks, such as Play!, Spray, Akka, Scruffy, Lift, and many others. Integration is trivial and easily achievable, all you have to do is to use the Scala API methods and you get out of the box integration.

Phantom also offers another API based on Twitter proprietary concurrency primitives. This is due to the fact that internally we rely very heavily on the Twitter eco-system. It's why phantom also offers Finagle-Thrift support out of the box and integrates with Twitter Scrooge. It fits in perfectly with applications powered by Finagle RPC, Zipkin, Thrift, Ostrich, Aurora, Mesos, and the rest of the Twitter lot.

Method name Description Scala result type
future Executes a command and returns a ResultSet. This is useful when you don't need to return a value. scala.concurrent.Future[ResultSet]
execute Executes a command and returns a ResultSet. This is useful when you don't need to return a value. com.twitter.util.Future[ResultSet]
one Executes a command and returns an Option[T]. Use this when you are selecting and you only need one value. Adds LIMIT 1 to the CQL query. scala.concurrent.Future[Option[Record]]
get Executes a command and returns an Option[T]. Use this when you are selecting and you only need one value. AddsLIMIT 1 to the CQL query. com.twitter.util.Future[Option[Record]]
fetch Returns a sequence of matches. Use when you expect more than 1 match. scala.concurrent.Future[Seq[Record]]
collect Returns a sequence of matches. Use when you expect more than 1 match. com.twitter.util.Future[Seq[Record]
fetchSpool This is useful when you need the underlying ResultSpool. com.twitter.concurrent.Spool[T]]
fetchEnumerator This is useful when you need the underlying Play based enumerator. play.api.libs.iteratee.Enumerator[T]

back to top

Phantom offers a dual asynchronous Future API for the completion of tasks, scala.concurrent.Future and com.twitter.util.Future via the phantom-finagle module, as well as support for Iteratees and reactive-streams via the relevant modules.

However, the concurrency primitives are all based on Google Guava executors and listening decorators. The future API is just for the convenience of users, to satisfy known return types. Scala futures and executors only come into play when you map results, they don't however do the job of actually talking to the database.

The Scala Future methods are:

ExampleRecord.select.one() // When you only want to select one record
ExampleRecord.update.where(_.name eqs name).modify(_.name setTo "someOtherName").future() // When you don't care about the return type.
ExampleRecord.select.fetchEnumerator // when you need an Enumerator
ExampleRecord.select.fetch
ExampleRecord.select.fetchRecord // When you want to fetch a Seq[Record]

back to top

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

object ExampleRecord extends ExampleRecord {
  override val tableName = "examplerecord"

  // now define a session, a normal Datastax cluster connection
  implicit val session = SomeCassandraClient.session;

  def getRecordsByName(name: String): Future[Seq[ExampleModel]] = {
    ExampleRecord.select.where(_.name eqs name).fetch
  }

  def getOneRecordByName(name: String, someId: UUID): Future[Option[ExampleModel]] = {
    ExampleRecord.select.where(_.name eqs name).and(_.id eqs someId).one()
  }
}

back to top

Phantom doesn't depend on Finagle for this, we are simply using "com.twitter" %% "util-core" % Version" to return a com.twitter.util.Future. However, the concurrency primitives are all based on Google Guava executors and listening decorators. The future API is just for the convenience of users.

ExampleRecord.select.get() // When you only want to select one record
ExampleRecord.update.where(_.name eqs name).modify(_.name setTo "someOtherName").execute() // When you don't care about the return type.
ExampleRecord.select.enumerate // when you need an Enumerator
ExampleRecord.select.collect // When you want to fetch a Seq[Record]

back to top

import com.twitter.util.Future

object ExampleRecord extends ExampleRecord {
  override val tableName = "examplerecord"

  // now define a session, a normal Datastax cluster connection
  implicit val session = SomeCassandraClient.session;

  def getRecordsByName(name: String): Future[Seq[ExampleModel]] = {
    ExampleRecord.select.where(_.name eqs name).collect
  }

  def getOneRecordByName(name: String, someId: UUID): Future[Option[ExampleModel]] = {
    ExampleRecord.select.where(_.name eqs name).and(_.id eqs someId).get()
  }
}