From 5f231c22129f6c2de92c0541202f1e894a77e28e Mon Sep 17 00:00:00 2001 From: InsanusMokrassar Date: Tue, 25 Apr 2023 19:14:38 +0600 Subject: [PATCH] ReadKeyValuesRepo#removeWithValue --- CHANGELOG.md | 2 ++ .../AutoRecacheWriteKeyValuesRepo.kt | 16 +++++++++- .../cache/full/FullKeyValuesCacheRepo.kt | 4 +++ .../inmo/micro_utils/repos/KeyValuesRepo.kt | 30 +++++++++++++++++++ .../repos/mappers/OneToManyKeyValueMappers.kt | 2 ++ .../kvs/KeyValuesFromKeyValueRepo.kt | 19 ++++++++++++ .../repos/onetomany/OneToManyAndroidRepo.kt | 14 +++++++++ .../exposed/onetomany/ExposedKeyValuesRepo.kt | 28 +++++++++++++++++ .../micro_utils/repos/MapKeyValuesRepo.kt | 18 +++++++++-- .../values/KtorWriteKeyValuesRepoClient.kt | 11 +++++++ .../common/one_to_many/OneToManyRoutes.kt | 1 + .../values/KtorWriteKeyValuesRepoRoutes.kt | 5 ++++ 12 files changed, 147 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60a3ef5c9c2..9ff14ecf7e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ * `Server`: * Now it is possible to take query parameters as list * `Repos`: + * `Common`: + * New `WriteKeyValuesRepo.removeWithValue` * `Cache`: * Rename full caching factories functions to `fullyCached` diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/fallback/keyvalues/AutoRecacheWriteKeyValuesRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/fallback/keyvalues/AutoRecacheWriteKeyValuesRepo.kt index 73cf0551472..86ca7c71c85 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/fallback/keyvalues/AutoRecacheWriteKeyValuesRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/fallback/keyvalues/AutoRecacheWriteKeyValuesRepo.kt @@ -7,6 +7,7 @@ import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging import dev.inmo.micro_utils.repos.WriteKeyValuesRepo import dev.inmo.micro_utils.repos.cache.cache.FullKVCache import dev.inmo.micro_utils.repos.cache.FallbackCacheRepo +import dev.inmo.micro_utils.repos.pagination.maxPagePagination import dev.inmo.micro_utils.repos.set import dev.inmo.micro_utils.repos.unset import kotlinx.coroutines.CoroutineScope @@ -50,7 +51,7 @@ open class AutoRecacheWriteKeyValuesRepo( override suspend fun clearWithValue(v: RegisteredObject) { originalRepo.clearWithValue(v) - doForAllWithNextPaging(FirstPagePagination(kvCache.count().takeIf { it < Int.MAX_VALUE } ?.toInt() ?: Int.MAX_VALUE)) { + doForAllWithNextPaging(kvCache.maxPagePagination()) { kvCache.keys(it).also { it.results.forEach { id -> kvCache.get(id) ?.takeIf { it.contains(v) } ?.let { @@ -73,6 +74,19 @@ open class AutoRecacheWriteKeyValuesRepo( } } + override suspend fun removeWithValue(v: RegisteredObject) { + originalRepo.removeWithValue(v) + doForAllWithNextPaging(kvCache.maxPagePagination()) { + kvCache.keys(it).also { + it.results.forEach { id -> + kvCache.get(id) ?.takeIf { it.contains(v) } ?.let { + kvCache.set(id, it - v) + } + } + } + } + } + override suspend fun add(toAdd: Map>) { originalRepo.add(toAdd) toAdd.forEach { (k, v) -> diff --git a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt index c2ecb6679d3..0946cb5c8fe 100644 --- a/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt +++ b/repos/cache/src/commonMain/kotlin/dev/inmo/micro_utils/repos/cache/full/FullKeyValuesCacheRepo.kt @@ -157,6 +157,10 @@ open class FullKeyValuesCacheRepo( override suspend fun invalidate() { kvCache.actualizeAll(parentRepo) } + + override suspend fun removeWithValue(v: Value) { + super.removeWithValue(v) + } } fun KeyValuesRepo.fullyCached( diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValuesRepo.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValuesRepo.kt index 1754abb70b8..57e8a6331c9 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValuesRepo.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/KeyValuesRepo.kt @@ -1,6 +1,7 @@ package dev.inmo.micro_utils.repos import dev.inmo.micro_utils.pagination.* +import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging import kotlinx.coroutines.flow.Flow @@ -44,9 +45,23 @@ interface WriteKeyValuesRepo : Repo { suspend fun add(toAdd: Map>) + /** + * Removes [Value]s by passed [Key]s without full clear of all data by [Key] + */ suspend fun remove(toRemove: Map>) + /** + * Removes [v] without full clear of all data by [Key]s with [v] + */ + suspend fun removeWithValue(v: Value) + + /** + * Fully clear all data by [k] + */ suspend fun clear(k: Key) + /** + * Clear [v] **with** full clear of all data by [Key]s with [v] + */ suspend fun clearWithValue(v: Value) suspend fun set(toSet: Map>) { @@ -100,6 +115,21 @@ interface KeyValuesRepo : ReadKeyValuesRepo, WriteKeyVal keysResult.currentPageIfNotEmpty() } } + suspend override fun removeWithValue(v: Value) { + val toRemove = mutableMapOf>() + + doForAllWithNextPaging { + keys(it).also { + it.results.forEach { + if (contains(it, v)) { + toRemove[it] = listOf(v) + } + } + } + } + + remove(toRemove) + } } typealias OneToManyKeyValueRepo = KeyValuesRepo diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/mappers/OneToManyKeyValueMappers.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/mappers/OneToManyKeyValueMappers.kt index 93b165e4c58..fbedd977906 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/mappers/OneToManyKeyValueMappers.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/mappers/OneToManyKeyValueMappers.kt @@ -97,6 +97,8 @@ open class MapperWriteKeyValuesRepo( }.toMap() ) + override suspend fun removeWithValue(v: FromValue) = to.removeWithValue(v.toOutValue()) + override suspend fun set(toSet: Map>) { to.set( toSet.map { (k, vs) -> k.toOutKey() to vs.map { v -> v.toOutValue() } }.toMap() diff --git a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/transforms/kvs/KeyValuesFromKeyValueRepo.kt b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/transforms/kvs/KeyValuesFromKeyValueRepo.kt index 7473599f14f..8248959b704 100644 --- a/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/transforms/kvs/KeyValuesFromKeyValueRepo.kt +++ b/repos/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/transforms/kvs/KeyValuesFromKeyValueRepo.kt @@ -4,6 +4,7 @@ import dev.inmo.micro_utils.pagination.FirstPagePagination import dev.inmo.micro_utils.pagination.utils.doForAllWithNextPaging import dev.inmo.micro_utils.repos.KeyValueRepo import dev.inmo.micro_utils.repos.KeyValuesRepo +import dev.inmo.micro_utils.repos.set import dev.inmo.micro_utils.repos.unset import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -60,6 +61,24 @@ open class KeyValuesFromKeyValueRepo>() + + doForAllWithNextPaging { + original.keys(it).also { + it.results.forEach { + val data = original.get(it) ?: return@forEach + + if (v in data) { + toRemove[it] = listOf(v) + } + } + } + } + + remove(toRemove) + } + override suspend fun add(toAdd: Map>) { original.set( toAdd.mapNotNull { (k, adding) -> diff --git a/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/onetomany/OneToManyAndroidRepo.kt b/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/onetomany/OneToManyAndroidRepo.kt index 11a29614c39..b923563760b 100644 --- a/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/onetomany/OneToManyAndroidRepo.kt +++ b/repos/common/src/main/kotlin/dev/inmo/micro_utils/repos/onetomany/OneToManyAndroidRepo.kt @@ -6,6 +6,7 @@ import dev.inmo.micro_utils.common.mapNotNullA import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.utils.reverse import dev.inmo.micro_utils.repos.* +import dev.inmo.micro_utils.repos.crud.asId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow @@ -260,6 +261,19 @@ class OneToManyAndroidRepo( _onValueRemoved.emit(k to v) } } + + override suspend fun removeWithValue(v: Value) { + helper.blockingWritableTransaction { + val keys = select(tableName, idColumnArray, "$valueColumnName=?", arrayOf(v.valueAsString())).map { + it.asId.keyFromString() + } + keys.filter { + delete(tableName, "$idColumnName=? AND $valueColumnName=?", arrayOf(it.keyAsString(), v.valueAsString())) > 0 + } + }.forEach { k -> + _onValueRemoved.emit(k to v) + } + } } fun OneToManyAndroidRepo( diff --git a/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/onetomany/ExposedKeyValuesRepo.kt b/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/onetomany/ExposedKeyValuesRepo.kt index 9b79231c8bc..456f2c3106d 100644 --- a/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/onetomany/ExposedKeyValuesRepo.kt +++ b/repos/exposed/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/exposed/onetomany/ExposedKeyValuesRepo.kt @@ -5,6 +5,7 @@ import dev.inmo.micro_utils.repos.exposed.ColumnAllocator import kotlinx.coroutines.flow.* import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq +import org.jetbrains.exposed.sql.SqlExpressionBuilder.inList import org.jetbrains.exposed.sql.transactions.transaction typealias ExposedOneToManyKeyValueRepo1 = ExposedKeyValuesRepo @@ -66,9 +67,36 @@ open class ExposedKeyValuesRepo( } } + override suspend fun removeWithValue(v: Value) { + transaction(database) { + val keys = select { selectByValue(v) }.map { it.asKey } + deleteWhere { SqlExpressionBuilder.selectByValue(v) } + keys + }.forEach { + _onValueRemoved.emit(it to v) + } + } + override suspend fun clear(k: Key) { transaction(database) { deleteWhere { keyColumn.eq(k) } }.also { _onDataCleared.emit(k) } } + + override suspend fun clearWithValue(v: Value) { + transaction(database) { + val toClear = select { selectByValue(v) } + .asSequence() + .map { it.asKey to it.asObject } + .groupBy { it.first } + .mapValues { it.value.map { it.second } } + deleteWhere { keyColumn.inList(toClear.keys) } + toClear + }.forEach { + it.value.forEach { v -> + _onValueRemoved.emit(it.key to v) + } + _onDataCleared.emit(it.key) + } + } } diff --git a/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapKeyValuesRepo.kt b/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapKeyValuesRepo.kt index cfe8dff41e8..48acbabd108 100644 --- a/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapKeyValuesRepo.kt +++ b/repos/inmemory/src/commonMain/kotlin/dev/inmo/micro_utils/repos/MapKeyValuesRepo.kt @@ -87,13 +87,27 @@ class MapWriteKeyValuesRepo( } } + override suspend fun removeWithValue(v: Value) { + map.forEach { (k, values) -> + if (values.remove(v)) { + _onValueRemoved.emit(k to v) + } + } + } + override suspend fun clear(k: Key) { map.remove(k) ?.also { _onDataCleared.emit(k) } } override suspend fun clearWithValue(v: Value) { - map.forEach { (k, values) -> - if (values.remove(v)) _onValueRemoved.emit(k to v) + map.filter { (_, values) -> + values.contains(v) + }.forEach { + map.remove(it.key) ?.onEach { v -> + _onValueRemoved.emit(it.key to v) + } ?.also { _ -> + _onDataCleared.emit(it.key) + } } } } diff --git a/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/key/values/KtorWriteKeyValuesRepoClient.kt b/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/key/values/KtorWriteKeyValuesRepoClient.kt index 37f14d66b49..5fe28f0a7af 100644 --- a/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/key/values/KtorWriteKeyValuesRepoClient.kt +++ b/repos/ktor/client/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/client/key/values/KtorWriteKeyValuesRepoClient.kt @@ -47,6 +47,17 @@ class KtorWriteKeyValuesRepoClient( }.throwOnUnsuccess { "Unable to remove $toRemove" } } + @OptIn(InternalAPI::class) + override suspend fun removeWithValue(v: Value) { + httpClient.post( + buildStandardUrl(baseUrl, removeWithValueRoute) + ) { + body = v + bodyType = valueTypeInfo + contentType(contentType) + }.throwOnUnsuccess { "Unable to remove $v" } + } + @OptIn(InternalAPI::class) override suspend fun clear(k: Key) { httpClient.post( diff --git a/repos/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/common/one_to_many/OneToManyRoutes.kt b/repos/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/common/one_to_many/OneToManyRoutes.kt index 3bd568bb3a4..00b895d6cbe 100644 --- a/repos/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/common/one_to_many/OneToManyRoutes.kt +++ b/repos/ktor/common/src/commonMain/kotlin/dev/inmo/micro_utils/repos/ktor/common/one_to_many/OneToManyRoutes.kt @@ -13,6 +13,7 @@ const val onDataClearedRoute = "onDataCleared" const val addRoute = "add" const val removeRoute = "remove" +const val removeWithValueRoute = "removeWithValue" const val clearRoute = "clear" const val clearWithValueRoute = "clearWithValue" const val setRoute = "set" diff --git a/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/key/values/KtorWriteKeyValuesRepoRoutes.kt b/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/key/values/KtorWriteKeyValuesRepoRoutes.kt index 0a37c28f67c..3f78ca0a7be 100644 --- a/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/key/values/KtorWriteKeyValuesRepoRoutes.kt +++ b/repos/ktor/server/src/jvmMain/kotlin/dev/inmo/micro_utils/repos/ktor/server/key/values/KtorWriteKeyValuesRepoRoutes.kt @@ -46,6 +46,11 @@ inline fun Route.configureWriteKeyValue call.respond(HttpStatusCode.OK) } + post(removeWithValueRoute) { + originalRepo.removeWithValue(call.receive()) + call.respond(HttpStatusCode.OK) + } + post(clearRoute) { originalRepo.clear(call.receive()) call.respond(HttpStatusCode.OK)