HashMapStore.kt
package com.hexagontk.store.hashmap
import com.hexagontk.core.filterNotEmpty
import com.hexagontk.store.Store
import kotlin.reflect.KClass
import kotlin.reflect.KProperty1
class HashMapStore<T : Any, K : Any>(
override val type: KClass<T>,
override val key: KProperty1<T, K>,
override val name: String = type.java.simpleName,
private val store: HashMap<K, Map<String, Any>> = hashMapOf(),
override val encoder: (T) -> Map<String, *>,
override val decoder: (Map<String, *>) -> T,
) : Store<T, K> {
override fun insertOne(instance: T): K {
store[key.get(instance)] = map(instance)
return key.get(instance)
}
override fun insertMany(instances: List<T>): List<K> {
instances.forEach {
store[key.get(it)] = map(it)
}
return instances.map { key.get(it) }
}
override fun saveOne(instance: T): K? {
if (store.containsKey(key.get(instance))) {
store[key.get(instance)] = map(instance)
return null
}
store[key.get(instance)] = map(instance)
@Suppress("UNCHECKED_CAST")
return fromStore(key.get(instance)) as K
}
override fun saveMany(instances: List<T>): List<K?> {
return instances.map(::saveOne)
}
override fun replaceOne(instance: T): Boolean =
store.replace(key.get(instance), map(instance)) != null
override fun replaceMany(instances: List<T>): List<T> =
instances.mapNotNull { if (replaceOne(it)) it else null }
override fun updateOne(key: K, updates: Map<String, *>): Boolean {
if (!store.containsKey(key)) return false
val instance = store[key]!!.toMutableMap()
updates
.filterNotEmpty()
.forEach {
instance[it.key] = toStore(it.value)
}
return store.replace(key, instance) != null
}
override fun updateMany(filter: Map<String, *>, updates: Map<String, *>): Long {
val filteredInstances = store.filter(filter)
return filteredInstances.map { updateOne(it, updates) }.count { it }.toLong()
}
override fun deleteOne(id: K): Boolean =
store.remove(id) != null
override fun deleteMany(filter: Map<String, *>): Long {
val filteredInstances = store.filter(filter)
return filteredInstances.map { deleteOne(it) }.count { it }.toLong()
}
override fun findOne(key: K): T? {
val result = store[key]
return result?.let { fromStore(result) }
}
override fun findOne(key: K, fields: List<String>): Map<String, *>? {
val instance = store[key]
return if (instance == null) null else fields.associateWith { instance[it] }
}
override fun findMany(
filter: Map<String, *>,
limit: Int?,
skip: Int?,
sort: Map<String, Boolean>
): List<T> {
val filteredKeys = store.filter(filter)
val filteredInstances = filteredKeys.map { store[it]!! }
@Suppress("UNCHECKED_CAST")
return filteredInstances
.sort(sort)
.paginate(skip ?: 0, limit ?: filteredKeys.size)
.map { fromStore(it as Map<String, Any>) }
}
override fun findMany(
filter: Map<String, *>,
fields: List<String>,
limit: Int?,
skip: Int?,
sort: Map<String, Boolean>
): List<Map<String, *>> {
val filteredInstances = store.filter(filter)
val result = filteredInstances.mapNotNull { findOne(it, fields) }
return result
.paginate(skip ?: 0, limit ?: result.size)
.sort(sort)
}
override fun count(filter: Map<String, *>): Long =
store.filter(filter).size.toLong()
override fun drop() =
store.clear()
private fun map(instance: T): Map<String, Any> = toStore(instance)
private fun HashMap<K, Map<String, Any>>.filter(filter: Map<String, *>): List<K> =
filter { it.value.containsValues(filter) }
.map { it.key }
private fun Map<String, Any>.containsValues(filter: Map<String, *>): Boolean =
filter.all {
when (val value = it.value) {
is List<*> -> value.contains(this[it.key])
else -> value == this[it.key]
}
}
private fun List<Map<String, *>>.paginate(skip: Int, limit: Int): List<Map<String, *>> =
let {
var endIndex = skip + limit
if (endIndex > this.size) endIndex = this.size
this.subList(skip, endIndex)
}
// TODO: Add sorting functionality (now only sorts by first field)
private fun List<Map<String, *>>.sort(sortFields: Map<String, Boolean>): List<Map<String, *>> =
if (sortFields.isEmpty())
this
else
sortedBy {
val firstSortField = sortFields.entries.first()
val sortingValue = it[firstSortField.key]
@Suppress("UNCHECKED_CAST")
if (sortingValue is Comparable<*>)
sortingValue as? Comparable<Any>
else
error("Not comparable value")
}
private fun toStore(instance: T): Map<String, Any> =
encoder(instance)
.filterNotEmpty()
.mapKeys { it.key.toString() }
.mapValues { it.value }
private fun toStore(value: Any): Any = value
private fun fromStore(map: Map<String, Any>): T =
decoder(map.filterNotEmpty())
private fun fromStore(value: Any): Any =
value
}