Creating interfaces for multiple EventStores
This commit is contained in:
@@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Vitor Pamplona
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||||
|
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
package com.vitorpamplona.quartz.nip01Core.store
|
||||||
|
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||||
|
|
||||||
|
interface IEventStore {
|
||||||
|
fun insert(event: Event): Boolean
|
||||||
|
|
||||||
|
interface ITransaction {
|
||||||
|
fun insert(event: Event): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
fun transaction(body: ITransaction.() -> Unit)
|
||||||
|
|
||||||
|
fun <T : Event> query(filter: Filter): List<T>
|
||||||
|
|
||||||
|
fun <T : Event> query(filters: List<Filter>): List<T>
|
||||||
|
|
||||||
|
fun <T : Event> query(
|
||||||
|
filter: Filter,
|
||||||
|
onEach: (T) -> Unit,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun <T : Event> query(
|
||||||
|
filters: List<Filter>,
|
||||||
|
onEach: (T) -> Unit,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun count(filter: Filter): Int
|
||||||
|
|
||||||
|
fun count(filters: List<Filter>): Int
|
||||||
|
|
||||||
|
fun delete(filter: Filter)
|
||||||
|
|
||||||
|
fun delete(filters: List<Filter>)
|
||||||
|
|
||||||
|
fun deleteExpiredEvents()
|
||||||
|
|
||||||
|
fun close()
|
||||||
|
}
|
||||||
+22
-22
@@ -175,10 +175,10 @@ class EventIndexesModule(
|
|||||||
return makeQueryIn(rowIdSubQuery.sql)
|
return makeQueryIn(rowIdSubQuery.sql)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun query(
|
fun <T : Event> query(
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
db: SQLiteDatabase,
|
db: SQLiteDatabase,
|
||||||
): List<Event> {
|
): List<T> {
|
||||||
val rowIdSubQuery = prepareRowIDSubQueries(filter)
|
val rowIdSubQuery = prepareRowIDSubQueries(filter)
|
||||||
|
|
||||||
return if (rowIdSubQuery == null) {
|
return if (rowIdSubQuery == null) {
|
||||||
@@ -188,10 +188,10 @@ class EventIndexesModule(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun query(
|
fun <T : Event> query(
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
db: SQLiteDatabase,
|
db: SQLiteDatabase,
|
||||||
onEach: (Event) -> Unit,
|
onEach: (T) -> Unit,
|
||||||
) {
|
) {
|
||||||
val rowIdSubQuery = prepareRowIDSubQueries(filter) ?: return db.runQueryEmitting(makeEverythingQuery(), onEach = onEach)
|
val rowIdSubQuery = prepareRowIDSubQueries(filter) ?: return db.runQueryEmitting(makeEverythingQuery(), onEach = onEach)
|
||||||
|
|
||||||
@@ -205,10 +205,10 @@ class EventIndexesModule(
|
|||||||
return makeQueryIn(unions)
|
return makeQueryIn(unions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun query(
|
fun <T : Event> query(
|
||||||
filters: List<Filter>,
|
filters: List<Filter>,
|
||||||
db: SQLiteDatabase,
|
db: SQLiteDatabase,
|
||||||
): List<Event> {
|
): List<T> {
|
||||||
val rowIdSubQueries = filters.mapNotNull { prepareRowIDSubQueries(it) }
|
val rowIdSubQueries = filters.mapNotNull { prepareRowIDSubQueries(it) }
|
||||||
|
|
||||||
if (rowIdSubQueries.isEmpty()) return db.runQuery(makeEverythingQuery())
|
if (rowIdSubQueries.isEmpty()) return db.runQuery(makeEverythingQuery())
|
||||||
@@ -219,10 +219,10 @@ class EventIndexesModule(
|
|||||||
return db.runQuery(makeQueryIn(unions), args)
|
return db.runQuery(makeQueryIn(unions), args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun query(
|
fun <T : Event> query(
|
||||||
filters: List<Filter>,
|
filters: List<Filter>,
|
||||||
db: SQLiteDatabase,
|
db: SQLiteDatabase,
|
||||||
onEach: (Event) -> Unit,
|
onEach: (T) -> Unit,
|
||||||
) {
|
) {
|
||||||
val rowIdSubQueries = filters.mapNotNull { prepareRowIDSubQueries(it) }
|
val rowIdSubQueries = filters.mapNotNull { prepareRowIDSubQueries(it) }
|
||||||
|
|
||||||
@@ -244,20 +244,20 @@ class EventIndexesModule(
|
|||||||
ORDER BY created_at DESC, id
|
ORDER BY created_at DESC, id
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
private fun SQLiteDatabase.runQuery(
|
private fun <T : Event> SQLiteDatabase.runQuery(
|
||||||
sql: String,
|
sql: String,
|
||||||
args: List<String> = emptyList(),
|
args: List<String> = emptyList(),
|
||||||
): List<Event> =
|
): List<T> =
|
||||||
rawQuery(sql, args.toTypedArray()).use { cursor ->
|
rawQuery(sql, args.toTypedArray()).use { cursor ->
|
||||||
parseResults(cursor)
|
parseResults(cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseResults(cursor: Cursor): List<Event> {
|
private fun <T : Event> parseResults(cursor: Cursor): List<T> {
|
||||||
val events = ArrayList<Event>()
|
val events = ArrayList<T>()
|
||||||
|
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
events.add(
|
events.add(
|
||||||
EventFactory.create(
|
EventFactory.create<T>(
|
||||||
cursor.getString(0).intern(),
|
cursor.getString(0).intern(),
|
||||||
cursor.getString(1).intern(),
|
cursor.getString(1).intern(),
|
||||||
cursor.getLong(2),
|
cursor.getLong(2),
|
||||||
@@ -272,21 +272,21 @@ class EventIndexesModule(
|
|||||||
return events
|
return events
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SQLiteDatabase.runQueryEmitting(
|
private fun <T : Event> SQLiteDatabase.runQueryEmitting(
|
||||||
sql: String,
|
sql: String,
|
||||||
args: List<String> = emptyList(),
|
args: List<String> = emptyList(),
|
||||||
onEach: (Event) -> Unit,
|
onEach: (T) -> Unit,
|
||||||
) = rawQuery(sql, args.toTypedArray()).use { cursor ->
|
) = rawQuery(sql, args.toTypedArray()).use { cursor ->
|
||||||
emitResults(cursor, onEach)
|
emitResults(cursor, onEach)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun emitResults(
|
private fun <T : Event> emitResults(
|
||||||
cursor: Cursor,
|
cursor: Cursor,
|
||||||
onEach: (Event) -> Unit,
|
onEach: (T) -> Unit,
|
||||||
) {
|
) {
|
||||||
while (cursor.moveToNext()) {
|
while (cursor.moveToNext()) {
|
||||||
onEach(
|
onEach(
|
||||||
EventFactory.create(
|
EventFactory.create<T>(
|
||||||
cursor.getString(0).intern(),
|
cursor.getString(0).intern(),
|
||||||
cursor.getString(1).intern(),
|
cursor.getString(1).intern(),
|
||||||
cursor.getLong(2),
|
cursor.getLong(2),
|
||||||
@@ -347,18 +347,18 @@ class EventIndexesModule(
|
|||||||
fun delete(
|
fun delete(
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
db: SQLiteDatabase,
|
db: SQLiteDatabase,
|
||||||
): Int? {
|
): Int {
|
||||||
val rowIdQuery = prepareRowIDSubQueries(filter) ?: return null
|
val rowIdQuery = prepareRowIDSubQueries(filter) ?: return 0
|
||||||
return db.runDelete(rowIdQuery.sql, rowIdQuery.args)
|
return db.runDelete(rowIdQuery.sql, rowIdQuery.args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun delete(
|
fun delete(
|
||||||
filters: List<Filter>,
|
filters: List<Filter>,
|
||||||
db: SQLiteDatabase,
|
db: SQLiteDatabase,
|
||||||
): Int? {
|
): Int {
|
||||||
val rowIdSubqueries = filters.mapNotNull { prepareRowIDSubQueries(it) }
|
val rowIdSubqueries = filters.mapNotNull { prepareRowIDSubQueries(it) }
|
||||||
|
|
||||||
if (rowIdSubqueries.isEmpty()) return null
|
if (rowIdSubqueries.isEmpty()) return 0
|
||||||
|
|
||||||
val unions = rowIdSubqueries.joinToString(" UNION ") { it.sql }
|
val unions = rowIdSubqueries.joinToString(" UNION ") { it.sql }
|
||||||
val args = rowIdSubqueries.flatMap { it.args }
|
val args = rowIdSubqueries.flatMap { it.args }
|
||||||
|
|||||||
+16
-19
@@ -23,6 +23,7 @@ package com.vitorpamplona.quartz.nip01Core.store.sqlite
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.Event
|
import com.vitorpamplona.quartz.nip01Core.core.Event
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.store.IEventStore
|
||||||
import com.vitorpamplona.quartz.nip01Core.store.sqlite.EventIndexesModule.IndexingStrategy
|
import com.vitorpamplona.quartz.nip01Core.store.sqlite.EventIndexesModule.IndexingStrategy
|
||||||
|
|
||||||
class EventStore(
|
class EventStore(
|
||||||
@@ -30,40 +31,36 @@ class EventStore(
|
|||||||
dbName: String? = "events.db",
|
dbName: String? = "events.db",
|
||||||
val relayUrl: String? = "wss://quartz.local",
|
val relayUrl: String? = "wss://quartz.local",
|
||||||
val tagIndexStrategy: IndexingStrategy = IndexingStrategy(),
|
val tagIndexStrategy: IndexingStrategy = IndexingStrategy(),
|
||||||
) {
|
) : IEventStore {
|
||||||
val store = SQLiteEventStore(context, dbName, relayUrl, tagIndexStrategy)
|
val store = SQLiteEventStore(context, dbName, relayUrl, tagIndexStrategy)
|
||||||
|
|
||||||
fun insert(event: Event) = store.insertEvent(event)
|
override fun insert(event: Event) = store.insertEvent(event)
|
||||||
|
|
||||||
interface ITransaction {
|
override fun transaction(body: IEventStore.ITransaction.() -> Unit) = store.transaction(body)
|
||||||
fun insert(event: Event): Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
fun transaction(body: ITransaction.() -> Unit) = store.transaction(body)
|
override fun <T : Event> query(filter: Filter) = store.query<T>(filter)
|
||||||
|
|
||||||
fun query(filter: Filter) = store.query(filter)
|
override fun <T : Event> query(filters: List<Filter>) = store.query<T>(filters)
|
||||||
|
|
||||||
fun query(filters: List<Filter>) = store.query(filters)
|
override fun <T : Event> query(
|
||||||
|
|
||||||
fun query(
|
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
onEach: (Event) -> Unit,
|
onEach: (T) -> Unit,
|
||||||
) = store.query(filter, onEach)
|
) = store.query(filter, onEach)
|
||||||
|
|
||||||
fun query(
|
override fun <T : Event> query(
|
||||||
filters: List<Filter>,
|
filters: List<Filter>,
|
||||||
onEach: (Event) -> Unit,
|
onEach: (T) -> Unit,
|
||||||
) = store.query(filters, onEach)
|
) = store.query(filters, onEach)
|
||||||
|
|
||||||
fun count(filter: Filter) = store.count(filter)
|
override fun count(filter: Filter) = store.count(filter)
|
||||||
|
|
||||||
fun count(filters: List<Filter>) = store.count(filters)
|
override fun count(filters: List<Filter>) = store.count(filters)
|
||||||
|
|
||||||
fun delete(filter: Filter) = store.delete(filter)
|
override fun delete(filter: Filter) = store.delete(filter)
|
||||||
|
|
||||||
fun delete(filters: List<Filter>) = store.delete(filters)
|
override fun delete(filters: List<Filter>) = store.delete(filters)
|
||||||
|
|
||||||
fun deleteExpiredEvents() = store.deleteExpiredEvents()
|
override fun deleteExpiredEvents() = store.deleteExpiredEvents()
|
||||||
|
|
||||||
fun close() = store.close()
|
override fun close() = store.close()
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-9
@@ -29,6 +29,7 @@ import com.vitorpamplona.quartz.nip01Core.core.Event
|
|||||||
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
import com.vitorpamplona.quartz.nip01Core.core.HexKey
|
||||||
import com.vitorpamplona.quartz.nip01Core.core.isEphemeral
|
import com.vitorpamplona.quartz.nip01Core.core.isEphemeral
|
||||||
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter
|
||||||
|
import com.vitorpamplona.quartz.nip01Core.store.IEventStore
|
||||||
import com.vitorpamplona.quartz.nip01Core.store.sqlite.EventIndexesModule.IndexingStrategy
|
import com.vitorpamplona.quartz.nip01Core.store.sqlite.EventIndexesModule.IndexingStrategy
|
||||||
import com.vitorpamplona.quartz.nip40Expiration.isExpired
|
import com.vitorpamplona.quartz.nip40Expiration.isExpired
|
||||||
|
|
||||||
@@ -117,7 +118,7 @@ class SQLiteEventStore(
|
|||||||
|
|
||||||
inner class Transaction(
|
inner class Transaction(
|
||||||
val db: SQLiteDatabase,
|
val db: SQLiteDatabase,
|
||||||
) : EventStore.ITransaction {
|
) : IEventStore.ITransaction {
|
||||||
override fun insert(event: Event): Boolean {
|
override fun insert(event: Event): Boolean {
|
||||||
if (event.isExpired()) throw SQLiteConstraintException("blocked: Cannot insert an expired event")
|
if (event.isExpired()) throw SQLiteConstraintException("blocked: Cannot insert an expired event")
|
||||||
if (event.kind.isEphemeral()) return false
|
if (event.kind.isEphemeral()) return false
|
||||||
@@ -139,27 +140,31 @@ class SQLiteEventStore(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun query(filter: Filter): List<Event> = eventIndexModule.query(filter, readableDatabase)
|
fun <T : Event> query(filter: Filter): List<T> = eventIndexModule.query(filter, readableDatabase)
|
||||||
|
|
||||||
fun query(filters: List<Filter>): List<Event> = eventIndexModule.query(filters, readableDatabase)
|
fun <T : Event> query(filters: List<Filter>): List<T> = eventIndexModule.query(filters, readableDatabase)
|
||||||
|
|
||||||
fun query(
|
fun <T : Event> query(
|
||||||
filter: Filter,
|
filter: Filter,
|
||||||
onEach: (Event) -> Unit,
|
onEach: (T) -> Unit,
|
||||||
) = eventIndexModule.query(filter, readableDatabase, onEach)
|
) = eventIndexModule.query(filter, readableDatabase, onEach)
|
||||||
|
|
||||||
fun query(
|
fun <T : Event> query(
|
||||||
filters: List<Filter>,
|
filters: List<Filter>,
|
||||||
onEach: (Event) -> Unit,
|
onEach: (T) -> Unit,
|
||||||
) = eventIndexModule.query(filters, readableDatabase, onEach)
|
) = eventIndexModule.query(filters, readableDatabase, onEach)
|
||||||
|
|
||||||
fun count(filter: Filter): Int = eventIndexModule.count(filter, readableDatabase)
|
fun count(filter: Filter): Int = eventIndexModule.count(filter, readableDatabase)
|
||||||
|
|
||||||
fun count(filters: List<Filter>): Int = eventIndexModule.count(filters, readableDatabase)
|
fun count(filters: List<Filter>): Int = eventIndexModule.count(filters, readableDatabase)
|
||||||
|
|
||||||
fun delete(filter: Filter): Int? = eventIndexModule.delete(filter, writableDatabase)
|
fun delete(filter: Filter) {
|
||||||
|
eventIndexModule.delete(filter, writableDatabase)
|
||||||
|
}
|
||||||
|
|
||||||
fun delete(filters: List<Filter>): Int? = eventIndexModule.delete(filters, writableDatabase)
|
fun delete(filters: List<Filter>) {
|
||||||
|
eventIndexModule.delete(filters, writableDatabase)
|
||||||
|
}
|
||||||
|
|
||||||
fun delete(id: HexKey): Int = writableDatabase.delete("event_headers", "id = ?", arrayOf(id))
|
fun delete(id: HexKey): Int = writableDatabase.delete("event_headers", "id = ?", arrayOf(id))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user