Skip to content

Queries and Statements as Interpolated

Gustavo De Micheli edited this page Aug 30, 2024 · 2 revisions

Let's see how we can define queries as string interpolation:

Usage

First we need to mark a CqlSession implicit:

import net.nmoncho.helenus.*

given CqlSession = cqlSession
given Future[CqlSession] = Future.successful(cqlSession)

Queries as Interpolated Strings

Then we can define queries as Interpolated Strings, using the cql prefix:

val hotel = "Rotterdam Marriott"
// hotel: String = "Rotterdam Marriott"

val hotelsByName = cql"SELECT * FROM hotels_cql WHERE name = $hotel"
// hotelsByName: WrappedBoundStatement[Row] = net.nmoncho.helenus.api.cql.WrappedBoundStatement@41d0f3a4

val resultSet = hotelsByName.execute()
// resultSet: PagingIterable[Row] = com.datastax.oss.driver.internal.core.PagingIterableWrapper@692e3baf

val result = resultSet.nextOption()
// result: Option[Row] = Some(
//   value = com.datastax.oss.driver.internal.core.cql.DefaultRow@1fd72db0
// )

After we interpolate at statement, we'll receive a ScalaBoundStatement, which we cna execute when we see fit.

Interpolated values will be replaced with Named Bind Parameters, making sure the query has been sanitized.

Avoiding Boilerplate

To avoid boilerplate, it's also possible to use constants, defined with final val, in your queries:

object Keyspace {
  final val table = "hotels_cql"

  final val country = "country"
  final val name = "name"
}

val bstmt =
  cql"SELECT ${Keyspace.country}, ${Keyspace.name} FROM ${Keyspace.table} WHERE ${Keyspace.name} = $hotel"
// bstmt: WrappedBoundStatement[Row] = net.nmoncho.helenus.api.cql.WrappedBoundStatement@378cbc01

// Let's take a look at the final query:
bstmt.getPreparedStatement.getQuery
// res3: String = "SELECT country, name FROM hotels_cql WHERE name = :hotel"

val hotelsByNameWithConstants = bstmt.execute().nextOption()
// hotelsByNameWithConstants: Option[Row] = Some(
//   value = com.datastax.oss.driver.internal.core.cql.DefaultRow@71873487
// )

Note: Constants will be interpolated as is, even if they are in bind parameter position. This means that depending on the value of your constant, you may have to wrap it in quotes.

Asynchronous Execution

We can also define interpolated queries asynchronously with the cqlAsync prefix:

val asyncBoundStatement = cqlAsync"SELECT * FROM hotels_cql WHERE name = $hotel"
// asyncBoundStatement: Future[WrappedBoundStatement[Row]] = Future(Success(net.nmoncho.helenus.api.cql.WrappedBoundStatement@69ec9a5b))

val asyncResult = for {
  query <- asyncBoundStatement
  resultSet <- query.executeAsync()
} yield resultSet.currPage.nextOption()
// asyncResult: Future[Option[Row]] = Future(Success(Some(com.datastax.oss.driver.internal.core.cql.DefaultRow@4f70807b)))

Iterating and Extracting Results

Defining and executing queries is one part of the story. The other part is how we make sense of the Rows we get out of these queries.

We can iterate results using some extension methods, and we can map rows to a more meaningful result using RowMappers.