diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt index acab7ca3c..cf7d10984 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/SeriesDtoDao.kt @@ -6,6 +6,7 @@ import org.gotson.komga.domain.model.SeriesSearchWithReadProgress import org.gotson.komga.infrastructure.web.toFilePath import org.gotson.komga.interfaces.rest.dto.AuthorDto import org.gotson.komga.interfaces.rest.dto.BookMetadataAggregationDto +import org.gotson.komga.interfaces.rest.dto.GroupCountDto import org.gotson.komga.interfaces.rest.dto.SeriesDto import org.gotson.komga.interfaces.rest.dto.SeriesMetadataDto import org.gotson.komga.interfaces.rest.persistence.SeriesDtoRepository @@ -20,7 +21,9 @@ import org.jooq.Record import org.jooq.ResultQuery import org.jooq.SelectOnConditionStep import org.jooq.impl.DSL +import org.jooq.impl.DSL.count import org.jooq.impl.DSL.lower +import org.jooq.impl.DSL.substring import org.springframework.data.domain.Page import org.springframework.data.domain.PageImpl import org.springframework.data.domain.PageRequest @@ -103,6 +106,27 @@ class SeriesDtoDao( return findAll(conditions, userId, pageable, search.toJoinConditions()) } + override fun countByFirstCharacter(search: SeriesSearchWithReadProgress, userId: String): List { + val conditions = search.toCondition() + val joinConditions = search.toJoinConditions() + + val firstChar = lower(substring(d.TITLE_SORT, 1, 1)) + return dsl.select(firstChar, count()) + .from(s) + .leftJoin(d).on(s.ID.eq(d.SERIES_ID)) + .leftJoin(bma).on(s.ID.eq(bma.SERIES_ID)) + .leftJoin(rs).on(s.ID.eq(rs.SERIES_ID)).and(readProgressConditionSeries(userId)) + .apply { if (joinConditions.genre) leftJoin(g).on(s.ID.eq(g.SERIES_ID)) } + .apply { if (joinConditions.tag) leftJoin(st).on(s.ID.eq(st.SERIES_ID)) } + .apply { if (joinConditions.collection) leftJoin(cs).on(s.ID.eq(cs.SERIES_ID)) } + .apply { if (joinConditions.aggregationAuthor) leftJoin(bmaa).on(s.ID.eq(bmaa.SERIES_ID)) } + .where(conditions) + .groupBy(firstChar) + .map { + GroupCountDto(it.value1(), it.value2()) + } + } + override fun findByIdOrNull(seriesId: String, userId: String): SeriesDto? = selectBase(userId) .where(s.ID.eq(seriesId)) diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/SeriesController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/SeriesController.kt index c07efccba..e48c6b285 100644 --- a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/SeriesController.kt +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/SeriesController.kt @@ -39,6 +39,7 @@ import org.gotson.komga.infrastructure.web.Authors import org.gotson.komga.infrastructure.web.DelimitedPair import org.gotson.komga.interfaces.rest.dto.BookDto import org.gotson.komga.interfaces.rest.dto.CollectionDto +import org.gotson.komga.interfaces.rest.dto.GroupCountDto import org.gotson.komga.interfaces.rest.dto.SeriesDto import org.gotson.komga.interfaces.rest.dto.SeriesMetadataUpdateDto import org.gotson.komga.interfaces.rest.dto.TachiyomiReadProgressDto @@ -162,6 +163,58 @@ class SeriesController( .map { it.restrictUrl(!principal.user.roleAdmin) } } + @AuthorsAsQueryParam + @Parameters( + Parameter( + description = "Search by regex criteria, in the form: regex,field. Supported fields are TITLE and TITLE_SORT.", + `in` = ParameterIn.QUERY, name = "search_regex", schema = Schema(type = "string") + ) + ) + @GetMapping("alphabetical-groups") + fun getAlphabeticalGroups( + @AuthenticationPrincipal principal: KomgaPrincipal, + @RequestParam(name = "search", required = false) searchTerm: String?, + @Parameter(hidden = true) @DelimitedPair("search_regex") searchRegex: Pair?, + @RequestParam(name = "library_id", required = false) libraryIds: List?, + @RequestParam(name = "collection_id", required = false) collectionIds: List?, + @RequestParam(name = "status", required = false) metadataStatus: List?, + @RequestParam(name = "read_status", required = false) readStatus: List?, + @RequestParam(name = "publisher", required = false) publishers: List?, + @RequestParam(name = "language", required = false) languages: List?, + @RequestParam(name = "genre", required = false) genres: List?, + @RequestParam(name = "tag", required = false) tags: List?, + @RequestParam(name = "age_rating", required = false) ageRatings: List?, + @RequestParam(name = "release_year", required = false) release_years: List?, + @RequestParam(name = "deleted", required = false) deleted: Boolean?, + @Parameter(hidden = true) @Authors authors: List?, + @Parameter(hidden = true) page: Pageable + ): List { + val seriesSearch = SeriesSearchWithReadProgress( + libraryIds = principal.user.getAuthorizedLibraryIds(libraryIds), + collectionIds = collectionIds, + searchTerm = searchTerm, + searchRegex = searchRegex?.let { + when (it.second.lowercase()) { + "title" -> Pair(it.first, SeriesSearch.SearchField.TITLE) + "title_sort" -> Pair(it.first, SeriesSearch.SearchField.TITLE_SORT) + else -> null + } + }, + metadataStatus = metadataStatus, + readStatus = readStatus, + publishers = publishers, + deleted = deleted, + languages = languages, + genres = genres, + tags = tags, + ageRatings = ageRatings?.map { it.toIntOrNull() }, + releaseYears = release_years, + authors = authors + ) + + return seriesDtoRepository.countByFirstCharacter(seriesSearch, principal.user.id) + } + @Operation(description = "Return recently added or updated series.") @PageableWithoutSortAsQueryParam @GetMapping("/latest") diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/dto/GroupCountDto.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/dto/GroupCountDto.kt new file mode 100644 index 000000000..f8fc373bf --- /dev/null +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/dto/GroupCountDto.kt @@ -0,0 +1,6 @@ +package org.gotson.komga.interfaces.rest.dto + +data class GroupCountDto( + val group: String, + val count: Int, +) diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/persistence/SeriesDtoRepository.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/persistence/SeriesDtoRepository.kt index ae90cd91b..dd794526d 100644 --- a/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/persistence/SeriesDtoRepository.kt +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/rest/persistence/SeriesDtoRepository.kt @@ -1,6 +1,7 @@ package org.gotson.komga.interfaces.rest.persistence import org.gotson.komga.domain.model.SeriesSearchWithReadProgress +import org.gotson.komga.interfaces.rest.dto.GroupCountDto import org.gotson.komga.interfaces.rest.dto.SeriesDto import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable @@ -11,4 +12,6 @@ interface SeriesDtoRepository { fun findAll(search: SeriesSearchWithReadProgress, userId: String, pageable: Pageable): Page fun findAllByCollectionId(collectionId: String, search: SeriesSearchWithReadProgress, userId: String, pageable: Pageable): Page fun findAllRecentlyUpdated(search: SeriesSearchWithReadProgress, userId: String, pageable: Pageable): Page + + fun countByFirstCharacter(search: SeriesSearchWithReadProgress, userId: String): List }