fix: don't strip accents on input data and sort series title with unicode collation

This commit is contained in:
Gauthier Roebroeck 2025-06-30 10:13:42 +08:00
parent 860274079d
commit c2c697fba7
11 changed files with 37 additions and 39 deletions

View file

@ -155,7 +155,7 @@ class SeriesLifecycle(
seriesMetadataRepository.insert(
SeriesMetadata(
title = series.name,
titleSort = series.name.stripAccents(),
titleSort = series.name,
seriesId = series.id,
),
)

View file

@ -72,7 +72,7 @@ class BookDtoDao(
private val sorts =
mapOf(
"name" to b.NAME.collate(SqliteUdfDataSource.COLLATION_UNICODE_3),
"series" to sd.TITLE_SORT.noCase(),
"series" to sd.TITLE_SORT.collate(SqliteUdfDataSource.COLLATION_UNICODE_3),
"created" to b.CREATED_DATE,
"createdDate" to b.CREATED_DATE,
"lastModified" to b.LAST_MODIFIED_DATE,

View file

@ -10,7 +10,6 @@ import org.gotson.komga.infrastructure.jooq.SeriesSearchHelper
import org.gotson.komga.infrastructure.jooq.csAlias
import org.gotson.komga.infrastructure.jooq.inOrNoCondition
import org.gotson.komga.infrastructure.jooq.insertTempStrings
import org.gotson.komga.infrastructure.jooq.noCase
import org.gotson.komga.infrastructure.jooq.selectTempStrings
import org.gotson.komga.infrastructure.jooq.sortByValues
import org.gotson.komga.infrastructure.jooq.toSortField
@ -85,7 +84,7 @@ class SeriesDtoDao(
private val sorts =
mapOf(
"metadata.titleSort" to d.TITLE_SORT.noCase(),
"metadata.titleSort" to d.TITLE_SORT.collate(SqliteUdfDataSource.COLLATION_UNICODE_3),
"createdDate" to s.CREATED_DATE,
"created" to s.CREATED_DATE,
"lastModifiedDate" to s.LAST_MODIFIED_DATE,

View file

@ -18,7 +18,6 @@ import org.gotson.komga.infrastructure.metadata.BookMetadataProvider
import org.gotson.komga.infrastructure.metadata.SeriesMetadataFromBookProvider
import org.gotson.komga.infrastructure.metadata.comicrack.dto.ComicInfo
import org.gotson.komga.infrastructure.metadata.comicrack.dto.Manga
import org.gotson.komga.language.stripAccents
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import java.net.URI
@ -142,7 +141,7 @@ class ComicInfoProvider(
return SeriesMetadataPatch(
title = series,
titleSort = series?.stripAccents(),
titleSort = series,
status = null,
summary = null,
readingDirection = readingDirection,

View file

@ -14,7 +14,6 @@ import org.gotson.komga.domain.model.SeriesMetadataPatch
import org.gotson.komga.infrastructure.mediacontainer.epub.getPackageFileContent
import org.gotson.komga.infrastructure.metadata.BookMetadataProvider
import org.gotson.komga.infrastructure.metadata.SeriesMetadataFromBookProvider
import org.gotson.komga.language.stripAccents
import org.jsoup.Jsoup
import org.jsoup.parser.Parser
import org.jsoup.safety.Safelist
@ -138,7 +137,7 @@ class EpubMetadataProvider(
return SeriesMetadataPatch(
title = series,
titleSort = series?.stripAccents(),
titleSort = series,
status = null,
readingDirection = direction,
publisher = publisher,

View file

@ -11,7 +11,6 @@ import org.gotson.komga.domain.model.Sidecar
import org.gotson.komga.infrastructure.metadata.SeriesMetadataProvider
import org.gotson.komga.infrastructure.metadata.mylar.dto.Status
import org.gotson.komga.infrastructure.sidecar.SidecarSeriesConsumer
import org.gotson.komga.language.stripAccents
import org.springframework.stereotype.Service
import kotlin.io.path.notExists
import org.gotson.komga.infrastructure.metadata.mylar.dto.Series as MylarSeries
@ -47,7 +46,7 @@ class MylarSeriesProvider(
return SeriesMetadataPatch(
title = title,
titleSort = title.stripAccents(),
titleSort = title,
status =
when (metadata.status) {
Status.Ended -> SeriesMetadata.Status.ENDED

View file

@ -46,6 +46,9 @@ fun LocalDateTime.notEquals(
precision: TemporalUnit = ChronoUnit.MILLIS,
) = this.truncatedTo(precision) != other.truncatedTo(precision)
/**
* Warning: This affects the Unicode code points of Korean Hangul.
*/
fun String.stripAccents(): String = StringUtils.stripAccents(this)
fun LocalDate.toDate(): Date = Date.from(this.atStartOfDay(ZoneId.of("Z")).toInstant())

View file

@ -168,33 +168,6 @@ class SeriesLifecycleTest(
assertThat(savedBooks.map { it.number }).containsExactly(1, 2, 3, 4, 5)
}
@Test
fun `given series name with diacritics when creating series then diacritics are stripped from metadata titlesort`() {
// given
val series1 = makeSeries("À l'assaut", library.id)
val series2 = makeSeries("Être ou ne pas être", library.id)
val series3 = makeSeries("Écarlate", library.id)
// when
val created1 = seriesLifecycle.createSeries(series1)
val created2 = seriesLifecycle.createSeries(series2)
val created3 = seriesLifecycle.createSeries(series3)
// then
with(seriesMetadataRepository.findById(created1.id)) {
assertThat(title).isEqualTo(series1.name)
assertThat(titleSort).isEqualTo("A l'assaut")
}
with(seriesMetadataRepository.findById(created2.id)) {
assertThat(title).isEqualTo(series2.name)
assertThat(titleSort).isEqualTo("Etre ou ne pas etre")
}
with(seriesMetadataRepository.findById(created3.id)) {
assertThat(title).isEqualTo(series3.name)
assertThat(titleSort).isEqualTo("Ecarlate")
}
}
@Nested
inner class Transactions {
@Test

View file

@ -127,6 +127,32 @@ class SeriesDtoDaoTest(
}
}
@Nested
inner class SortCriteria {
@Test
fun `given series when sorting by title sort then results are ordered`() {
// given
seriesLifecycle.createSeries(makeSeries("Éb", library.id))
seriesLifecycle.createSeries(makeSeries("Ea", library.id))
seriesLifecycle.createSeries(makeSeries("Ec", library.id))
searchIndexLifecycle.rebuildIndex()
Thread.sleep(500) // index rebuild is done asynchronously, and need a slight delay to be updated
// when
val found =
seriesDtoDao
.findAll(
SeriesSearch(),
SearchContext(user),
UnpagedSorted(Sort.by("metadata.titleSort")),
).content
// then
assertThat(found.map { it.metadata.title }).containsExactly("Ea", "Éb", "Ec")
}
}
@Nested
inner class ReadProgress {
@Test

View file

@ -389,7 +389,7 @@ class ComicInfoProviderTest {
with(patch) {
assertThat(title).isEqualTo("séries")
assertThat(titleSort).isEqualTo("series")
assertThat(titleSort).isEqualTo("séries")
assertThat(status).isNull()
assertThat(collections).containsExactlyInAnyOrder("collections", "multiple")
assertThat(publisher).isEqualTo("publisher")

View file

@ -60,7 +60,7 @@ class MylarSeriesProviderTest {
with(patch) {
assertThat(title).isEqualTo("Sàndman")
assertThat(titleSort).isEqualTo("Sandman")
assertThat(titleSort).isEqualTo("Sàndman")
assertThat(status).isEqualTo(SeriesMetadata.Status.ENDED)
assertThat(summary).isEqualTo("Sandman comics formatted")
assertThat(readingDirection).isNull()