Is it possible to parameterize queries or parameters for an Acolyte ScalaCompositeHandler? - sql

Background:
I have attempted to accomplish the question defined here, and I have not been able to succeed. Acolyte requires you to define the queries and parameters you want to handle within a match expression, and the values used in match expressions must be known at compile time. (Note, however, that this StackOverflow answer appears to provide a way around this limitation).
If this is indeed not possible, the inability to dynamically define the parameters and queries for Acolyte would be, for my use case, a severe limitation of the framework. I suspect this would be a limitation for others as well.
One SO user who has advocated for the use of Acolyte across a handful of questions stated in this comment that it is possible to dynamically define queries and their responses. So, I have opened this question as an invitation for someone to show that to be the case.
Question:
Using Acolyte, I want to be able to encapsulate the logic for matching queries and generating their responses. This is a desired feature because I want to keep my code DRY. In other words, I am looking for something like the following pseudo-code:
def generateHandler(query: String, accountId: Int, parameters: Seq[String]): ScalaCompositeHandler = AcolyteDSL.handleQuery {
parameters.foreach(p =>
// Tell the handler to handle this specific parameter
case acolyte.jdbc.QueryExecution(query, ExecutedParameter(accountId) :: ExecutedParameter(p) :: Nil) =>
someResultFunction(p)
)
}
Is this possible in Acolyte? If so, please provide an example.

It is indeed possible to parameterize queries and/or parameters by utilizing pattern matching.
See the code below for an example:
import java.sql.DriverManager
import acolyte.jdbc._
import acolyte.jdbc.Implicits._
import org.scalatest.FunSpec
class AcolyteTest extends FunSpec {
describe("Using pattern matching to extract a query parameter") {
it("should extract the parameter and make it usable for dynamic result returning") {
val query = "SELECT someresult FROM someDB WHERE id = ?"
val rows = RowLists.rowList1(classOf[String] -> "someresult")
val handlerName = "testOneHandler"
val handler = AcolyteDSL.handleQuery {
case acolyte.jdbc.QueryExecution(`query`, ExecutedParameter(id) :: _) =>
rows.append(id.toString)
}
Driver.register(handlerName, handler)
val connection = DriverManager.getConnection(s"jdbc:acolyte:anything-you-want?handler=$handlerName")
val preparedStatement = connection.prepareStatement(query)
preparedStatement.setString(1, "hello world")
val resultSet = preparedStatement.executeQuery()
resultSet.next()
assertResult(resultSet.getString(1))("hello world")
}
it("should support a slightly more complex example") {
val firstResult = "The first result"
val secondResult = "The second result"
val query = "SELECT someresult FROM someDB WHERE id = ?"
val rows = RowLists.rowList1(classOf[String] -> "someresult")
val results: Map[String, RowList1.Impl[String]] = Map(
"one" -> rows.append(firstResult),
"two" -> rows.append(secondResult)
)
def getResult(parameter: String): QueryResult = {
results.get(parameter) match {
case Some(row) => row.asResult()
case _ => acolyte.jdbc.QueryResult.Nil
}
}
val handlerName = "testTwoHandler"
val handler = AcolyteDSL.handleQuery {
case acolyte.jdbc.QueryExecution(`query`, ExecutedParameter(id) :: _) =>
getResult(id.toString)
}
Driver.register(handlerName, handler)
val connection = DriverManager.getConnection(s"jdbc:acolyte:anything-you-want?handler=$handlerName")
val preparedStatement = connection.prepareStatement(query)
preparedStatement.setString(1, "one")
val resultSetOne = preparedStatement.executeQuery()
resultSetOne.next()
assertResult(resultSetOne.getString(1))(firstResult)
preparedStatement.setString(1, "two")
val resultSetTwo = preparedStatement.executeQuery()
resultSetTwo.next()
assertResult(resultSetTwo.getString(1))(secondResult)
}
}
}

Related

How to use Lucene's DistinctValuesCollector?

