Merge branch 'vitorpamplona:main' into followset-improvements

This commit is contained in:
KotlinGeekDev
2025-10-08 01:53:14 +00:00
committed by GitHub
20 changed files with 830 additions and 25 deletions
@@ -26,7 +26,7 @@ import java.util.concurrent.ConcurrentSkipListMap
import java.util.function.BiConsumer
class LargeSoftCache<K, V> : CacheOperations<K, V> {
private val cache = ConcurrentSkipListMap<K, WeakReference<V>>()
protected val cache = ConcurrentSkipListMap<K, WeakReference<V>>()
fun keys() = cache.keys
@@ -125,6 +125,16 @@ class LargeSoftCache<K, V> : CacheOperations<K, V> {
cache.forEach(BiConsumerWrapper(this, consumer))
}
override fun forEach(
from: K,
to: K,
consumer: BiConsumer<K, V>,
) {
cache
.subMap(from, true, to, true)
.forEach(BiConsumerWrapper(this, consumer))
}
class BiConsumerWrapper<K, V>(
val cache: LargeSoftCache<K, V>,
val inner: BiConsumer<K, V>,
@@ -0,0 +1,79 @@
/**
* 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.amethyst.model
import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import com.vitorpamplona.quartz.utils.cache.CacheCollectors
const val START_KEY = "0000000000000000000000000000000000000000000000000000000000000000"
const val END_KEY = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
const val START_D_TAG = ""
const val END_D_TAG = "\uFFFF\uFFFF\uFFFF\uFFFF"
fun kindStart(
kind: Int,
pubKey: HexKey,
) = Address(kind, pubKey, "")
fun kindEnd(
kind: Int,
pubKey: HexKey,
) = Address(kind, pubKey, END_D_TAG)
fun kindStart(kind: Int) = kindStart(kind, START_KEY)
fun kindEnd(kind: Int) = kindEnd(kind, END_KEY)
val ACCEPT_ALL_FILTER = CacheCollectors.BiFilter<Address, AddressableNote> { key, note -> true }
fun LargeSoftCache<Address, AddressableNote>.filterIntoSet(
kind: Int,
consumer: CacheCollectors.BiFilter<Address, AddressableNote> = ACCEPT_ALL_FILTER,
): Set<AddressableNote> = filterIntoSet(kindStart(kind), kindEnd(kind), consumer)
fun LargeSoftCache<Address, AddressableNote>.filterIntoSet(
kinds: List<Int>,
consumer: CacheCollectors.BiFilter<Address, AddressableNote> = ACCEPT_ALL_FILTER,
): Set<AddressableNote> {
val set = mutableSetOf<AddressableNote>()
kinds.forEach {
set.addAll(filterIntoSet(kindStart(it), kindEnd(it), consumer))
}
return set
}
fun LargeSoftCache<Address, AddressableNote>.filterIntoSet(
kind: Int,
pubKey: HexKey,
consumer: CacheCollectors.BiFilter<Address, AddressableNote> = ACCEPT_ALL_FILTER,
): Set<AddressableNote> = filterIntoSet(kindStart(kind, pubKey), kindEnd(kind, pubKey), consumer)
fun <R> LargeSoftCache<Address, AddressableNote>.mapNotNullIntoSet(
kind: Int,
consumer: CacheCollectors.BiMapper<Address, AddressableNote, R?>,
): Set<R> = mapNotNullIntoSet(kindStart(kind), kindEnd(kind), consumer)
fun <R> LargeSoftCache<Address, AddressableNote>.mapNotNullIntoSet(
kind: Int,
pubKey: HexKey,
consumer: CacheCollectors.BiMapper<Address, AddressableNote, R?>,
): Set<R> = mapNotNullIntoSet(kindStart(kind, pubKey), kindEnd(kind, pubKey), consumer)
@@ -55,12 +55,12 @@ class AccountNotificationsEoseFromInboxRelaysManager(
relay = it,
pubkey = user(key).pubkeyHex,
since = since?.get(it)?.time ?: TimeUtils.oneWeekAgo(),
)
filterNotificationsToPubkey(
relay = it,
pubkey = user(key).pubkeyHex,
since = since?.get(it)?.time ?: key.feedContentStates.notifications.lastNoteCreatedAtIfFilled() ?: TimeUtils.oneWeekAgo(),
)
) +
filterNotificationsToPubkey(
relay = it,
pubkey = user(key).pubkeyHex,
since = since?.get(it)?.time ?: key.feedContentStates.notifications.lastNoteCreatedAtIfFilled() ?: TimeUtils.oneWeekAgo(),
)
}
val userJobMap = mutableMapOf<User, List<Job>>()
@@ -22,7 +22,9 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip23LongForm
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.LocalCache.notes
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.filterIntoSet
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
@@ -45,13 +47,11 @@ open class DiscoverLongFormFeedFilter(
override fun feed(): List<Note> {
val params = buildFilterParams(account)
val notes =
LocalCache.addressables.filterIntoSet { _, it ->
LocalCache.addressables.filterIntoSet(LongTextNoteEvent.KIND) { _, it ->
val noteEvent = it.event
noteEvent is LongTextNoteEvent && params.match(noteEvent)
}
return sort(notes)
}
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip51FollowSets
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.filterIntoSet
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
@@ -45,7 +46,7 @@ open class DiscoverFollowSetsFeedFilter(
val params = buildFilterParams(account)
val notes =
LocalCache.addressables.filterIntoSet { _, it ->
LocalCache.addressables.filterIntoSet(FollowListEvent.KIND) { _, it ->
val noteEvent = it.event
noteEvent is FollowListEvent && params.match(noteEvent)
}
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip72Communities
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.mapNotNullIntoSet
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
import com.vitorpamplona.quartz.nip01Core.core.Address
@@ -54,7 +55,7 @@ open class DiscoverCommunityFeedFilter(
// Here we only need to look for CommunityDefinition Events
val notes =
LocalCache.addressables.mapNotNullIntoSet { key, note ->
LocalCache.addressables.mapNotNullIntoSet(CommunityDefinitionEvent.KIND) { key, note ->
val noteEvent = note.event
if (noteEvent == null && shouldInclude(key, filterParams, note.relays)) {
// send unloaded communities to the screen
@@ -24,6 +24,7 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.ParticipantListBuilder
import com.vitorpamplona.amethyst.model.filterIntoSet
import com.vitorpamplona.amethyst.model.topNavFeeds.allFollows.AllFollowsByOutboxTopNavFilter
import com.vitorpamplona.amethyst.model.topNavFeeds.allFollows.AllFollowsByProxyTopNavFilter
import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.author.AuthorsByOutboxTopNavFilter
@@ -55,7 +56,7 @@ open class DiscoverNIP89FeedFilter(
override fun feed(): List<Note> {
val notes =
LocalCache.addressables.filterIntoSet { _, it ->
LocalCache.addressables.filterIntoSet(AppDefinitionEvent.KIND) { _, it ->
acceptDVM(it)
}
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip99Classifieds
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.filterIntoSet
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent
@@ -46,7 +47,7 @@ open class DiscoverMarketplaceFeedFilter(
val params = buildFilterParams(account)
val notes =
LocalCache.addressables.filterIntoSet { _, it ->
LocalCache.addressables.filterIntoSet(ClassifiedsEvent.KIND) { _, it ->
val noteEvent = it.event
noteEvent is ClassifiedsEvent && noteEvent.isWellFormed() && params.match(noteEvent)
}
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.drafts.dal
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.filterIntoSet
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
@@ -39,7 +40,7 @@ class DraftEventsFeedFilter(
override fun feed(): List<Note> {
val drafts =
LocalCache.addressables.filterIntoSet { _, note ->
LocalCache.addressables.filterIntoSet(DraftWrapEvent.KIND, account.userProfile().pubkeyHex) { _, note ->
acceptableEvent(note)
}
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.home.dal
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.filterIntoSet
import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.muted.MutedAuthorsByOutboxTopNavFilter
import com.vitorpamplona.amethyst.model.topNavFeeds.noteBased.muted.MutedAuthorsByProxyTopNavFilter
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
@@ -45,6 +46,17 @@ import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
class HomeNewThreadFeedFilter(
val account: Account,
) : AdditiveFeedFilter<Note>() {
companion object {
val ADDRESSABLE_KINDS =
listOf(
AudioTrackEvent.KIND,
InteractiveStoryPrologueEvent.KIND,
WikiNoteEvent.KIND,
ClassifiedsEvent.KIND,
LongTextNoteEvent.KIND,
)
}
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + account.settings.defaultHomeFollowList.value
override fun showHiddenKey(): Boolean =
@@ -67,7 +79,9 @@ class HomeNewThreadFeedFilter(
}
val longFormNotes =
LocalCache.addressables.filterIntoSet { _, note ->
LocalCache.addressables.filterIntoSet(
kinds = ADDRESSABLE_KINDS,
) { _, note ->
acceptableEvent(note, filterParams)
}
@@ -24,9 +24,11 @@ import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.filterIntoSet
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
import com.vitorpamplona.quartz.experimental.audio.track.AudioTrackEvent
import com.vitorpamplona.quartz.experimental.forks.forkFromVersion
import com.vitorpamplona.quartz.experimental.forks.isForkFromAddressWithPubkey
import com.vitorpamplona.quartz.nip01Core.core.AddressableEvent
@@ -37,6 +39,7 @@ import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
@@ -47,6 +50,11 @@ import com.vitorpamplona.quartz.nip47WalletConnect.LnZapPaymentResponseEvent
import com.vitorpamplona.quartz.nip51Lists.PrivateTagArrayEvent
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent
import com.vitorpamplona.quartz.nip52Calendar.CalendarDateSlotEvent
import com.vitorpamplona.quartz.nip52Calendar.CalendarRSVPEvent
import com.vitorpamplona.quartz.nip52Calendar.CalendarTimeSlotEvent
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
import com.vitorpamplona.quartz.nip58Badges.BadgeDefinitionEvent
@@ -58,10 +66,25 @@ import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryRequestEvent
import com.vitorpamplona.quartz.nip90Dvms.NIP90ContentDiscoveryResponseEvent
import com.vitorpamplona.quartz.nip90Dvms.NIP90StatusEvent
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
class NotificationFeedFilter(
val account: Account,
) : AdditiveFeedFilter<Note>() {
companion object {
val ADDRESSABLE_KINDS =
listOf(
AudioTrackEvent.KIND,
WikiNoteEvent.KIND,
ClassifiedsEvent.KIND,
LongTextNoteEvent.KIND,
CalendarTimeSlotEvent.KIND,
CalendarDateSlotEvent.KIND,
CalendarRSVPEvent.KIND,
LiveActivitiesEvent.KIND,
)
}
override fun feedKey(): String = account.userProfile().pubkeyHex + "-" + account.settings.defaultNotificationFollowList.value
override fun showHiddenKey(): Boolean =
@@ -83,7 +106,7 @@ class NotificationFeedFilter(
LocalCache.notes.filterIntoSet { _, note ->
note.event !is AddressableEvent && acceptableEvent(note, filterParams)
} +
LocalCache.addressables.filterIntoSet { _, note ->
LocalCache.addressables.filterIntoSet(ADDRESSABLE_KINDS) { _, note ->
acceptableEvent(note, filterParams)
}
@@ -24,7 +24,9 @@ import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.amethyst.model.Account
import com.vitorpamplona.amethyst.model.AddressableNote
import com.vitorpamplona.amethyst.model.LocalCache
import com.vitorpamplona.amethyst.model.LocalCache.notes
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.filterIntoSet
import com.vitorpamplona.amethyst.ui.dal.AdditiveFeedFilter
import com.vitorpamplona.amethyst.ui.dal.DefaultFeedOrder
import com.vitorpamplona.amethyst.ui.dal.FilterByListParams
@@ -67,7 +69,10 @@ class VideoFeedFilter(
LocalCache.notes.filterIntoSet { _, it ->
acceptableEvent(it, params)
} +
LocalCache.addressables.filterIntoSet { _, it ->
LocalCache.addressables.filterIntoSet(VideoHorizontalEvent.KIND) { _, it ->
acceptableEvent(it, params)
} +
LocalCache.addressables.filterIntoSet(VideoVerticalEvent.KIND) { _, it ->
acceptableEvent(it, params)
}
@@ -0,0 +1,152 @@
/**
* 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.amethyst.model
import com.vitorpamplona.quartz.nip01Core.core.Address
import com.vitorpamplona.quartz.nip01Core.core.HexKey
import junit.framework.TestCase.assertEquals
import org.junit.Test
class LargeCacheAddressableFilterTest {
companion object {
fun LargeSoftCache<Address, AddressableNote>.addMock(
kind: Int,
pubkey: HexKey,
dTag: String = "",
) {
val address = Address(kind, pubkey, dTag)
put(address, AddressableNote(address))
}
val cache =
LargeSoftCache<Address, AddressableNote>().apply {
addMock(32000, "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad")
addMock(32000, "6dd3b72e325da7383b275eef1c66131ba4664326e162bc060527509b4e33ae43")
addMock(32000, "3064bf97800a4b04b612fc0fd498936eae75fffbdca5bbd09d19a6dc598530ab")
addMock(32000, "f8ff11c7a7d3478355d3b4d174e5a473797a906ea4aa61aa9b6bc0652c1ea17a")
addMock(32001, "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad")
addMock(32001, "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad", "a")
addMock(32001, "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad", "z")
addMock(32001, "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad", "askldfjaljksdflkaj")
addMock(32001, "6dd3b72e325da7383b275eef1c66131ba4664326e162bc060527509b4e33ae43")
addMock(32001, "3064bf97800a4b04b612fc0fd498936eae75fffbdca5bbd09d19a6dc598530ab")
addMock(32001, "f8ff11c7a7d3478355d3b4d174e5a473797a906ea4aa61aa9b6bc0652c1ea17a")
addMock(32002, "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad")
addMock(32002, "6dd3b72e325da7383b275eef1c66131ba4664326e162bc060527509b4e33ae43")
addMock(32002, "3064bf97800a4b04b612fc0fd498936eae75fffbdca5bbd09d19a6dc598530ab")
addMock(32002, "f8ff11c7a7d3478355d3b4d174e5a473797a906ea4aa61aa9b6bc0652c1ea17a")
addMock(32003, "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad")
addMock(32003, "6dd3b72e325da7383b275eef1c66131ba4664326e162bc060527509b4e33ae43")
addMock(32003, "3064bf97800a4b04b612fc0fd498936eae75fffbdca5bbd09d19a6dc598530ab")
addMock(32003, "f8ff11c7a7d3478355d3b4d174e5a473797a906ea4aa61aa9b6bc0652c1ea17a")
}
}
@Test
fun filterIntoSet() {
val query0 = cache.filterIntoSet(32_000)
val query1 = cache.filterIntoSet(32_001)
val query2 = cache.filterIntoSet(32_002)
val query3 = cache.filterIntoSet(32_003)
assertEquals(4, query0.size)
assertEquals(7, query1.size)
assertEquals(4, query2.size)
assertEquals(4, query3.size)
}
@Test
fun mapIntoSet() {
val query0 = cache.mapNotNullIntoSet(32_000) { key, note -> note }
val query1 = cache.mapNotNullIntoSet(32_001) { key, note -> note }
val query2 = cache.mapNotNullIntoSet(32_002) { key, note -> note }
val query3 = cache.mapNotNullIntoSet(32_003) { key, note -> note }
assertEquals(4, query0.size)
assertEquals(7, query1.size)
assertEquals(4, query2.size)
assertEquals(4, query3.size)
}
@Test
fun filterListIntoSet() {
val query = cache.filterIntoSet(listOf(32_000, 32_001, 32_002, 32_003))
assertEquals(19, query.size)
val query2 = cache.filterIntoSet(listOf(32_000, 32_002, 32_003))
assertEquals(12, query2.size)
val query3 = cache.filterIntoSet(listOf(32_000, 32_001, 32_003))
assertEquals(15, query3.size)
}
@Test
fun filterKindKeyIntoSet() {
val query0 = cache.filterIntoSet(32_000, "6dd3b72e325da7383b275eef1c66131ba4664326e162bc060527509b4e33ae43")
val query1 = cache.filterIntoSet(32_001, "6dd3b72e325da7383b275eef1c66131ba4664326e162bc060527509b4e33ae43")
val query2 = cache.filterIntoSet(32_002, "6dd3b72e325da7383b275eef1c66131ba4664326e162bc060527509b4e33ae43")
val query3 = cache.filterIntoSet(32_003, "6dd3b72e325da7383b275eef1c66131ba4664326e162bc060527509b4e33ae43")
assertEquals(1, query0.size)
assertEquals(1, query1.size)
assertEquals(1, query2.size)
assertEquals(1, query3.size)
val query4 = cache.filterIntoSet(32_000, "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad")
val query5 = cache.filterIntoSet(32_001, "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad")
val query6 = cache.filterIntoSet(32_002, "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad")
val query7 = cache.filterIntoSet(32_003, "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad")
assertEquals(1, query4.size)
assertEquals(4, query5.size)
assertEquals(1, query6.size)
assertEquals(1, query7.size)
}
@Test
fun mapKindKeyIntoSet() {
val query0 = cache.mapNotNullIntoSet(32_000, "6dd3b72e325da7383b275eef1c66131ba4664326e162bc060527509b4e33ae43") { key, note -> note }
val query1 = cache.mapNotNullIntoSet(32_001, "6dd3b72e325da7383b275eef1c66131ba4664326e162bc060527509b4e33ae43") { key, note -> note }
val query2 = cache.mapNotNullIntoSet(32_002, "6dd3b72e325da7383b275eef1c66131ba4664326e162bc060527509b4e33ae43") { key, note -> note }
val query3 = cache.mapNotNullIntoSet(32_003, "6dd3b72e325da7383b275eef1c66131ba4664326e162bc060527509b4e33ae43") { key, note -> note }
assertEquals(1, query0.size)
assertEquals(1, query1.size)
assertEquals(1, query2.size)
assertEquals(1, query3.size)
val query4 = cache.mapNotNullIntoSet(32_000, "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad") { key, note -> note }
val query5 = cache.mapNotNullIntoSet(32_001, "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad") { key, note -> note }
val query6 = cache.mapNotNullIntoSet(32_002, "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad") { key, note -> note }
val query7 = cache.mapNotNullIntoSet(32_003, "0b6d941c46411a95edb1c93da7ad6ca26370497d8c7b7d621f5cb59f48841bad") { key, note -> note }
assertEquals(1, query4.size)
assertEquals(4, query5.size)
assertEquals(1, query6.size)
assertEquals(1, query7.size)
}
}