diff --git a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/cache/EventCache.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/cache/EventCache.kt index 4edd119a23..59c2a4a764 100644 --- a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/cache/EventCache.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/cache/EventCache.kt @@ -422,7 +422,6 @@ import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import java.util.SortedSet /** * The in-memory event store: every `Note`, `User` and `Channel` the app has consumed, plus the @@ -590,7 +589,7 @@ open class EventCache : } } - fun filter(filter: Filter): SortedSet = filter(filter) { true } + fun filter(filter: Filter): List = filter(filter) { true } /** * Every note matching [filter]'s NIP-01 fields that also satisfies [predicate]. @@ -603,7 +602,7 @@ open class EventCache : fun filter( filter: Filter, predicate: (Note) -> Boolean, - ): SortedSet { + ): List { val byKinds = filter.kinds?.filter { it.isAddressable() || it.isReplaceable() } val addressableMatches = @@ -642,14 +641,20 @@ open class EventCache : } } - val all = (addressableMatches + noteMatches).toSortedSet(CreatedAtIdHexComparator) + // toSet() before sorting: a filter that repeats a kind scans that kind twice, and the + // SortedSet this used to return collapsed the repeats. Note declares no equals(), so a + // plain Set is the same reference-identity de-duplication that comparator gave. + // + // The order is the comparator's, newest first, and it is load-bearing: the napplet + // gateway answers REQs out of this, and NIP-01 has relays return events newest first. + val all = (addressableMatches + noteMatches).toSet().sortedWith(CreatedAtIdHexComparator) val limit = filter.limit ?: return all // Sorted first, then cut. Both halves arrive in hash-walk order, so taking before sorting // dropped whichever matches the walk happened to reach last — the newest ones as often as // not — and a query with 200 addressable matches never showed a single regular note. if (all.size <= limit) return all - return all.asSequence().take(limit).toCollection(sortedSetOf(CreatedAtIdHexComparator)) + return all.take(limit) } fun observeNotes(filter: Filter): Flow> = diff --git a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/observables/EventListMatchingFilter.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/observables/EventListMatchingFilter.kt index c6373a1267..c46ba85e8f 100644 --- a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/observables/EventListMatchingFilter.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/observables/EventListMatchingFilter.kt @@ -26,7 +26,6 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter -import java.util.SortedSet import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentSkipListSet @@ -69,7 +68,7 @@ import java.util.concurrent.ConcurrentSkipListSet */ class EventListMatchingFilter( private val filter: Filter, - private val atOnce: (filter: Filter) -> SortedSet, + private val atOnce: (filter: Filter) -> List, private val update: (List) -> Unit, ) : Observable { /** A note plus the sort key captured at insertion time, so ordering never depends on mutable state. */ diff --git a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/observables/NoteListMatchingFilter.kt b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/observables/NoteListMatchingFilter.kt index d2962053fd..35d395f626 100644 --- a/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/observables/NoteListMatchingFilter.kt +++ b/commons/src/jvmAndroid/kotlin/com/vitorpamplona/amethyst/commons/model/observables/NoteListMatchingFilter.kt @@ -26,7 +26,6 @@ import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.HexKey import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter -import java.util.SortedSet import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentSkipListSet @@ -68,7 +67,7 @@ import java.util.concurrent.ConcurrentSkipListSet */ class NoteListMatchingFilter( private val filter: Filter, - private val atOnce: (filter: Filter) -> SortedSet, + private val atOnce: (filter: Filter) -> List, private val update: (List) -> Unit, ) : Observable { /** A note plus the sort key captured at insertion time, so ordering never depends on mutable state. */ diff --git a/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/observables/EventListMatchingFilterTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/observables/EventListMatchingFilterTest.kt index b28699e362..6e46771881 100644 --- a/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/observables/EventListMatchingFilterTest.kt +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/observables/EventListMatchingFilterTest.kt @@ -27,7 +27,6 @@ import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent import com.vitorpamplona.quartz.utils.EventFactory -import java.util.TreeSet import java.util.concurrent.CountDownLatch import java.util.concurrent.atomic.AtomicReference import kotlin.concurrent.thread @@ -65,7 +64,7 @@ class EventListMatchingFilterTest { sink: (List) -> Unit, ) = EventListMatchingFilter( filter = withFilter, - atOnce = { TreeSet(CreatedAtIdHexComparator) }, + atOnce = { emptyList() }, update = sink, ) diff --git a/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/observables/NoteListMatchingFilterTest.kt b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/observables/NoteListMatchingFilterTest.kt index a77efc8318..ba366ecb62 100644 --- a/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/observables/NoteListMatchingFilterTest.kt +++ b/commons/src/jvmTest/kotlin/com/vitorpamplona/amethyst/commons/model/observables/NoteListMatchingFilterTest.kt @@ -27,7 +27,6 @@ import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.relay.filters.Filter import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent import com.vitorpamplona.quartz.utils.EventFactory -import java.util.TreeSet import java.util.concurrent.CountDownLatch import java.util.concurrent.atomic.AtomicReference import kotlin.concurrent.thread @@ -67,7 +66,7 @@ class NoteListMatchingFilterTest { sink: (List) -> Unit, ) = NoteListMatchingFilter( filter = withFilter, - atOnce = { TreeSet(CreatedAtIdHexComparator) }, + atOnce = { emptyList() }, update = sink, )