Releases: nMoncho/helenus
Helenus v1.6.1
v1.6.0
Helenus v1.6.0 (2024-06-11)
Features
Flink Experimental Support
On this release we add support for Apache Flink. You can use Helenus as either a Source
or a Sink
.
The way this works is a bit different from Akka/Pekko, or just plain Helenus. Since the statements will be ran in different nodes, these have to be late prepared. This means that we provide a thunk to Flink, instead of an already prepared statement.
For example, if we want to define a Source, we can do:
val query = (session: CqlSession) => "SELECT * FROM hotels".toCQL(session).prepareUnit.as[Hotel].apply()
val input: DataStream[Hotel] = env.fromSource(
query.asSource(CassandraSource.Config()),
WatermarkStrategy.noWatermarks(),
"Cassandra Source"
)
Notice that we must provide a thunk of the type: CqlSession => ScalaBoundStatement[Out]
. We don't provide a ScalaPreparedStatement
instead since query parameters cannot be bound during parallel execution.
We can also provide the previous snippet in curried form:
val input: DataStream[Hotel] = env.fromSource(
"SELECT * FROM hotels".toCQL(_)
.prepareUnit
.as[Hotel]
.apply()
.asSource(CassandraSource.Config()),
WatermarkStrategy.noWatermarks(),
"Cassandra Source"
)
The current implementation is heavily influenced by Flink's Cassandra Connector. After we get more experience and feedback, we'll probably adjust and improve our implementation.
This was implemented in this commit:
- Add Flink Source/Sink for DataStream and DataSet (9dbb3 Gustavo De Micheli)
Ignoring null
values
By default Helenus ignores null
values provided to ScalaPreparedStatement
s. The way this works is that after a statement is bound, null values are just skipped from the bind parameters.
Nonetheless sometimes users may want to insert null
into a row (e.g. they are updating a row and want to set that column as empty).
To this purpose you can use the withIgnoreNullFields
method to decided if you want to ignore null fields or not (true
by default):
"INSERT INTO hotels(id, name, phone, address, pois) VALUES (?, ?, ?, ?, ?)".stripMargin.toCQL
.prepare[String, String, String, Address, Set[String]]
.withIgnoreNullFields(ignore = false)
This was implemented in these two commits:
- Allow
prepareFrom
queries to ignore 'null/None' through options (441a8 Gustavo De Micheli) - Allow users to choose if 'null/None' fields are ignored or not when binding parameters to a statement. (fab1d Gustavo De Micheli)
Add oneOption
extension method as convenience for queries with a single result
In previous releases we added the nextOption
method which would return an
Future[Option[(T, MappedAsyncPagingIterable)]]
. This was done to provide a similar
API to Pager, in which the client code could iterate over results while keeping
track of the mutated paging iterable, which would change when fetching the next page.
An example can be seen on the tests:
result <- "SELECT * FROM hotels".toCQLAsync.prepareUnit
.as[Hotel]
.map(_.withPageSize(2))
.executeAsync()
Some((hotelA, iteratorA)) <- result.nextOption()
Some((hotelB, iteratorB)) <- iteratorA.nextOption() // we use 'iteratorA', not 'result'
Some((hotelC, iteratorC)) <- iteratorB.nextOption()
Some((hotelD, iteratorD)) <- iteratorC.nextOption()
Some((hotelE, iteratorE)) <- iteratorD.nextOption()
lastResult <- iteratorE.nextOption()
This is cumbersome when we want only the first result, for example when
we use a LIMIT 1
in the query, which is where oneOption
becomes useful:
result <- "SELECT * FROM hotels LIMIT 1".toCQLAsync.prepareUnit
.as[Hotel]
.executeAsync()
firstResult = result.oneOption // this should be Some
nextResult = result.oneOption // this will always be None
oneOption
is no longer providing a Future
but only an Option
as the
page is already fetched.
This was implemented in this commit:
- Add
oneOption
extension method to MappedAsyncPagingIterable. (e7878 Gustavo De Micheli)
Other Features
- Add extension methods to
Future[ScalaBoundStatement[Out]]
like the synchronous counterpart had: (f0d44 Gustavo De Micheli)
Other changes
Update shapeless to 2.3.12
5e46a Scala Steward 2024-05-20 08:25:26
Update mockito-core to 5.12.0
f5ff3 Scala Steward 2024-05-11 17:32:11
Update logback-classic to 1.5.6
546c4 Scala Steward 2024-05-11 10:10:21
Update sbt-scalafix to 0.12.1
7f359 Scala Steward 2024-05-11 10:10:09
Update scalacheck to 1.18.0
b4a90 Scala Steward 2024-05-11 10:09:59
Update scalacheck to 1.17.1
2fe71 Scala Steward 2024-04-19 13:06:41
Update scala-collection-compat to 2.12.0
725b3 Scala Steward 2024-04-19 12:54:52
Update logback-classic to 1.5.5
a1805 Scala Steward 2024-04-14 09:34:43
Update slf4j-api to 2.0.13
43062 Scala Steward 2024-04-14 09:34:23
Update scalafmt-core to 3.8.1
eaceb Scala Steward 2024-04-10 12:54:39
Update logback-classic to 1.5.4
0037c Scala Steward 2024-04-10 12:54:23
Update scalafmt-core to 3.8.0
1a034 Scala Steward 2024-03-01 16:10:53
Helenus v1.5.0
v1.5.0 (2024-02-13)
Features
- Allow RowMapper to handle Either fields encoded as different columns. This provides an alterinative to the EitherCodec where values are encoded in a tuple (27219 Gustavo De Micheli)
- Add integration between Mapped Statements and Akka/Pekko Streams (751ae Gustavo De Micheli)
- Add short-hand methods to prepare and execute 'prepareFrom' on Async statements (02cb4 Gustavo De Micheli)
Other changes
Update mockito-core to 5.10.0
36cd5 Scala Steward 2024-02-03 18:42:39
Update jna to 5.14.0
ff63d Scala Steward 2024-02-03 18:42:08
Update slf4j-api to 2.0.11
0ceb7 Scala Steward 2024-02-03 18:41:42
Update pekko-connectors-cassandra to 1.0.2
fafc1 Scala Steward 2024-02-03 18:41:21
Helenus v1.4.1
Release Description
This release includes:
- Delegate
accepts
to inner codec forOptionCodec
Delegate accepts
to inner codec for OptionCodec
An unnecessary warning would be logged if a Option of an UDT would be used, as the OptionCodec
wouldn't delegate the information properly. This release fixes that.
Helenus v1.4.0
Release Description
This release includes:
- Add
Pager
on Interpolated Queries and ScalaBoundStatements - Provide
PagingState
as Materialized Value - Add Interpolated Statement Queries to Akka/Pekko Streams
Add Pager
on Interpolated Queries and ScalaBoundStatements
In this release users can make use of Pager
from Interpolated Queries and ScalaBoundStatement
s. In previous releases this feature was limited to ScalaPreparedStatement
s.
Provide PagingState
as Materialized Value
In previous releases a Pager
could be executed reactively providing a Publisher[(Pager[Out], Out)]
, where the first tuple element could be used to fetch the next page. This value would be duplicated for every element of the page.
From this release both Akka and Pekko Streams can get the PagingState
as the Materialized Value of the stream. This PagingState
can be used later on to create another Pager
and resume the query execution.
Add Interpolated Statement Queries to Akka/Pekko Streams
This feature is long coming. From this release we can define Interpolated Queries as Akka/Pekko Sources.
Its intended use is to define a query where we get data from. This feature doesn't extend nicely to a Write Sink since bind parameters are bound when the query is constructed, which would only work for one set of values, instead of a ScalaPreparedStatement
where bind parameters are bound for each element in the stream going to the Sink.
Helenus v1.3.1
Release Description
This release includes:
- Verify 'ScalaPrepareStatement' arity
- Iterate future results with 'nextOption'
- Allow to create UDT Codec by defining field order
- Introduce
Pager
- Introduce a
Mapping
to combine an Adapter and a RowMapper
Verify ScalaPrepareStatement
arity
Specifying the same arity of bind parameters and function parameters can be error prone. This release introduces a runtime check that will warn users if the arity if different. It will also check that the expected and the provided type match.
Unfortunately there is no way to provide this information a compile time, like Phantom or Quill does. We believe that the database is the source of truth, so things like this will always have to be checked a runtime.
Iterate future results with nextOption
Iterating results of an asynchronous execution works like pagination. This release introduces a fix that would prevent users from requesting the next page after the first.
Allow to create UDT Codec by defining field order
On previous releases Helenus provided two ways of defining UDT Codecs: udtOf
when case classes fields were defined in the same order as the CQL Type, and udtFrom
when this wasn't the case.
udtFrom
was a bit cumbersome since it required a CqlSession
to build the UDT Mapping. This release allows users to define the order of the fields without requiring a CqlSession
using the udtFromFields
method:
// For the type:
// CREATE TYPE IF ice_cream (name TEXT, num_cherries INT, cone BOOLEAN)
// The fields `cone` and `numCherries` are swapped in respect with the CQL type
case class IceCream(name: String, cone: Boolean, numCherries: Int)
// The second parameter list defines what's the order of the CQL Fields
val codec: TypeCodec[IceCream] =
Codec.udtFromFields[IceCream]("keyspace", "ice_cream", frozen = true)(_.name, _.numCherries, _.cone)
Due to a macro limitation we cannot provide default arguments, and users must define the keyspace, cql type name, and if it's frozen. Nonetheless these parameters can be empty strings and the implementation will choose the correct values from context.
Introduce Pager
This release introduces Pager
, an abstraction that allows users to paginate over query results. You can read more about it in our wiki Pagination
Introduce a Mapping
This release introduces Mapping
, a way to combine an Adapter
and a RowMapper
.
Its intended use is to allow users to have an ORM-like feature where a case class can be used to insert and query values from a table.
This also allows users to overcome the limitation of having statements with up to 22 parameters.
Helenus v1.2.2
Release Description
This release includes:
- Optional elements on tuples now return
None
when empty
Optional elements on tuples now return None
when empty
Just like the previous bugfix, this release takes care of handling null
values on tuples
Helenus v1.2.1
Release Description
This release includes:
- Optional Fields on UTDs now return
None
when empty
Optional Fields on UTDs now return None
when empty
On previous release when a UDT had a field that was an Option[A]
it would be encoded as null
in the database when empty, which when retrieved would be set also to null
on the case class
, whereas the desired behavior would be to use a None
.
This release fixes this issue.
Helenus v1.2.0
Release Description
This release includes:
- Allow
MappedAsyncPagingIterable
to be iterated one element at a time - Allow Async
ScalaPreparedStatement
to use an explicitfrom
Adapter. - Add
withOptions
method toScalaBoundStatement
- Allow
RowMapper
to be defined explicitly - Scala Steward Updates
Allow MappedAsyncPagingIterable
to be iterated one element at a time
Now MappedAsyncPagingIterable
has a similar nextOption
extension method as PagingIterable
. This method should be handy when getting optional values from an async execution. You can still do fut.map(page => Option(page.one()))
if you're only interested in the first element.
Allow Async ScalaPreparedStatement
to use an explicit from
Adapter.
Usually we use an Adapter
to define how a case class
translates into a tuple to be used in a query. With the synchronous API we could define an Adapter
explicitly but this was missing from the Async extensions methods. This release adds that capability to the former
Add withOptions
method to ScalaBoundStatement
In previous releases we added the ability to set options, such as ConsistencyLevel
, to a ScalaPreparedStatement
. We could still set these options directly to the BoundStatement
as the original Java Driver intends, but we would loose the output type of the query.
This release introduces two new extension methods on the ScalaBoundStatement[Out]
so it's possible to set options while retaining its output type.
Allow RowMapper
to be defined explicitly
In some cases we realized that it's more convenient to use a RowMapper
explicitly.
Helenus v1.1.0
Release Description
This release includes:
- Update MiMa Configuration
- Add
getCol
methods to Row Extension - Scala Steward Updates
Update MiMa Configuration
After the v1.0.0
release we modified the project build.sbt
to use that release for checking changes and detect compatibility problems against. We also add a step on the CI pipeline to verify each new commit.
Add getCol
methods to Row Extension
We added a new integration point against the Java Driver Row
. You can now get a row's value with a single method, whether that's a "primitive" value, a collection, or a UDT.
Where as before you would do:
case class IceCream(name: String, numCherries: Int, cone: Boolean)
IceCream(
row.getString("name"),
row.getInt("num_cherries"),
row.getBoolean("cone")
)
Now you can do:
IceCream(
row.getCol[String]("name"),
row.getCol[Int]("num_cherries"),
row.getCol[Boolean]("cone")
)