Quick filter request refactor
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
package org.koitharu.kotatsu.core.db
|
||||
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
import java.util.LinkedList
|
||||
|
||||
class MangaQueryBuilder(
|
||||
private val table: String,
|
||||
private val conditionCallback: ConditionCallback
|
||||
) {
|
||||
|
||||
private var filterOptions: Collection<ListFilterOption> = emptyList()
|
||||
private var whereConditions = LinkedList<String>()
|
||||
private var orderBy: String? = null
|
||||
private var groupBy: String? = null
|
||||
private var extraJoins: String? = null
|
||||
private var limit: Int = 0
|
||||
|
||||
fun filters(options: Collection<ListFilterOption>) = apply {
|
||||
filterOptions = options
|
||||
}
|
||||
|
||||
fun where(condition: String) = apply {
|
||||
whereConditions.add(condition)
|
||||
}
|
||||
|
||||
fun orderBy(orderBy: String?) = apply {
|
||||
this@MangaQueryBuilder.orderBy = orderBy
|
||||
}
|
||||
|
||||
fun groupBy(groupBy: String?) = apply {
|
||||
this@MangaQueryBuilder.groupBy = groupBy
|
||||
}
|
||||
|
||||
fun limit(limit: Int) = apply {
|
||||
this@MangaQueryBuilder.limit = limit
|
||||
}
|
||||
|
||||
fun join(join: String?) = apply {
|
||||
extraJoins = join
|
||||
}
|
||||
|
||||
fun build() = buildString {
|
||||
append("SELECT * FROM ")
|
||||
append(table)
|
||||
extraJoins?.let {
|
||||
append(' ')
|
||||
append(it)
|
||||
}
|
||||
if (whereConditions.isNotEmpty()) {
|
||||
whereConditions.joinTo(
|
||||
buffer = this,
|
||||
prefix = " WHERE ",
|
||||
separator = " AND ",
|
||||
)
|
||||
}
|
||||
if (filterOptions.isNotEmpty()) {
|
||||
if (whereConditions.isEmpty()) {
|
||||
append(" WHERE")
|
||||
}
|
||||
var isFirst = true
|
||||
val groupedOptions = filterOptions.groupBy { it.groupKey }
|
||||
for ((_, group) in groupedOptions) {
|
||||
if (group.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
if (isFirst) {
|
||||
isFirst = false
|
||||
append(' ')
|
||||
} else {
|
||||
append(" AND ")
|
||||
}
|
||||
if (group.size > 1) {
|
||||
group.joinTo(
|
||||
buffer = this,
|
||||
separator = " OR ",
|
||||
prefix = "(",
|
||||
postfix = ")",
|
||||
transform = ::getConditionOrThrow,
|
||||
)
|
||||
} else {
|
||||
append(getConditionOrThrow(group.single()))
|
||||
}
|
||||
}
|
||||
}
|
||||
groupBy?.let {
|
||||
append(" GROUP BY ")
|
||||
append(it)
|
||||
}
|
||||
orderBy?.let {
|
||||
append(" ORDER BY ")
|
||||
append(it)
|
||||
}
|
||||
if (limit > 0) {
|
||||
append(" LIMIT ")
|
||||
append(limit)
|
||||
}
|
||||
}.let { SimpleSQLiteQuery(it) }
|
||||
|
||||
private fun getConditionOrThrow(option: ListFilterOption): String =
|
||||
requireNotNull(conditionCallback.getCondition(option)) {
|
||||
"Unsupported filter option $option"
|
||||
}
|
||||
|
||||
fun interface ConditionCallback {
|
||||
|
||||
fun getCondition(option: ListFilterOption): String?
|
||||
}
|
||||
}
|
||||
@@ -6,51 +6,26 @@ import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.RawQuery
|
||||
import androidx.room.Transaction
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.koitharu.kotatsu.core.db.entity.toEntity
|
||||
import org.koitharu.kotatsu.core.db.MangaQueryBuilder
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
import org.koitharu.kotatsu.tracker.data.TrackLogEntity
|
||||
import org.koitharu.kotatsu.tracker.data.TrackLogWithManga
|
||||
|
||||
@Dao
|
||||
abstract class TrackLogsDao {
|
||||
abstract class TrackLogsDao : MangaQueryBuilder.ConditionCallback {
|
||||
|
||||
fun observeAll(limit: Int, filterOptions: Set<ListFilterOption>): Flow<List<TrackLogWithManga>> {
|
||||
val query = buildString {
|
||||
append("SELECT * FROM track_logs")
|
||||
if (filterOptions.isNotEmpty()) {
|
||||
append(" WHERE")
|
||||
var isFirst = true
|
||||
val groupedOptions = filterOptions.groupBy { it.groupKey }
|
||||
for ((_, group) in groupedOptions) {
|
||||
if (group.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
if (isFirst) {
|
||||
isFirst = false
|
||||
append(' ')
|
||||
} else {
|
||||
append(" AND ")
|
||||
}
|
||||
if (group.size > 1) {
|
||||
group.joinTo(this, separator = " OR ", prefix = "(", postfix = ")") {
|
||||
it.getCondition()
|
||||
}
|
||||
} else {
|
||||
append(group.single().getCondition())
|
||||
}
|
||||
}
|
||||
}
|
||||
append(" ORDER BY created_at DESC")
|
||||
if (limit > 0) {
|
||||
append(" LIMIT ")
|
||||
append(limit)
|
||||
}
|
||||
}
|
||||
return observeAllImpl(SimpleSQLiteQuery(query))
|
||||
}
|
||||
fun observeAll(
|
||||
limit: Int,
|
||||
filterOptions: Set<ListFilterOption>,
|
||||
): Flow<List<TrackLogWithManga>> = observeAllImpl(
|
||||
MangaQueryBuilder("track_logs", this)
|
||||
.filters(filterOptions)
|
||||
.limit(limit)
|
||||
.orderBy("created_at DESC")
|
||||
.build(),
|
||||
)
|
||||
|
||||
@Query("SELECT COUNT(*) FROM track_logs WHERE unread = 1")
|
||||
abstract fun observeUnreadCount(): Flow<Int>
|
||||
@@ -77,10 +52,10 @@ abstract class TrackLogsDao {
|
||||
@RawQuery(observedEntities = [TrackLogEntity::class])
|
||||
protected abstract fun observeAllImpl(query: SupportSQLiteQuery): Flow<List<TrackLogWithManga>>
|
||||
|
||||
private fun ListFilterOption.getCondition(): String = when (this) {
|
||||
override fun getCondition(option: ListFilterOption): String? = when (option) {
|
||||
ListFilterOption.Macro.FAVORITE -> "EXISTS(SELECT * FROM favourites WHERE favourites.manga_id = track_logs.manga_id)"
|
||||
is ListFilterOption.Favorite -> "EXISTS(SELECT * FROM favourites WHERE favourites.manga_id = track_logs.manga_id AND favourites.category_id = ${category.id})"
|
||||
is ListFilterOption.Tag -> "EXISTS(SELECT * FROM manga_tags WHERE manga_tags.manga_id = track_logs.manga_id AND tag_id = ${tag.toEntity().id})"
|
||||
else -> throw IllegalArgumentException("Unsupported option $this")
|
||||
is ListFilterOption.Favorite -> "EXISTS(SELECT * FROM favourites WHERE favourites.manga_id = track_logs.manga_id AND favourites.category_id = ${option.category.id})"
|
||||
is ListFilterOption.Tag -> "EXISTS(SELECT * FROM manga_tags WHERE manga_tags.manga_id = track_logs.manga_id AND tag_id = ${option.tagId})"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,14 +11,15 @@ import androidx.sqlite.db.SimpleSQLiteQuery
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.intellij.lang.annotations.Language
|
||||
import org.koitharu.kotatsu.core.db.entity.toEntity
|
||||
import org.koitharu.kotatsu.core.db.MangaQueryBuilder
|
||||
import org.koitharu.kotatsu.core.db.TABLE_FAVOURITES
|
||||
import org.koitharu.kotatsu.favourites.domain.model.Cover
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
import org.koitharu.kotatsu.list.domain.ListSortOrder
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress.Companion.PROGRESS_COMPLETED
|
||||
|
||||
@Dao
|
||||
abstract class FavouritesDao {
|
||||
abstract class FavouritesDao : MangaQueryBuilder.ConditionCallback {
|
||||
|
||||
/** SELECT **/
|
||||
|
||||
@@ -55,41 +56,17 @@ abstract class FavouritesDao {
|
||||
order: ListSortOrder,
|
||||
filterOptions: Set<ListFilterOption>,
|
||||
limit: Int
|
||||
): Flow<List<FavouriteManga>> {
|
||||
val orderBy = getOrderBy(order)
|
||||
val query = buildString {
|
||||
append(
|
||||
"SELECT * FROM favourites LEFT JOIN manga ON favourites.manga_id = manga.manga_id " +
|
||||
"WHERE deleted_at = 0",
|
||||
)
|
||||
if (categoryId != 0L) {
|
||||
append(" AND category_id = ")
|
||||
append(categoryId)
|
||||
}
|
||||
val groupedOptions = filterOptions.groupBy { it.groupKey }
|
||||
for ((_, group) in groupedOptions) {
|
||||
if (group.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
append(" AND ")
|
||||
if (group.size > 1) {
|
||||
group.joinTo(this, separator = " OR ", prefix = "(", postfix = ")") {
|
||||
it.getCondition()
|
||||
}
|
||||
} else {
|
||||
append(group.single().getCondition())
|
||||
}
|
||||
}
|
||||
append(" GROUP BY favourites.manga_id ORDER BY ")
|
||||
append(orderBy)
|
||||
if (limit > 0) {
|
||||
append(" LIMIT ")
|
||||
append(limit)
|
||||
}
|
||||
}
|
||||
|
||||
return observeAllImpl(SimpleSQLiteQuery(query))
|
||||
}
|
||||
): Flow<List<FavouriteManga>> = observeAllImpl(
|
||||
MangaQueryBuilder(TABLE_FAVOURITES, this)
|
||||
.join("LEFT JOIN manga ON favourites.manga_id = manga.manga_id")
|
||||
.where("deleted_at = 0")
|
||||
.run { if (categoryId != 0L) where("category_id = $categoryId") else this }
|
||||
.filters(filterOptions)
|
||||
.groupBy("favourites.manga_id")
|
||||
.orderBy(getOrderBy(order))
|
||||
.limit(limit)
|
||||
.build(),
|
||||
)
|
||||
|
||||
suspend fun findCovers(categoryId: Long, order: ListSortOrder): List<Cover> {
|
||||
val orderBy = getOrderBy(order)
|
||||
@@ -213,13 +190,13 @@ abstract class FavouritesDao {
|
||||
else -> throw IllegalArgumentException("Sort order $sortOrder is not supported")
|
||||
}
|
||||
|
||||
private fun ListFilterOption.getCondition(): String = when (this) {
|
||||
override fun getCondition(option: ListFilterOption): String? = when (option) {
|
||||
ListFilterOption.Macro.COMPLETED -> "EXISTS(SELECT * FROM history WHERE history.manga_id = favourites.manga_id AND history.percent >= $PROGRESS_COMPLETED)"
|
||||
ListFilterOption.Macro.NEW_CHAPTERS -> "(SELECT chapters_new FROM tracks WHERE tracks.manga_id = favourites.manga_id) > 0"
|
||||
ListFilterOption.Macro.NSFW -> "manga.nsfw = 1"
|
||||
is ListFilterOption.Tag -> "EXISTS(SELECT * FROM manga_tags WHERE favourites.manga_id = manga_tags.manga_id AND tag_id = ${tag.toEntity().id})"
|
||||
is ListFilterOption.Tag -> "EXISTS(SELECT * FROM manga_tags WHERE favourites.manga_id = manga_tags.manga_id AND tag_id = ${option.tagId})"
|
||||
ListFilterOption.Downloaded,
|
||||
is ListFilterOption.Favorite,
|
||||
ListFilterOption.Macro.FAVORITE -> throw IllegalArgumentException("Unsupported option $this")
|
||||
ListFilterOption.Macro.FAVORITE -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,17 +6,17 @@ import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.RawQuery
|
||||
import androidx.room.Transaction
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.koitharu.kotatsu.core.db.MangaQueryBuilder
|
||||
import org.koitharu.kotatsu.core.db.TABLE_HISTORY
|
||||
import org.koitharu.kotatsu.core.db.entity.TagEntity
|
||||
import org.koitharu.kotatsu.core.db.entity.toEntity
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
import org.koitharu.kotatsu.list.domain.ListSortOrder
|
||||
import org.koitharu.kotatsu.list.domain.ReadingProgress.Companion.PROGRESS_COMPLETED
|
||||
|
||||
@Dao
|
||||
abstract class HistoryDao {
|
||||
abstract class HistoryDao : MangaQueryBuilder.ConditionCallback {
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM history WHERE deleted_at = 0 ORDER BY updated_at DESC LIMIT :limit OFFSET :offset")
|
||||
@@ -34,48 +34,30 @@ abstract class HistoryDao {
|
||||
order: ListSortOrder,
|
||||
filterOptions: Set<ListFilterOption>,
|
||||
limit: Int
|
||||
): Flow<List<HistoryWithManga>> {
|
||||
val orderBy = when (order) {
|
||||
ListSortOrder.LAST_READ -> "history.updated_at DESC"
|
||||
ListSortOrder.LONG_AGO_READ -> "history.updated_at ASC"
|
||||
ListSortOrder.NEWEST -> "history.created_at DESC"
|
||||
ListSortOrder.OLDEST -> "history.created_at ASC"
|
||||
ListSortOrder.PROGRESS -> "history.percent DESC"
|
||||
ListSortOrder.UNREAD -> "history.percent ASC"
|
||||
ListSortOrder.ALPHABETIC -> "manga.title"
|
||||
ListSortOrder.ALPHABETIC_REVERSE -> "manga.title DESC"
|
||||
ListSortOrder.NEW_CHAPTERS -> "IFNULL((SELECT chapters_new FROM tracks WHERE tracks.manga_id = manga.manga_id), 0) DESC"
|
||||
ListSortOrder.UPDATED -> "IFNULL((SELECT last_chapter_date FROM tracks WHERE tracks.manga_id = manga.manga_id), 0) DESC"
|
||||
else -> throw IllegalArgumentException("Sort order $order is not supported")
|
||||
}
|
||||
val query = buildString {
|
||||
append(
|
||||
"SELECT * FROM history LEFT JOIN manga ON history.manga_id = manga.manga_id " +
|
||||
"WHERE history.deleted_at = 0",
|
||||
): Flow<List<HistoryWithManga>> = observeAllImpl(
|
||||
MangaQueryBuilder(TABLE_HISTORY, this)
|
||||
.join("LEFT JOIN manga ON history.manga_id = manga.manga_id")
|
||||
.where("history.deleted_at = 0")
|
||||
.filters(filterOptions)
|
||||
.orderBy(
|
||||
orderBy = when (order) {
|
||||
ListSortOrder.LAST_READ -> "history.updated_at DESC"
|
||||
ListSortOrder.LONG_AGO_READ -> "history.updated_at ASC"
|
||||
ListSortOrder.NEWEST -> "history.created_at DESC"
|
||||
ListSortOrder.OLDEST -> "history.created_at ASC"
|
||||
ListSortOrder.PROGRESS -> "history.percent DESC"
|
||||
ListSortOrder.UNREAD -> "history.percent ASC"
|
||||
ListSortOrder.ALPHABETIC -> "manga.title"
|
||||
ListSortOrder.ALPHABETIC_REVERSE -> "manga.title DESC"
|
||||
ListSortOrder.NEW_CHAPTERS -> "IFNULL((SELECT chapters_new FROM tracks WHERE tracks.manga_id = manga.manga_id), 0) DESC"
|
||||
ListSortOrder.UPDATED -> "IFNULL((SELECT last_chapter_date FROM tracks WHERE tracks.manga_id = manga.manga_id), 0) DESC"
|
||||
else -> throw IllegalArgumentException("Sort order $order is not supported")
|
||||
},
|
||||
)
|
||||
val groupedOptions = filterOptions.groupBy { it.groupKey }
|
||||
for ((_, group) in groupedOptions) {
|
||||
if (group.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
append(" AND ")
|
||||
if (group.size > 1) {
|
||||
group.joinTo(this, separator = " OR ", prefix = "(", postfix = ")") {
|
||||
it.getCondition()
|
||||
}
|
||||
} else {
|
||||
append(group.single().getCondition())
|
||||
}
|
||||
}
|
||||
append(" GROUP BY history.manga_id ORDER BY ")
|
||||
append(orderBy)
|
||||
if (limit > 0) {
|
||||
append(" LIMIT ")
|
||||
append(limit)
|
||||
}
|
||||
}
|
||||
return observeAllImpl(SimpleSQLiteQuery(query))
|
||||
}
|
||||
.groupBy("history.manga_id")
|
||||
.limit(limit)
|
||||
.build(),
|
||||
)
|
||||
|
||||
@Query("SELECT manga_id FROM history WHERE deleted_at = 0")
|
||||
abstract suspend fun findAllIds(): LongArray
|
||||
@@ -170,13 +152,13 @@ abstract class HistoryDao {
|
||||
@RawQuery(observedEntities = [HistoryEntity::class])
|
||||
protected abstract fun observeAllImpl(query: SupportSQLiteQuery): Flow<List<HistoryWithManga>>
|
||||
|
||||
private fun ListFilterOption.getCondition(): String = when (this) {
|
||||
ListFilterOption.Downloaded -> throw IllegalArgumentException("Unsupported option $this")
|
||||
is ListFilterOption.Favorite -> "EXISTS(SELECT * FROM favourites WHERE history.manga_id = favourites.manga_id AND category_id = ${category.id})"
|
||||
override fun getCondition(option: ListFilterOption): String? = when (option) {
|
||||
ListFilterOption.Downloaded -> null
|
||||
is ListFilterOption.Favorite -> "EXISTS(SELECT * FROM favourites WHERE history.manga_id = favourites.manga_id AND category_id = ${option.category.id})"
|
||||
ListFilterOption.Macro.COMPLETED -> "percent >= $PROGRESS_COMPLETED"
|
||||
ListFilterOption.Macro.NEW_CHAPTERS -> "(SELECT chapters_new FROM tracks WHERE tracks.manga_id = history.manga_id) > 0"
|
||||
ListFilterOption.Macro.FAVORITE -> "EXISTS(SELECT * FROM favourites WHERE history.manga_id = favourites.manga_id)"
|
||||
ListFilterOption.Macro.NSFW -> "manga.nsfw = 1"
|
||||
is ListFilterOption.Tag -> "EXISTS(SELECT * FROM manga_tags WHERE history.manga_id = manga_tags.manga_id AND tag_id = ${tag.toEntity().id})"
|
||||
is ListFilterOption.Tag -> "EXISTS(SELECT * FROM manga_tags WHERE history.manga_id = manga_tags.manga_id AND tag_id = ${option.tagId})"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.list.domain
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.db.entity.toEntity
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||
|
||||
@@ -55,6 +56,8 @@ sealed interface ListFilterOption {
|
||||
val tag: MangaTag
|
||||
) : ListFilterOption {
|
||||
|
||||
val tagId: Long = tag.toEntity().id
|
||||
|
||||
override val titleResId: Int
|
||||
get() = 0
|
||||
|
||||
|
||||
@@ -7,54 +7,29 @@ import androidx.room.Query
|
||||
import androidx.room.RawQuery
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Update
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.koitharu.kotatsu.core.db.MangaQueryBuilder
|
||||
import org.koitharu.kotatsu.core.db.entity.TagEntity
|
||||
import org.koitharu.kotatsu.core.db.entity.toEntity
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
|
||||
@Dao
|
||||
abstract class SuggestionDao {
|
||||
abstract class SuggestionDao : MangaQueryBuilder.ConditionCallback {
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM suggestions ORDER BY relevance DESC")
|
||||
abstract fun observeAll(): Flow<List<SuggestionWithManga>>
|
||||
|
||||
fun observeAll(limit: Int, filterOptions: Collection<ListFilterOption>): Flow<List<SuggestionWithManga>> {
|
||||
val query = buildString {
|
||||
append("SELECT * FROM suggestions")
|
||||
if (filterOptions.isNotEmpty()) {
|
||||
append(" WHERE")
|
||||
var isFirst = true
|
||||
val groupedOptions = filterOptions.groupBy { it.groupKey }
|
||||
for ((_, group) in groupedOptions) {
|
||||
if (group.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
if (isFirst) {
|
||||
isFirst = false
|
||||
append(' ')
|
||||
} else {
|
||||
append(" AND ")
|
||||
}
|
||||
if (group.size > 1) {
|
||||
group.joinTo(this, separator = " OR ", prefix = "(", postfix = ")") {
|
||||
it.getCondition()
|
||||
}
|
||||
} else {
|
||||
append(group.single().getCondition())
|
||||
}
|
||||
}
|
||||
}
|
||||
append(" ORDER BY relevance DESC")
|
||||
if (limit > 0) {
|
||||
append(" LIMIT ")
|
||||
append(limit)
|
||||
}
|
||||
}
|
||||
return observeAllImpl(SimpleSQLiteQuery(query))
|
||||
}
|
||||
fun observeAll(
|
||||
limit: Int,
|
||||
filterOptions: Collection<ListFilterOption>
|
||||
): Flow<List<SuggestionWithManga>> = observeAllImpl(
|
||||
MangaQueryBuilder("suggestions", this)
|
||||
.filters(filterOptions)
|
||||
.orderBy("relevance DESC")
|
||||
.limit(limit)
|
||||
.build(),
|
||||
)
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM suggestions ORDER BY RANDOM() LIMIT 1")
|
||||
@@ -93,9 +68,9 @@ abstract class SuggestionDao {
|
||||
@RawQuery(observedEntities = [SuggestionEntity::class])
|
||||
protected abstract fun observeAllImpl(query: SupportSQLiteQuery): Flow<List<SuggestionWithManga>>
|
||||
|
||||
private fun ListFilterOption.getCondition(): String = when (this) {
|
||||
override fun getCondition(option: ListFilterOption): String? = when (option) {
|
||||
ListFilterOption.Macro.NSFW -> "(SELECT nsfw FROM manga WHERE manga.manga_id = suggestions.manga_id) = 1"
|
||||
is ListFilterOption.Tag -> "EXISTS(SELECT * FROM manga_tags WHERE manga_tags.manga_id = suggestions.manga_id AND tag_id = ${tag.toEntity().id})"
|
||||
else -> throw IllegalArgumentException("Unsupported option $this")
|
||||
is ListFilterOption.Tag -> "EXISTS(SELECT * FROM manga_tags WHERE manga_tags.manga_id = suggestions.manga_id AND tag_id = ${option.tagId})"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,13 @@ import androidx.room.Query
|
||||
import androidx.room.RawQuery
|
||||
import androidx.room.Transaction
|
||||
import androidx.room.Upsert
|
||||
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||
import androidx.sqlite.db.SupportSQLiteQuery
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.koitharu.kotatsu.core.db.entity.toEntity
|
||||
import org.koitharu.kotatsu.core.db.MangaQueryBuilder
|
||||
import org.koitharu.kotatsu.list.domain.ListFilterOption
|
||||
|
||||
@Dao
|
||||
abstract class TracksDao {
|
||||
abstract class TracksDao : MangaQueryBuilder.ConditionCallback {
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM tracks ORDER BY last_check_time ASC LIMIT :limit OFFSET :offset")
|
||||
@@ -44,33 +43,17 @@ abstract class TracksDao {
|
||||
@Query("SELECT * FROM tracks WHERE chapters_new > 0 ORDER BY last_chapter_date DESC")
|
||||
abstract fun observeUpdatedManga(): Flow<List<MangaWithTrack>>
|
||||
|
||||
fun observeUpdatedManga(limit: Int, filterOptions: Set<ListFilterOption>): Flow<List<MangaWithTrack>> {
|
||||
val query = buildString {
|
||||
append("SELECT * FROM tracks WHERE chapters_new > 0")
|
||||
if (filterOptions.isNotEmpty()) {
|
||||
val groupedOptions = filterOptions.groupBy { it.groupKey }
|
||||
for ((_, group) in groupedOptions) {
|
||||
if (group.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
append(" AND ")
|
||||
if (group.size > 1) {
|
||||
group.joinTo(this, separator = " OR ", prefix = "(", postfix = ")") {
|
||||
it.getCondition()
|
||||
}
|
||||
} else {
|
||||
append(group.single().getCondition())
|
||||
}
|
||||
}
|
||||
}
|
||||
append(" ORDER BY last_chapter_date DESC")
|
||||
if (limit > 0) {
|
||||
append(" LIMIT ")
|
||||
append(limit)
|
||||
}
|
||||
}
|
||||
return observeMangaImpl(SimpleSQLiteQuery(query))
|
||||
}
|
||||
fun observeUpdatedManga(
|
||||
limit: Int,
|
||||
filterOptions: Set<ListFilterOption>,
|
||||
): Flow<List<MangaWithTrack>> = observeMangaImpl(
|
||||
MangaQueryBuilder("tracks", this)
|
||||
.where("chapters_new > 0")
|
||||
.filters(filterOptions)
|
||||
.limit(limit)
|
||||
.orderBy("last_chapter_date DESC")
|
||||
.build(),
|
||||
)
|
||||
|
||||
@Query("DELETE FROM tracks")
|
||||
abstract suspend fun clear()
|
||||
@@ -94,10 +77,10 @@ abstract class TracksDao {
|
||||
@RawQuery(observedEntities = [TrackEntity::class])
|
||||
protected abstract fun observeMangaImpl(query: SupportSQLiteQuery): Flow<List<MangaWithTrack>>
|
||||
|
||||
private fun ListFilterOption.getCondition(): String = when (this) {
|
||||
override fun getCondition(option: ListFilterOption): String? = when (option) {
|
||||
ListFilterOption.Macro.FAVORITE -> "EXISTS(SELECT * FROM favourites WHERE favourites.manga_id = tracks.manga_id)"
|
||||
is ListFilterOption.Favorite -> "EXISTS(SELECT * FROM favourites WHERE favourites.manga_id = tracks.manga_id AND favourites.category_id = ${category.id})"
|
||||
is ListFilterOption.Tag -> "EXISTS(SELECT * FROM manga_tags WHERE manga_tags.manga_id = tracks.manga_id AND tag_id = ${tag.toEntity().id})"
|
||||
else -> throw IllegalArgumentException("Unsupported option $this")
|
||||
is ListFilterOption.Favorite -> "EXISTS(SELECT * FROM favourites WHERE favourites.manga_id = tracks.manga_id AND favourites.category_id = ${option.category.id})"
|
||||
is ListFilterOption.Tag -> "EXISTS(SELECT * FROM manga_tags WHERE manga_tags.manga_id = tracks.manga_id AND tag_id = ${option.tagId})"
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user