My objective is to collect distinct values of select fields to provided them as filter options for the frontend. DistinctValuesCollector seems to be the tool for this, however since I haven't found code sample and documentation except for the Javadocs I can't currently correctly construct this collector. Can anyone provide an example?
This is my attempt which doesn't deliver the desired distinct values of the field PROJEKTSTATUS.name.
val groupSelector = TermGroupSelector(PROJEKTSTATUS.name)
val searchGroup = SearchGroup<BytesRef>()
val valueSelector = TermGroupSelector(PROJEKTSTATUS.name)
val groups = mutableListOf(searchGroup)
val distinctValuesCollector = DistinctValuesCollector(groupSelector, groups, valueSelector)
That field is indexed as follows:
document.add(TextField(PROJEKTSTATUS.name, aggregat.projektstatus, YES))
document.add(SortedDocValuesField(PROJEKTSTATUS.name, BytesRef(aggregat.projektstatus)))
Thanks to #andrewJames's hint to a test class I could figure it out:
fun IndexSearcher.collectFilterOptions(query: Query, field: String, topNGroups: Int = 128, mapper: Function<String?, String?> = Function { it }): Set<String?> {
val firstPassGroupingCollector = FirstPassGroupingCollector(TermGroupSelector(field), Sort(), topNGroups)
search(query, firstPassGroupingCollector)
val topGroups = firstPassGroupingCollector.getTopGroups(0)
val groupSelector = firstPassGroupingCollector.groupSelector
val distinctValuesCollector = DistinctValuesCollector(groupSelector, topGroups, groupSelector)
search(query, distinctValuesCollector)
return distinctValuesCollector.groups.map { mapper.apply(it.groupValue.utf8ToString()) }.toSet()
}

R2DBC: How to bind data class for sql query without needing all parameters?

I am trying to bind my data class for a sql query but I am getting a error when I am not using all the parameters from my data class. Is there a way to check in the sql query which parameters needs binding and which ones do not or allow to bind parameters which are not used. The error looks as following:
Request error
java.lang.IllegalArgumentException: Identifier 'deleted_at' is not a valid identifier. Should be of the pattern '\$([\d]+)'.
at io.r2dbc.postgresql.ExtendedQueryPostgresqlStatement.getIndex(ExtendedQueryPostgresqlStatement.java:196)
Caused by: java.lang.IllegalArgumentException: Identifier 'deleted_at' is not a valid identifier. Should be of the pattern '\$([\d]+)'.
at io.r2dbc.postgresql.ExtendedQueryPostgresqlStatement.getIndex(ExtendedQueryPostgresqlStatement.java:196)
And this is the code I use:
Repository:
client
.sql(
"""
INSERT INTO app_user_settings (uuid, allows_to_process_transactions, app_user_id) VALUES (:uuid, :allows_to_process_transactions, :app_user_id)
RETURNING *
""".trimIndent()
)
.bind(AppUserSettingsWriteConverter(), appUserSettings)
.map(AppUserSettingsReadConverter()::convert)
.awaitOne()
Custom bind method:
fun <T> DatabaseClient.GenericExecuteSpec.bind(
convertor: Converter<T, OutboundRow>,
value: T
): DatabaseClient.GenericExecuteSpec {
val outboundRow = convertor.convert(value!!)!!
val t = outboundRow.toMap()
var execution = this
t.forEach { (t, u) ->
execution = execution.bind(t.toString(), u)
}
return execution
}
WriteConverter:
class AppUserSettingsWriteConverter : Converter<AppUserSettings, OutboundRow> {
override fun convert(source: AppUserSettings): OutboundRow {
val outboundRow = OutboundRow()
if (source.isSaved()) {
outboundRow[SqlIdentifier.unquoted("id")] = Parameter.from(source.id)
}
outboundRow[SqlIdentifier.unquoted("uuid")] = Parameter.from(source.uuid)
outboundRow[SqlIdentifier.unquoted("allows_to_process_transactions")] = Parameter.from(source.allowsToProcessTransactions)
outboundRow[SqlIdentifier.unquoted("app_user_id")] = Parameter.from(source.appUserId)
outboundRow[SqlIdentifier.unquoted("deleted_at")] = Parameter.fromOrEmpty(source.deletedAt, ZonedDateTime::class.java)
return outboundRow
}
}
I am using now a check if deleted_at is empty and then not bind it but would prefer if there is another way to do it.

