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()
}
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.
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().
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.
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")))