diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/SqliteUdfDataSource.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/SqliteUdfDataSource.kt index d4475040..7d6db1d7 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/SqliteUdfDataSource.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/datasource/SqliteUdfDataSource.kt @@ -3,6 +3,7 @@ package org.gotson.komga.infrastructure.datasource import com.ibm.icu.text.Collator import io.github.oshai.kotlinlogging.KotlinLogging import org.gotson.komga.infrastructure.unicode.Collators +import org.gotson.komga.language.stripAccents import org.sqlite.Collation import org.sqlite.Function import org.sqlite.SQLiteConnection @@ -13,6 +14,7 @@ private val log = KotlinLogging.logger {} class SqliteUdfDataSource : SQLiteDataSource() { companion object { + const val UDF_STRIP_ACCENTS = "UDF_STRIP_ACCENTS" const val COLLATION_UNICODE_1 = "COLLATION_UNICODE_1" const val COLLATION_UNICODE_3 = "COLLATION_UNICODE_3" } @@ -26,6 +28,7 @@ class SqliteUdfDataSource : SQLiteDataSource() { private fun addAllUdf(connection: SQLiteConnection) { createUdfRegexp(connection) + createUdfStripAccents(connection) createUnicodeCollation(connection, COLLATION_UNICODE_3, Collators.collator3) createUnicodeCollation(connection, COLLATION_UNICODE_1, Collators.collator1) } @@ -46,6 +49,21 @@ class SqliteUdfDataSource : SQLiteDataSource() { ) } + private fun createUdfStripAccents(connection: SQLiteConnection) { + log.debug { "Adding custom $UDF_STRIP_ACCENTS function" } + Function.create( + connection, + UDF_STRIP_ACCENTS, + object : Function() { + override fun xFunc() = + when (val text = value_text(0)) { + null -> error("Argument must not be null") + else -> result(text.stripAccents()) + } + }, + ) + } + private fun createUnicodeCollation( connection: SQLiteConnection, collationName: String, diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookSearchHelper.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookSearchHelper.kt index 12ba9663..316531d5 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookSearchHelper.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookSearchHelper.kt @@ -146,7 +146,11 @@ class BookSearchHelper( DSL .select(Tables.BOOK_METADATA_TAG.BOOK_ID) .from(Tables.BOOK_METADATA_TAG) - .where(Tables.BOOK_METADATA_TAG.TAG.unicode1().equal(tag)) + .where( + Tables.BOOK_METADATA_TAG.TAG + .unicode1() + .equal(tag), + ) } val innerAny = { DSL @@ -170,8 +174,21 @@ class BookSearchHelper( .select(Tables.BOOK_METADATA_AUTHOR.BOOK_ID) .from(Tables.BOOK_METADATA_AUTHOR) .where(DSL.noCondition()) - .apply { if (name != null) and(Tables.BOOK_METADATA_AUTHOR.NAME.unicode1().equal(name)) } - .apply { if (role != null) and(Tables.BOOK_METADATA_AUTHOR.ROLE.unicode1().equal(role)) } + .apply { + if (name != null) + and( + Tables.BOOK_METADATA_AUTHOR.NAME + .unicode1() + .equal(name), + ) + }.apply { + if (role != null) + and( + Tables.BOOK_METADATA_AUTHOR.ROLE + .unicode1() + .equal(role), + ) + } } when (searchCondition.operator) { is SearchOperator.Is -> { diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesSearchHelper.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesSearchHelper.kt index 07f79dec..7bbfc0dd 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesSearchHelper.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesSearchHelper.kt @@ -6,7 +6,6 @@ import org.gotson.komga.domain.model.SearchCondition import org.gotson.komga.domain.model.SearchContext import org.gotson.komga.domain.model.SearchOperator import org.gotson.komga.domain.model.SeriesMetadata -import org.gotson.komga.infrastructure.datasource.SqliteUdfDataSource import org.gotson.komga.jooq.main.Tables import org.jooq.Condition import org.jooq.impl.DSL @@ -101,12 +100,19 @@ class SeriesSearchHelper( DSL .select(Tables.SERIES_METADATA_TAG.SERIES_ID) .from(Tables.SERIES_METADATA_TAG) - .where(Tables.SERIES_METADATA_TAG.TAG.unicode1().equal(tag)) - .union( + .where( + Tables.SERIES_METADATA_TAG.TAG + .unicode1() + .equal(tag), + ).union( DSL .select(Tables.BOOK_METADATA_AGGREGATION_TAG.SERIES_ID) .from(Tables.BOOK_METADATA_AGGREGATION_TAG) - .where(Tables.BOOK_METADATA_AGGREGATION_TAG.TAG.unicode1().equal(tag)), + .where( + Tables.BOOK_METADATA_AGGREGATION_TAG.TAG + .unicode1() + .equal(tag), + ), ) } val innerAny = { @@ -141,17 +147,15 @@ class SeriesSearchHelper( if (name != null) and( Tables.BOOK_METADATA_AGGREGATION_AUTHOR.NAME - .collate( - SqliteUdfDataSource.COLLATION_UNICODE_3, - ).equalIgnoreCase(name), + .unicode1() + .equal(name), ) }.apply { if (role != null) and( Tables.BOOK_METADATA_AGGREGATION_AUTHOR.ROLE - .collate( - SqliteUdfDataSource.COLLATION_UNICODE_3, - ).equalIgnoreCase(role), + .unicode1() + .equal(role), ) } } @@ -209,7 +213,11 @@ class SeriesSearchHelper( DSL .select(Tables.SERIES_METADATA_GENRE.SERIES_ID) .from(Tables.SERIES_METADATA_GENRE) - .where(Tables.SERIES_METADATA_GENRE.GENRE.unicode1().equal(genre)) + .where( + Tables.SERIES_METADATA_GENRE.GENRE + .unicode1() + .equal(genre), + ) } val innerAny = { DSL @@ -236,7 +244,11 @@ class SeriesSearchHelper( DSL .select(Tables.SERIES_METADATA_SHARING.SERIES_ID) .from(Tables.SERIES_METADATA_SHARING) - .where(Tables.SERIES_METADATA_SHARING.LABEL.unicode1().equal(label)) + .where( + Tables.SERIES_METADATA_SHARING.LABEL + .unicode1() + .equal(label), + ) } val innerAny = { DSL diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/Utils.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/Utils.kt index b7bd7f9d..efeb9802 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/Utils.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/Utils.kt @@ -17,10 +17,15 @@ import java.util.zip.GZIPOutputStream fun Field.noCase() = this.collate("NOCASE") +/** + * Warning: SQLite doesn't use collations with LIKE + */ fun Field.unicode1() = this.collate(SqliteUdfDataSource.COLLATION_UNICODE_1) fun Field.unicode3() = this.collate(SqliteUdfDataSource.COLLATION_UNICODE_3) +fun Field.udfStripAccents() = DSL.function(SqliteUdfDataSource.UDF_STRIP_ACCENTS, String::class.java, this) + fun Sort.toOrderBy(sorts: Map>): List> = this.mapNotNull { it.toSortField(sorts) diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDao.kt index dd61173c..a6b51911 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/main/ReferentialDao.kt @@ -49,7 +49,7 @@ class ReferentialDao( .selectDistinct(a.NAME, a.ROLE) .from(a) .apply { filterOnLibraryIds?.let { leftJoin(b).on(a.BOOK_ID.eq(b.ID)) } } - .where(a.NAME.unicode1().contains(search)) + .where(a.NAME.udfStripAccents().contains(search.stripAccents())) .apply { filterOnLibraryIds?.let { and(b.LIBRARY_ID.`in`(it)) } } .orderBy(a.NAME.unicode3()) .fetchInto(a) @@ -65,7 +65,7 @@ class ReferentialDao( .from(bmaa) .leftJoin(s) .on(bmaa.SERIES_ID.eq(s.ID)) - .where(bmaa.NAME.unicode1().contains(search)) + .where(bmaa.NAME.udfStripAccents().contains(search.stripAccents())) .and(s.LIBRARY_ID.eq(libraryId)) .apply { filterOnLibraryIds?.let { and(s.LIBRARY_ID.`in`(it)) } } .orderBy(bmaa.NAME.unicode3()) @@ -83,7 +83,7 @@ class ReferentialDao( .leftJoin(cs) .on(bmaa.SERIES_ID.eq(cs.SERIES_ID)) .apply { filterOnLibraryIds?.let { leftJoin(s).on(bmaa.SERIES_ID.eq(s.ID)) } } - .where(bmaa.NAME.unicode1().contains(search)) + .where(bmaa.NAME.udfStripAccents().contains(search.stripAccents())) .and(cs.COLLECTION_ID.eq(collectionId)) .apply { filterOnLibraryIds?.let { and(s.LIBRARY_ID.`in`(it)) } } .orderBy(bmaa.NAME.unicode3()) @@ -99,7 +99,7 @@ class ReferentialDao( .selectDistinct(bmaa.NAME, bmaa.ROLE) .from(bmaa) .apply { filterOnLibraryIds?.let { leftJoin(s).on(bmaa.SERIES_ID.eq(s.ID)) } } - .where(bmaa.NAME.unicode1().contains(search)) + .where(bmaa.NAME.udfStripAccents().contains(search.stripAccents())) .and(bmaa.SERIES_ID.eq(seriesId)) .apply { filterOnLibraryIds?.let { and(s.LIBRARY_ID.`in`(it)) } } .orderBy(bmaa.NAME.unicode3()) @@ -220,7 +220,7 @@ class ReferentialDao( .selectDistinct(a.NAME) .from(a) .apply { filterOnLibraryIds?.let { leftJoin(b).on(a.BOOK_ID.eq(b.ID)) } } - .where(a.NAME.unicode1().contains(search)) + .where(a.NAME.udfStripAccents().contains(search.stripAccents())) .apply { filterOnLibraryIds?.let { and(b.LIBRARY_ID.`in`(it)) } } .orderBy(a.NAME.unicode3()) .fetch(a.NAME)