Kotlin nested for loops to asSequence

I'm trying to convert my nested for loop to asSequence in Kotlin. Here, my goal is to get and update the value of all my object array from another object array with the same key.
nested for loop:
val myFields = getMyFields()
val otherFields = getOtherFields()
for (myField in myFields) { // loop tru the my fields
for (otherField in otherFields) { // find the same fields
if (myField.key == otherField.key) { // if the same, update the value
val updatedMyField = myField.copy(value = otherValue.value)
myFields[myFields.indexOf(myField)] = updatedMyField // update my field value
break
}
}
}
What I've tried:
val updatedMyFields = getMyFields().asSequence()
.map { myField ->
getOtherFields().asSequence()
.map { otherField ->
if (myField.key == otherField.key) {
return#map otherField.value
} else {
return#map ""
}
}
.filter { it?.isNotEmpty() == true }
.first()?.map { myField.copy(value = it.toString()) }
}
.toList()
but this does not compile as it will return List<List<MyField>>.
I'm just looking for something much cleaner for this.
As comments suggest, this would probably be much more efficient with a Map.
(More precisely, a map solution would take time proportional to the sum of the list lengths, while the nested for loop takes time proportional to their product — which gets bigger much faster.)
Here's one way of doing that:
val otherFields = getOtherFields().associate{ it.key to it.value }
val myFields = getMyFields().map {
val otherValue = otherFields[it.key]
if (otherValue != null) it.copy(value = otherValue) else it
}
The first line creates a Map from the ‘other fields’ keys to their values.  The rest then uses it to create a new list from ‘my fields’, substituting the values from the ‘other fields’ where present.
I've had to make assumptions about the types &c, since the code in the question is incomplete, but this should do the same.  Obviously, you can change how it merges the values by amending the it.copy().
There are likely to be even simpler and more efficient ways, depending on the surrounding code.  If you expanded it into a Minimal, Complete, and Verifiable Example — in particular, one that illustrates how you already use a Map, as per your comment — we might be able to suggest something better.
Why do you want to use asSequence() ? You can go for something like that:
val myFields = getMyFields()
val otherFields = getOtherFields()
myFields.forEach{firstField ->
otherFields.forEach{secondField ->
if (firstField.key == secondField.key) {
myFields[myFields.indexOf(firstField)] = secondField.value
}
}
}
This will do the same job than your nested for loop and it's easier to read, to understand and so to maintain than your nested asSequence().

How to typesafe reduce a Collection of Either to only Right

Maybe a stupid question but I just don't get it.
I have a Set<Either<Failure, Success>> and want to output a Set<Success> with Arrow-kt.
You can map the set like this for right:
val successes = originalSet.mapNotNull { it.orNull() }.toSet()
or if you want the lefts:
val failures = originalSet.mapNotNull { it.swap().orNull() }.toSet()
The final toSet() is optional if you want to keep it as a Set as mapNotNull is an extension function on Iterable and always returns a List
PS: No stupid questions :)
Update:
It can be done avoiding nullables:
val successes = originalSet
.map { it.toOption() }
.filter { it is Some }
.toSet()
We could potentially add Iterable<Option<A>>.filterSome and Iterable<Either<A, B>.mapAsOptions functions.
Update 2:
That last example returns a Set<Option<Success>>. If you want to unwrap the results without using null then one thing you can try is to fold the Set:
val successes = originalSet
.fold(emptySet<Success>()) { acc, item ->
item.fold({ acc }, { acc + it })
}
This last option (unintended pun) doesn't require the use of Option.

Slick 2 - Update columns in a table and return whole table object

