diff --git a/CHANGELOG.md b/CHANGELOG.md index f223cc45c17..0fe26376bc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,16 @@ * `ReactionEmoji.Unicode` now correctly compares on name for equality. +## Performance + +* `CacheEntitySupplier#getGuildMembers` will no longer query MemberData on each user. +* Reduced the number of mappings inside suppliers. + +## Fixes + +* Fixed type mismatch when comparing guildId in `CacheEntitySupplier#getGuildMembers` + + # 0.6.0 ## Changes diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/CacheEntitySupplier.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/CacheEntitySupplier.kt index 636258d7378..7779ddb85a0 100644 --- a/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/CacheEntitySupplier.kt +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/CacheEntitySupplier.kt @@ -29,6 +29,14 @@ import kotlinx.coroutines.flow.* * if none are presented like other `getX` functions. Instead, the flow will be empty. */ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { + /** + * + * The Cache this [CacheEntitySupplier] operates on. + * + * short-hand for [Kord.cache] + * + */ + private val cache: DataCache = kord.cache /** * Returns a [Flow] of [Channel]s fetched from cache. @@ -37,7 +45,7 @@ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { * [terminal operators](https://kotlinlang.org/docs/reference/coroutines/flow.html#terminal-flow-operators) instead. */ val channels: Flow - get() = kord.cache.query().asFlow().map { Channel.from(it, kord) } + get() = cache.query().asFlow().map { Channel.from(it, kord) } /** * fetches all cached [Guild]s @@ -46,7 +54,7 @@ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { * [terminal operators](https://kotlinlang.org/docs/reference/coroutines/flow.html#terminal-flow-operators) instead. */ override val guilds: Flow - get() = kord.cache.query().asFlow().map { Guild(it, kord) } + get() = cache.query().asFlow().map { Guild(it, kord) } /** * fetches all cached [Region]s @@ -55,60 +63,60 @@ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { * [terminal operators](https://kotlinlang.org/docs/reference/coroutines/flow.html#terminal-flow-operators) instead. */ override val regions: Flow - get() = kord.cache.query().asFlow().map { Region(it, kord) } + get() = cache.query().asFlow().map { Region(it, kord) } /** * fetches all cached [Role]s */ val roles: Flow - get() = kord.cache.query().asFlow().map { Role(it, kord) } + get() = cache.query().asFlow().map { Role(it, kord) } /** * fetches all cached [User]s */ val users: Flow - get() = kord.cache.query().asFlow().map { User(it, kord) } + get() = cache.query().asFlow().map { User(it, kord) } /** * fetches all cached [Member]s */ @OptIn(FlowPreview::class) val members: Flow - get() = kord.cache.query().asFlow().flatMapConcat { userData -> - kord.cache.query { MemberData::userId eq userData.id } - .asFlow().map { Member(it, userData, kord) } + get() = cache.query().asFlow().mapNotNull { + val userData = cache.query { UserData::id eq it.userId }.singleOrNull() ?: return@mapNotNull null + Member(it, userData, kord) } suspend fun getRole(id: Snowflake): Role? { - val data = kord.cache.query { RoleData::id eq id.longValue }.singleOrNull() ?: return null + val data = cache.query { RoleData::id eq id.longValue }.singleOrNull() ?: return null return Role(data, kord) } override suspend fun getChannelOrNull(id: Snowflake): Channel? { - val data = kord.cache.query { ChannelData::id eq id.longValue }.singleOrNull() ?: return null + val data = cache.query { ChannelData::id eq id.longValue }.singleOrNull() ?: return null return Channel.from(data, kord) } - override fun getGuildChannels(guildId: Snowflake): Flow = kord.cache.query { + override fun getGuildChannels(guildId: Snowflake): Flow = cache.query { ChannelData::guildId eq guildId.longValue }.asFlow().map { Channel.from(it, kord) as GuildChannel } - override fun getChannelPins(channelId: Snowflake): Flow = kord.cache.query { + override fun getChannelPins(channelId: Snowflake): Flow = cache.query { MessageData::channelId eq channelId.longValue MessageData::pinned eq true }.asFlow().map { Message(it, kord) } override suspend fun getGuildOrNull(id: Snowflake): Guild? { - val data = kord.cache.query { GuildData::id eq id.longValue }.singleOrNull() ?: return null + val data = cache.query { GuildData::id eq id.longValue }.singleOrNull() ?: return null return Guild(data, kord) } override suspend fun getMemberOrNull(guildId: Snowflake, userId: Snowflake): Member? { - val userData = kord.cache.query { UserData::id eq userId.longValue }.singleOrNull() ?: return null + val userData = cache.query { UserData::id eq userId.longValue }.singleOrNull() ?: return null - val memberData = kord.cache.query { + val memberData = cache.query { MemberData::userId eq userId.longValue MemberData::guildId eq guildId.longValue }.singleOrNull() ?: return null @@ -117,7 +125,7 @@ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { } override suspend fun getMessageOrNull(channelId: Snowflake, messageId: Snowflake): Message? { - val data = kord.cache.query { MessageData::id eq messageId.longValue }.singleOrNull() + val data = cache.query { MessageData::id eq messageId.longValue }.singleOrNull() ?: return null return Message(data, kord) @@ -125,7 +133,7 @@ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { override fun getMessagesAfter(messageId: Snowflake, channelId: Snowflake, limit: Int): Flow { require(limit > 0) { "At least 1 item should be requested, but got $limit." } - return kord.cache.query { + return cache.query { MessageData::channelId eq channelId.longValue MessageData::id gt messageId.longValue }.asFlow().map { Message(it, kord) }.take(limit) @@ -133,7 +141,7 @@ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { override fun getMessagesBefore(messageId: Snowflake, channelId: Snowflake, limit: Int): Flow { require(limit > 0) { "At least 1 item should be requested, but got $limit." } - return kord.cache.query { + return cache.query { MessageData::channelId eq channelId.longValue MessageData::id lt messageId.longValue }.asFlow().map { Message(it, kord) }.take(limit) @@ -151,7 +159,7 @@ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { override suspend fun getSelfOrNull(): User? = getUserOrNull(kord.selfId) override suspend fun getRoleOrNull(guildId: Snowflake, roleId: Snowflake): Role? { - val data = kord.cache.query { + val data = cache.query { RoleData::id eq roleId.longValue RoleData::guildId eq guildId.longValue }.singleOrNull() ?: return null @@ -159,38 +167,36 @@ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { return Role(data, kord) } - override fun getGuildRoles(guildId: Snowflake): Flow = kord.cache.query { + override fun getGuildRoles(guildId: Snowflake): Flow = cache.query { RoleData::guildId eq guildId.longValue }.asFlow().map { Role(it, kord) } override suspend fun getGuildBanOrNull(guildId: Snowflake, userId: Snowflake): Ban? { - val data = kord.cache.query { + val data = cache.query { BanData::userId eq userId.longValue BanData::guildId eq guildId.longValue }.singleOrNull() ?: return null return Ban(data, kord) } - override fun getGuildBans(guildId: Snowflake): Flow = kord.cache.query { + override fun getGuildBans(guildId: Snowflake): Flow = cache.query { BanData::guildId eq guildId.longValue }.asFlow().map { Ban(it, kord) } override fun getGuildMembers(guildId: Snowflake, limit: Int): Flow { require(limit > 0) { "At least 1 item should be requested, but got $limit." } - return kord.cache.query().asFlow().flatMapConcat { userData -> - kord.cache.query { - MemberData::userId eq userData.id - MemberData::guildId eq guildId - }.asFlow().map { Member(it, userData, kord) }.take(limit) + return cache.query { MemberData::guildId eq guildId.longValue }.asFlow().mapNotNull { + val userData = cache.query { UserData::id eq it.userId }.singleOrNull() ?: return@mapNotNull null + Member(it, userData, kord) } } - override fun getGuildVoiceRegions(guildId: Snowflake): Flow = kord.cache.query { + override fun getGuildVoiceRegions(guildId: Snowflake): Flow = cache.query { RegionData::guildId eq guildId.longValue }.asFlow().map { Region(it, kord) } override suspend fun getEmojiOrNull(guildId: Snowflake, emojiId: Snowflake): GuildEmoji? { - val data = kord.cache.query { + val data = cache.query { EmojiData::guildId eq guildId.longValue EmojiData::id eq emojiId.longValue }.singleOrNull() ?: return null @@ -198,7 +204,7 @@ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { return GuildEmoji(data, kord) } - override fun getEmojis(guildId: Snowflake): Flow = kord.cache.query { + override fun getEmojis(guildId: Snowflake): Flow = cache.query { EmojiData::guildId eq guildId.longValue }.asFlow().map { GuildEmoji(it, kord) } @@ -209,16 +215,16 @@ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { }.take(limit) } - override fun getChannelWebhooks(channelId: Snowflake): Flow = kord.cache.query { + override fun getChannelWebhooks(channelId: Snowflake): Flow = cache.query { WebhookData::channelId eq channelId.longValue }.asFlow().map { Webhook(it, kord) } - override fun getGuildWebhooks(guildId: Snowflake): Flow = kord.cache.query { + override fun getGuildWebhooks(guildId: Snowflake): Flow = cache.query { WebhookData::guildId eq guildId.longValue }.asFlow().map { Webhook(it, kord) } override suspend fun getWebhookOrNull(id: Snowflake): Webhook? { - val data = kord.cache.query { + val data = cache.query { WebhookData::id eq id.longValue }.singleOrNull() ?: return null @@ -226,7 +232,7 @@ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { } override suspend fun getWebhookWithTokenOrNull(id: Snowflake, token: String): Webhook? { - val data = kord.cache.query { + val data = cache.query { WebhookData::id eq id.longValue WebhookData::token eq token }.singleOrNull() ?: return null @@ -235,7 +241,7 @@ class CacheEntitySupplier(private val kord: Kord) : EntitySupplier { } override suspend fun getUserOrNull(id: Snowflake): User? { - val data = kord.cache.query { UserData::id eq id.longValue }.singleOrNull() ?: return null + val data = cache.query { UserData::id eq id.longValue }.singleOrNull() ?: return null return User(data, kord) } diff --git a/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/RestEntitySupplier.kt b/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/RestEntitySupplier.kt index 3c8c9b60d5f..9997848d8b2 100644 --- a/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/RestEntitySupplier.kt +++ b/core/src/main/kotlin/com/gitlab/kordlib/core/supplier/RestEntitySupplier.kt @@ -44,14 +44,19 @@ class RestEntitySupplier(val kord: Kord) : EntitySupplier { override val guilds: Flow get() = paginateForwards(idSelector = DiscordPartialGuild::id, batchSize = 100) { position -> user.getCurrentUserGuilds(position, 100) } - .map { guild.getGuild(it.id) } - .map { GuildData.from(it) } - .map { Guild(it, kord) } + .map { + val guild = guild.getGuild(it.id) + val data = GuildData.from(guild) + Guild(data, kord) + } override val regions: Flow get() = flow { - voice.getVoiceRegions().forEach { emit(it) } - }.map { RegionData.from(null, it) }.map { Region(it, kord) } + voice.getVoiceRegions().forEach { + val data = RegionData.from(null, it) + emit(Region(data, kord)) + } + } override suspend fun getChannelOrNull(id: Snowflake): Channel? = catchNotFound { Channel.from(channel.getChannel(id.value).toData(), kord) } @@ -105,7 +110,10 @@ class RestEntitySupplier(val kord: Kord) : EntitySupplier { val flow = paginateForwards(messageId, batchSize, idSelector = { it.id }) { position -> kord.rest.channel.getMessages(channelId.value, position, batchSize) - }.map { MessageData.from(it) }.map { Message(it, kord) } + }.map { + val data = MessageData.from(it) + Message(data, kord) + } return if (limit != Int.MAX_VALUE) flow.take(limit) else flow } @@ -116,7 +124,10 @@ class RestEntitySupplier(val kord: Kord) : EntitySupplier { val flow = paginateBackwards(messageId, batchSize, idSelector = { it.id }) { position -> kord.rest.channel.getMessages(channelId.value, position, batchSize) - }.map { MessageData.from(it) }.map { Message(it, kord) } + }.map { + val data = MessageData.from(it) + Message(data, kord) + } return if (limit != Int.MAX_VALUE) flow.take(limit) else flow } @@ -194,7 +205,10 @@ class RestEntitySupplier(val kord: Kord) : EntitySupplier { limit = 100, position = position ) - }.map { UserData.from(it) }.map { User(it, kord) } + }.map { + val data = UserData.from(it) + User(data, kord) + } override suspend fun getEmojiOrNull(guildId: Snowflake, emojiId: Snowflake) = catchNotFound { val data = EmojiData.from(guildId.value, emojiId.value, emoji.getEmoji(guildId.value, emojiId.value))