mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-12-23 17:17:14 +00:00
commit
027e927e1b
@ -1,5 +1,12 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.12.3
|
||||||
|
|
||||||
|
* `Repos`:
|
||||||
|
* `Exposed`:
|
||||||
|
* Add abstract exposed variants of `KeyValue` and `KeyValues` repos
|
||||||
|
* Add new extension `Query#selectPaginated`
|
||||||
|
|
||||||
## 0.12.2
|
## 0.12.2
|
||||||
|
|
||||||
* `Versions`:
|
* `Versions`:
|
||||||
|
@ -14,5 +14,5 @@ crypto_js_version=4.1.1
|
|||||||
# Project data
|
# Project data
|
||||||
|
|
||||||
group=dev.inmo
|
group=dev.inmo
|
||||||
version=0.12.2
|
version=0.12.3
|
||||||
android_code_version=141
|
android_code_version=142
|
||||||
|
@ -26,7 +26,6 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
|
|||||||
override val deletedObjectsIdsFlow: Flow<IdType> = _deletedObjectsIdsFlow.asSharedFlow()
|
override val deletedObjectsIdsFlow: Flow<IdType> = _deletedObjectsIdsFlow.asSharedFlow()
|
||||||
|
|
||||||
protected abstract fun InsertStatement<Number>.asObject(value: InputValueType): ObjectType
|
protected abstract fun InsertStatement<Number>.asObject(value: InputValueType): ObjectType
|
||||||
abstract val selectByIds: SqlExpressionBuilder.(List<IdType>) -> Op<Boolean>
|
|
||||||
|
|
||||||
protected abstract fun insert(value: InputValueType, it: InsertStatement<Number>)
|
protected abstract fun insert(value: InputValueType, it: InsertStatement<Number>)
|
||||||
protected abstract fun update(id: IdType, value: InputValueType, it: UpdateStatement)
|
protected abstract fun update(id: IdType, value: InputValueType, it: UpdateStatement)
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.exposed
|
||||||
|
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
|
||||||
|
interface CommonExposedRepo<IdType, ObjectType> : ExposedRepo {
|
||||||
|
val ResultRow.asObject: ObjectType
|
||||||
|
val selectById: SqlExpressionBuilder.(IdType) -> Op<Boolean>
|
||||||
|
val selectByIds: SqlExpressionBuilder.(List<IdType>) -> Op<Boolean>
|
||||||
|
get() = { list ->
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
Op.FALSE
|
||||||
|
} else {
|
||||||
|
var op = selectById(list.first())
|
||||||
|
(1 until list.size).forEach {
|
||||||
|
op = op.or(selectById(list[it]))
|
||||||
|
}
|
||||||
|
op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,4 @@ package dev.inmo.micro_utils.repos.exposed
|
|||||||
|
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
|
|
||||||
interface ExposedCRUDRepo<ObjectType, IdType> : ExposedRepo {
|
interface ExposedCRUDRepo<ObjectType, IdType> : CommonExposedRepo<IdType, ObjectType>
|
||||||
val ResultRow.asObject: ObjectType
|
|
||||||
val selectById: SqlExpressionBuilder.(IdType) -> Op<Boolean>
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.exposed.keyvalue
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.repos.KeyValueRepo
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import org.jetbrains.exposed.sql.statements.InsertStatement
|
||||||
|
import org.jetbrains.exposed.sql.statements.UpdateStatement
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|
||||||
|
abstract class AbstractExposedKeyValueRepo<Key, Value>(
|
||||||
|
override val database: Database,
|
||||||
|
tableName: String? = null
|
||||||
|
) : KeyValueRepo<Key, Value>, AbstractExposedReadKeyValueRepo<Key, Value>(
|
||||||
|
database,
|
||||||
|
tableName
|
||||||
|
) {
|
||||||
|
protected val _onNewValue = MutableSharedFlow<Pair<Key, Value>>()
|
||||||
|
protected val _onValueRemoved = MutableSharedFlow<Key>()
|
||||||
|
|
||||||
|
override val onNewValue: Flow<Pair<Key, Value>> = _onNewValue.asSharedFlow()
|
||||||
|
override val onValueRemoved: Flow<Key> = _onValueRemoved.asSharedFlow()
|
||||||
|
|
||||||
|
protected abstract fun update(k: Key, v: Value, it: UpdateStatement)
|
||||||
|
protected abstract fun insert(k: Key, v: Value, it: InsertStatement<Number>)
|
||||||
|
|
||||||
|
override suspend fun set(toSet: Map<Key, Value>) {
|
||||||
|
transaction(database) {
|
||||||
|
toSet.mapNotNull { (k, v) ->
|
||||||
|
if (update({ selectById(k) }) { update(k, v, it) } > 0) {
|
||||||
|
k to v
|
||||||
|
} else {
|
||||||
|
val inserted = insert {
|
||||||
|
insert(k, v, it)
|
||||||
|
}.getOrNull(keyColumn) != null
|
||||||
|
if (inserted) {
|
||||||
|
k to v
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.forEach {
|
||||||
|
_onNewValue.emit(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun unset(toUnset: List<Key>) {
|
||||||
|
transaction(database) {
|
||||||
|
toUnset.mapNotNull {
|
||||||
|
if (deleteWhere { selectById(it) } > 0) {
|
||||||
|
it
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.forEach {
|
||||||
|
_onValueRemoved.emit(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun unsetWithValues(toUnset: List<Value>) {
|
||||||
|
transaction(database) {
|
||||||
|
toUnset.flatMap {
|
||||||
|
val keys = select { selectByValue(it) }.mapNotNull { it.asKey }
|
||||||
|
deleteWhere { selectByIds(keys) }
|
||||||
|
keys
|
||||||
|
}
|
||||||
|
}.distinct().forEach {
|
||||||
|
_onValueRemoved.emit(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.exposed.keyvalue
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.pagination.*
|
||||||
|
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
|
||||||
|
import dev.inmo.micro_utils.repos.exposed.*
|
||||||
|
import dev.inmo.micro_utils.repos.exposed.utils.selectPaginated
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|
||||||
|
abstract class AbstractExposedReadKeyValueRepo<Key, Value>(
|
||||||
|
override val database: Database,
|
||||||
|
tableName: String? = null
|
||||||
|
) : ReadKeyValueRepo<Key, Value>,
|
||||||
|
CommonExposedRepo<Key, Value>,
|
||||||
|
Table(tableName ?: "") {
|
||||||
|
abstract val keyColumn: Column<*>
|
||||||
|
abstract val ResultRow.asKey: Key
|
||||||
|
abstract val selectByValue: SqlExpressionBuilder.(Value) -> Op<Boolean>
|
||||||
|
|
||||||
|
override suspend fun get(k: Key): Value? = transaction(database) {
|
||||||
|
select { selectById(k) }.limit(1).firstOrNull() ?.asObject
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun contains(key: Key): Boolean = transaction(database) {
|
||||||
|
select { selectById(key) }.limit(1).any()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun count(): Long = transaction(database) { selectAll().count() }
|
||||||
|
|
||||||
|
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(database) {
|
||||||
|
selectAll().selectPaginated(
|
||||||
|
pagination,
|
||||||
|
keyColumn,
|
||||||
|
reversed
|
||||||
|
) {
|
||||||
|
it.asKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun keys(v: Value, pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(database) {
|
||||||
|
select { selectByValue(v) }.selectPaginated(
|
||||||
|
pagination,
|
||||||
|
keyColumn,
|
||||||
|
reversed
|
||||||
|
) {
|
||||||
|
it.asKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<Value> = transaction(database) {
|
||||||
|
selectAll().selectPaginated(
|
||||||
|
pagination,
|
||||||
|
keyColumn,
|
||||||
|
reversed
|
||||||
|
) {
|
||||||
|
it.asObject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.exposed.onetomany
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.repos.KeyValuesRepo
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import org.jetbrains.exposed.sql.statements.InsertStatement
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|
||||||
|
abstract class AbstractExposedKeyValuesRepo<Key, Value>(
|
||||||
|
override val database: Database,
|
||||||
|
tableName: String? = null
|
||||||
|
) : KeyValuesRepo<Key, Value>, AbstractExposedReadKeyValuesRepo<Key, Value>(
|
||||||
|
database,
|
||||||
|
tableName
|
||||||
|
) {
|
||||||
|
protected val _onNewValue: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
|
||||||
|
override val onNewValue: Flow<Pair<Key, Value>>
|
||||||
|
get() = _onNewValue.asSharedFlow()
|
||||||
|
protected val _onValueRemoved: MutableSharedFlow<Pair<Key, Value>> = MutableSharedFlow()
|
||||||
|
override val onValueRemoved: Flow<Pair<Key, Value>>
|
||||||
|
get() = _onValueRemoved.asSharedFlow()
|
||||||
|
protected val _onDataCleared: MutableSharedFlow<Key> = MutableSharedFlow()
|
||||||
|
override val onDataCleared: Flow<Key>
|
||||||
|
get() = _onDataCleared.asSharedFlow()
|
||||||
|
|
||||||
|
protected abstract fun insert(k: Key, v: Value, it: InsertStatement<Number>)
|
||||||
|
|
||||||
|
override suspend fun add(toAdd: Map<Key, List<Value>>) {
|
||||||
|
transaction(database) {
|
||||||
|
toAdd.keys.flatMap { k ->
|
||||||
|
toAdd[k] ?.mapNotNull { v ->
|
||||||
|
if (select { selectById(k).and(selectByValue(v)) }.limit(1).any()) {
|
||||||
|
return@mapNotNull null
|
||||||
|
}
|
||||||
|
val insertResult = insert {
|
||||||
|
insert(k, v, it)
|
||||||
|
}
|
||||||
|
if (insertResult.insertedCount > 0) {
|
||||||
|
k to v
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
}.forEach { _onNewValue.emit(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
|
||||||
|
transaction(database) {
|
||||||
|
toRemove.keys.flatMap { k ->
|
||||||
|
toRemove[k] ?.mapNotNull { v ->
|
||||||
|
if (deleteWhere { selectById(k).and(selectByValue(v)) } > 0 ) {
|
||||||
|
k to v
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
} ?: emptyList()
|
||||||
|
}
|
||||||
|
}.forEach {
|
||||||
|
_onValueRemoved.emit(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun clear(k: Key) {
|
||||||
|
transaction(database) {
|
||||||
|
deleteWhere { selectById(k) }
|
||||||
|
}.also { _onDataCleared.emit(k) }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.exposed.onetomany
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.pagination.*
|
||||||
|
import dev.inmo.micro_utils.repos.ReadKeyValueRepo
|
||||||
|
import dev.inmo.micro_utils.repos.ReadKeyValuesRepo
|
||||||
|
import dev.inmo.micro_utils.repos.exposed.*
|
||||||
|
import dev.inmo.micro_utils.repos.exposed.utils.selectPaginated
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
|
||||||
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|
||||||
|
abstract class AbstractExposedReadKeyValuesRepo<Key, Value>(
|
||||||
|
override val database: Database,
|
||||||
|
tableName: String? = null
|
||||||
|
) : ReadKeyValuesRepo<Key, Value>,
|
||||||
|
CommonExposedRepo<Key, Value>,
|
||||||
|
Table(tableName ?: "") {
|
||||||
|
abstract val keyColumn: Column<*>
|
||||||
|
abstract val ResultRow.asKey: Key
|
||||||
|
abstract val selectByValue: SqlExpressionBuilder.(Value) -> Op<Boolean>
|
||||||
|
|
||||||
|
override suspend fun count(k: Key): Long = transaction(database) { select { selectById(k) }.count() }
|
||||||
|
|
||||||
|
override suspend fun count(): Long = transaction(database) { selectAll().count() }
|
||||||
|
|
||||||
|
override suspend fun get(
|
||||||
|
k: Key,
|
||||||
|
pagination: Pagination,
|
||||||
|
reversed: Boolean
|
||||||
|
): PaginationResult<Value> = transaction(database) {
|
||||||
|
select { selectById(k) }.selectPaginated(
|
||||||
|
pagination,
|
||||||
|
keyColumn,
|
||||||
|
reversed
|
||||||
|
) {
|
||||||
|
it.asObject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun keys(
|
||||||
|
pagination: Pagination,
|
||||||
|
reversed: Boolean
|
||||||
|
): PaginationResult<Key> = transaction(database) {
|
||||||
|
selectAll().selectPaginated(
|
||||||
|
pagination,
|
||||||
|
keyColumn,
|
||||||
|
reversed
|
||||||
|
) {
|
||||||
|
it.asKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun keys(
|
||||||
|
v: Value,
|
||||||
|
pagination: Pagination,
|
||||||
|
reversed: Boolean
|
||||||
|
): PaginationResult<Key> = transaction(database) {
|
||||||
|
select { selectByValue(v) }.selectPaginated(
|
||||||
|
pagination,
|
||||||
|
keyColumn,
|
||||||
|
reversed
|
||||||
|
) {
|
||||||
|
it.asKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun contains(k: Key): Boolean = transaction(database) {
|
||||||
|
select { selectById(k) }.limit(1).any()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun contains(k: Key, v: Value): Boolean = transaction(database) {
|
||||||
|
select { selectById(k).and(selectByValue(v)) }.limit(1).any()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package dev.inmo.micro_utils.repos.exposed.utils
|
||||||
|
|
||||||
|
import dev.inmo.micro_utils.pagination.*
|
||||||
|
import org.jetbrains.exposed.sql.*
|
||||||
|
|
||||||
|
fun <T> Query.selectPaginated(
|
||||||
|
pagination: Pagination,
|
||||||
|
orderBy: Pair<Expression<*>, SortOrder>? = null,
|
||||||
|
createResult: (ResultRow) -> T
|
||||||
|
): PaginationResult<T> {
|
||||||
|
val count = count()
|
||||||
|
val list = paginate(pagination, orderBy).map(createResult)
|
||||||
|
return list.createPaginationResult(pagination, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> Query.selectPaginated(
|
||||||
|
pagination: Pagination,
|
||||||
|
orderBy: Expression<*>?,
|
||||||
|
reversed: Boolean = false,
|
||||||
|
createResult: (ResultRow) -> T
|
||||||
|
) = selectPaginated(pagination, orderBy ?.let { it to if (reversed) SortOrder.DESC else SortOrder.ASC }, createResult)
|
Loading…
Reference in New Issue
Block a user