How would you update a few columns in a table table while returning the entire updated table when using slick?
Assuming SomeTables is some TableQuery, you would typically write a query like this if you want to, for example, add an item to the table (and returning the newly added item)
val returnedItem = SomeTables returning SomeTables += someTable
How would you do the same if you want to update an item and return the whole back the whole item, I suspect you would do something like this
val q = SomeTables.filter(_.id === id).map(x => (x.someColumn,x.anotherColumn)) returning SomeTables
val returnedItem = q.update((3,"test"))
The following code however does not work, and I can't see any documentation on how to do this
Note that I am aware you can just query the item beforehand, update it, and then use copy on the original object, however this requires a lot of boilerplate (and DB trips as well)
This feature is not supported in Slick (v2 or v3-M1); although I don't see any specific reason prohibiting it's implementation, UPDATE ... RETURNING is not a standard SQL feature (for example, H2 does not support it: http://www.h2database.com/html/grammar.html#update). I'll leave as an exercise to the reader to explore how one might safely and efficiently emulate the feature for RDBMSes lacking UDPATE ... RETURNING.
When you call "returning" on a scala.slick.lifted.Query, it gives you a JdbcInsertInvokerComponent$ReturningInsertInvokerDef. You'll find no update method, although there is an insertOrUpdate method; however, insertOrUpdate only returns the returning expression result if an insert occurs, None is returned for updates, so no help here.
From this we can conclude that if you want to use the UPDATE ... RETURNING SQL feature, you'll either need to use StaticQuery or roll your own patch to Slick. You can manually write your queries (and re-implement your table projections as GetResult / SetParameter serializers), or you can try this snippet of code:
package com.spingo.slick
import scala.slick.driver.JdbcDriver.simple.{queryToUpdateInvoker, Query}
import scala.slick.driver.JdbcDriver.{updateCompiler, queryCompiler, quoteIdentifier}
import scala.slick.jdbc.{ResultConverter, CompiledMapping, JdbcBackend, JdbcResultConverterDomain, GetResult, SetParameter, StaticQuery => Q}
import scala.slick.util.SQLBuilder
import slick.ast._
object UpdateReturning {
implicit class UpdateReturningInvoker[E, U, C[_]](updateQuery: Query[E, U, C]) {
def updateReturning[A, F](returningQuery: Query[A, F, C], v: U)(implicit session: JdbcBackend#Session): List[F] = {
val ResultSetMapping(_,
CompiledStatement(_, sres: SQLBuilder.Result, _),
CompiledMapping(_updateConverter, _)) = updateCompiler.run(updateQuery.toNode).tree
val returningNode = returningQuery.toNode
val fieldNames = returningNode match {
case Bind(_, _, Pure(Select(_, col), _)) =>
List(col.name)
case Bind(_, _, Pure(ProductNode(children), _)) =>
children map { case Select(_, col) => col.name } toList
case Bind(_, TableExpansion(_, _, TypeMapping(ProductNode(children), _, _)), Pure(Ref(_), _)) =>
children map { case Select(_, col) => col.name } toList
}
implicit val pconv: SetParameter[U] = {
val ResultSetMapping(_, compiled, CompiledMapping(_converter, _)) = updateCompiler.run(updateQuery.toNode).tree
val converter = _converter.asInstanceOf[ResultConverter[JdbcResultConverterDomain, U]]
SetParameter[U] { (value, params) =>
converter.set(value, params.ps)
}
}
implicit val rconv: GetResult[F] = {
val ResultSetMapping(_, compiled, CompiledMapping(_converter, _)) = queryCompiler.run(returningNode).tree
val converter = _converter.asInstanceOf[ResultConverter[JdbcResultConverterDomain, F]]
GetResult[F] { p => converter.read(p.rs) }
}
val fieldsExp = fieldNames map (quoteIdentifier) mkString ", "
val sql = sres.sql + s" RETURNING ${fieldsExp}"
val unboundQuery = Q.query[U, F](sql)
unboundQuery(v).list
}
}
}
I'm certain the above can be improved; I've written it based on my somewhat limited understanding of Slick internals, and it works for me and can leverage the projections / type-mappings you've already defined.
Usage:
import com.spingo.slick.UpdateReturning._
val tq = TableQuery[MyTable]
val st = tq filter(_.id === 1048003) map { e => (e.id, e.costDescription) }
st.updateReturning(tq map (identity), (1048003, Some("such cost")))