Skip to content

An Apache Cassandra Client for Scala 2 inspired by Anorm and Quill

License

Notifications You must be signed in to change notification settings

nMoncho/helenus

Repository files navigation

Helenus


main status Maven Central 2.13

Helenus is collection of Scala utilities for Apache Cassandra. Its goal is to make interacting with Cassandra easier, in a type-safe manner, while trying to avoid introducing a complex API.

We also provide integration against several streaming libraries:

  • Akka v2.6 (Apache License)
  • Akka BUSL
  • Flink (Experimental)
  • Pekko

Installation

Include the library into you project definition:

libraryDependencies += "net.nmoncho" %% "helenus-core" % "1.6.1"

Motivation

We tried using libraries such as Phantom and Quill, which are great by the way, but they didn't fit entirely our mindset of workflow. We believe the best way to use Cassandra, or any DB for that matter, is to use its Query Language directly.

Helenus takes inspiration from libraries such as Anorm, trying to provide a similar experience by putting CQL first. Our goals are:

  • Give users control over the queries that are actually executed.
  • Keep the library simple with a concise API.

Features

  • TypeCodecs for Scala types. Every type extending AnyVal, most Scala Collections, Scala Enumeration, etc.
    • Codecs for UDTs defined as Case Classes.
    • Codecs for Tuples defined with Scala Tuples.
  • CQL templating, with String Interpolation. See usage.
  • PreparedStatements and BoundStatements extension methods

Supported Codecs

As of this version, Helenus supports the following types:

  • Java types: String, UUID, Instant, LocalDate, LocalTime, InetAddress.
  • AnyVal types: Boolean, Byte, Double, Float, Int, Long, Short.
    • This means, if used properly, no more boxing.
  • Collections: Seq, List, Vector, Map, Set, SortedMap, SortedSet.
    • If you need a codec that isn't provided out of the box, please read this guide on how to add it.
  • Enumerations: Can be encoded by name or by order. See Enumeration Codecs.
  • Tuples: Encoded as regular Cassandra tuples
  • Case Classes: Encoded as regular Cassandra UDTs
  • Others: Option, and Either (encoded as a tuple).

Usage

// First import helenus...
import net.nmoncho.helenus._

// Then mark your session implicit
implicit val session: CqlSession = getSession
// session: CqlSession = com.datastax.oss.driver.internal.core.session.DefaultSession@6ce6edd8

case class Address(street: String, city: String, stateOrProvince: String, postalCode: String, country: String)

case class Hotel(id: String, name: String, phone: String, address: Address, pois: Set[String])

// We can derive Cassandra TypeCodecs used to map UDTs to case classes
implicit val typeCodec: TypeCodec[Address] = Codec.udtOf[Address]()
// typeCodec: TypeCodec[Address] = UtdCodec[Address]

// We can derive how query results map to case classes
implicit val rowMapper: RowMapper[Hotel] = RowMapper[Hotel]
// rowMapper: RowMapper[Hotel] = net.nmoncho.helenus.internal.CaseClassRowMapperDerivation$$anonfun$net$nmoncho$helenus$internal$CaseClassRowMapperDerivation$$$nestedInanonfun$genericCCRowMapperBuilder$1$1@14c049f2

val hotelId = "h1"
// hotelId: String = "h1"

// We can prepare queries with parameters that don't require boxing
val hotelsById = "SELECT * FROM hotels WHERE id = ?".toCQL
    .prepare[String]
    .as[Hotel]
// hotelsById: internal.cql.ScalaPreparedStatement1[String, Hotel] = net.nmoncho.helenus.internal.cql.ScalaPreparedStatement1@4b9b45d8

// We can extract a single result using `nextOption()`, or
// use `to(Coll)` to transform the result to a collection
hotelsById.execute("h1").nextOption()
// res0: Option[Hotel] = Some(
//   value = Hotel(
//     id = "h1",
//     name = "The New York Hotel Rotterdam",
//     phone = "+31 10 217 3000",
//     address = Address(
//       street = "Meent 78-82",
//       city = "Rotterdam",
//       stateOrProvince = "Zuid-Holland",
//       postalCode = "3011 JM",
//       country = "Netherlands"
//     ),
//     pois = Set("Erasmus Bridge", "Markthal Rotterdam", "Rotterdam Zoo")
//   )
// )

// We can also run the same using CQL interpolated queries
val interpolatedHotelsById = cql"SELECT * FROM hotels WHERE id = $hotelId"
// interpolatedHotelsById: api.cql.WrappedBoundStatement[com.datastax.oss.driver.api.core.cql.Row] = net.nmoncho.helenus.api.cql.WrappedBoundStatement@61a5462f

interpolatedHotelsById.as[Hotel].execute().nextOption()
// res1: Option[Hotel] = Some(
//   value = Hotel(
//     id = "h1",
//     name = "The New York Hotel Rotterdam",
//     phone = "+31 10 217 3000",
//     address = Address(
//       street = "Meent 78-82",
//       city = "Rotterdam",
//       stateOrProvince = "Zuid-Holland",
//       postalCode = "3011 JM",
//       country = "Netherlands"
//     ),
//     pois = Set("Erasmus Bridge", "Markthal Rotterdam", "Rotterdam Zoo")
//   )
// )

For a more detailed guide on how to use Helenus, please read our wiki. We also provide example projects.