mirror of
https://github.com/gotson/komga.git
synced 2026-05-08 12:35:30 +02:00
feat(api): search authors by name and role
This commit is contained in:
parent
c0d7be9627
commit
a45a73c8bd
3 changed files with 119 additions and 13 deletions
|
|
@ -1,6 +1,8 @@
|
||||||
package org.gotson.komga.domain.persistence
|
package org.gotson.komga.domain.persistence
|
||||||
|
|
||||||
import org.gotson.komga.domain.model.Author
|
import org.gotson.komga.domain.model.Author
|
||||||
|
import org.springframework.data.domain.Page
|
||||||
|
import org.springframework.data.domain.Pageable
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
interface ReferentialRepository {
|
interface ReferentialRepository {
|
||||||
|
|
@ -11,6 +13,11 @@ interface ReferentialRepository {
|
||||||
fun findAuthorsNamesByName(search: String, filterOnLibraryIds: Collection<String>?): List<String>
|
fun findAuthorsNamesByName(search: String, filterOnLibraryIds: Collection<String>?): List<String>
|
||||||
fun findAuthorsRoles(filterOnLibraryIds: Collection<String>?): List<String>
|
fun findAuthorsRoles(filterOnLibraryIds: Collection<String>?): List<String>
|
||||||
|
|
||||||
|
fun findAuthorsByName(search: String, role: String?, filterOnLibraryIds: Collection<String>?, pageable: Pageable): Page<Author>
|
||||||
|
fun findAuthorsByNameAndLibrary(search: String, role: String?, libraryId: String, filterOnLibraryIds: Collection<String>?, pageable: Pageable): Page<Author>
|
||||||
|
fun findAuthorsByNameAndCollection(search: String, role: String?, collectionId: String, filterOnLibraryIds: Collection<String>?, pageable: Pageable): Page<Author>
|
||||||
|
fun findAuthorsByNameAndSeries(search: String, role: String?, seriesId: String, filterOnLibraryIds: Collection<String>?, pageable: Pageable): Page<Author>
|
||||||
|
|
||||||
fun findAllGenres(filterOnLibraryIds: Collection<String>?): Set<String>
|
fun findAllGenres(filterOnLibraryIds: Collection<String>?): Set<String>
|
||||||
fun findAllGenresByLibrary(libraryId: String, filterOnLibraryIds: Collection<String>?): Set<String>
|
fun findAllGenresByLibrary(libraryId: String, filterOnLibraryIds: Collection<String>?): Set<String>
|
||||||
fun findAllGenresByCollection(collectionId: String, filterOnLibraryIds: Collection<String>?): Set<String>
|
fun findAllGenresByCollection(collectionId: String, filterOnLibraryIds: Collection<String>?): Set<String>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,11 @@ import org.gotson.komga.jooq.tables.records.BookMetadataAuthorRecord
|
||||||
import org.jooq.DSLContext
|
import org.jooq.DSLContext
|
||||||
import org.jooq.impl.DSL.lower
|
import org.jooq.impl.DSL.lower
|
||||||
import org.jooq.impl.DSL.select
|
import org.jooq.impl.DSL.select
|
||||||
|
import org.springframework.data.domain.Page
|
||||||
|
import org.springframework.data.domain.PageImpl
|
||||||
|
import org.springframework.data.domain.PageRequest
|
||||||
|
import org.springframework.data.domain.Pageable
|
||||||
|
import org.springframework.data.domain.Sort
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
|
||||||
|
|
@ -71,6 +76,68 @@ class ReferentialDao(
|
||||||
.fetchInto(bmaa)
|
.fetchInto(bmaa)
|
||||||
.map { it.toDomain() }
|
.map { it.toDomain() }
|
||||||
|
|
||||||
|
override fun findAuthorsByName(search: String, role: String?, filterOnLibraryIds: Collection<String>?, pageable: Pageable): Page<Author> {
|
||||||
|
return findAuthorsByName(search, role, filterOnLibraryIds, pageable, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findAuthorsByNameAndLibrary(search: String, role: String?, libraryId: String, filterOnLibraryIds: Collection<String>?, pageable: Pageable): Page<Author> {
|
||||||
|
return findAuthorsByName(search, role, filterOnLibraryIds, pageable, FilterBy(FilterByType.LIBRARY, libraryId))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findAuthorsByNameAndCollection(search: String, role: String?, collectionId: String, filterOnLibraryIds: Collection<String>?, pageable: Pageable): Page<Author> {
|
||||||
|
return findAuthorsByName(search, role, filterOnLibraryIds, pageable, FilterBy(FilterByType.COLLECTION, collectionId))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun findAuthorsByNameAndSeries(search: String, role: String?, seriesId: String, filterOnLibraryIds: Collection<String>?, pageable: Pageable): Page<Author> {
|
||||||
|
return findAuthorsByName(search, role, filterOnLibraryIds, pageable, FilterBy(FilterByType.SERIES, seriesId))
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum class FilterByType {
|
||||||
|
LIBRARY,
|
||||||
|
COLLECTION,
|
||||||
|
SERIES,
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class FilterBy(
|
||||||
|
val type: FilterByType,
|
||||||
|
val id: String,
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun findAuthorsByName(search: String, role: String?, filterOnLibraryIds: Collection<String>?, pageable: Pageable, filterBy: FilterBy?): Page<Author> {
|
||||||
|
val query = dsl.selectDistinct(bmaa.NAME, bmaa.ROLE)
|
||||||
|
.from(bmaa)
|
||||||
|
.apply { if (filterOnLibraryIds != null || filterBy?.type == FilterByType.LIBRARY) leftJoin(s).on(bmaa.SERIES_ID.eq(s.ID)) }
|
||||||
|
.apply { if (filterBy?.type == FilterByType.COLLECTION) leftJoin(cs).on(bmaa.SERIES_ID.eq(cs.SERIES_ID)) }
|
||||||
|
.where(bmaa.NAME.containsIgnoreCase(search))
|
||||||
|
.apply { role?.let { and(bmaa.ROLE.eq(role)) } }
|
||||||
|
.apply { filterOnLibraryIds?.let { and(s.LIBRARY_ID.`in`(it)) } }
|
||||||
|
.apply {
|
||||||
|
filterBy?.let {
|
||||||
|
when (it.type) {
|
||||||
|
FilterByType.LIBRARY -> and(s.LIBRARY_ID.eq(it.id))
|
||||||
|
FilterByType.COLLECTION -> and(cs.COLLECTION_ID.eq(it.id))
|
||||||
|
FilterByType.SERIES -> and(bmaa.SERIES_ID.eq(it.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val count = dsl.fetchCount(query)
|
||||||
|
|
||||||
|
val items = query
|
||||||
|
.orderBy(bmaa.NAME, bmaa.ROLE)
|
||||||
|
.apply { if (pageable.isPaged) limit(pageable.pageSize).offset(pageable.offset) }
|
||||||
|
.fetchInto(a)
|
||||||
|
.map { it.toDomain() }
|
||||||
|
|
||||||
|
val pageSort = Sort.by("name")
|
||||||
|
return PageImpl(
|
||||||
|
items,
|
||||||
|
if (pageable.isPaged) PageRequest.of(pageable.pageNumber, pageable.pageSize, pageSort)
|
||||||
|
else PageRequest.of(0, maxOf(count, 20), pageSort),
|
||||||
|
count.toLong()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun findAuthorsNamesByName(search: String, filterOnLibraryIds: Collection<String>?): List<String> =
|
override fun findAuthorsNamesByName(search: String, filterOnLibraryIds: Collection<String>?): List<String> =
|
||||||
dsl.selectDistinct(a.NAME)
|
dsl.selectDistinct(a.NAME)
|
||||||
.from(a)
|
.from(a)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
package org.gotson.komga.interfaces.rest
|
package org.gotson.komga.interfaces.rest
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter
|
||||||
import org.gotson.komga.domain.persistence.ReferentialRepository
|
import org.gotson.komga.domain.persistence.ReferentialRepository
|
||||||
import org.gotson.komga.infrastructure.security.KomgaPrincipal
|
import org.gotson.komga.infrastructure.security.KomgaPrincipal
|
||||||
|
import org.gotson.komga.infrastructure.swagger.PageableWithoutSortAsQueryParam
|
||||||
import org.gotson.komga.interfaces.rest.dto.AuthorDto
|
import org.gotson.komga.interfaces.rest.dto.AuthorDto
|
||||||
import org.gotson.komga.interfaces.rest.dto.toDto
|
import org.gotson.komga.interfaces.rest.dto.toDto
|
||||||
|
import org.springframework.data.domain.Page
|
||||||
|
import org.springframework.data.domain.PageRequest
|
||||||
|
import org.springframework.data.domain.Pageable
|
||||||
import org.springframework.http.MediaType
|
import org.springframework.http.MediaType
|
||||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
|
@ -12,13 +17,13 @@ import org.springframework.web.bind.annotation.RequestParam
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("api/v1", produces = [MediaType.APPLICATION_JSON_VALUE])
|
@RequestMapping("api", produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||||
class ReferentialController(
|
class ReferentialController(
|
||||||
private val referentialRepository: ReferentialRepository
|
private val referentialRepository: ReferentialRepository
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@GetMapping("/authors")
|
@GetMapping("v1/authors")
|
||||||
fun getAuthors(
|
fun getAuthorsV1(
|
||||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||||
@RequestParam(name = "search", defaultValue = "") search: String,
|
@RequestParam(name = "search", defaultValue = "") search: String,
|
||||||
@RequestParam(name = "library_id", required = false) libraryId: String?,
|
@RequestParam(name = "library_id", required = false) libraryId: String?,
|
||||||
|
|
@ -33,20 +38,47 @@ class ReferentialController(
|
||||||
else -> referentialRepository.findAuthorsByName(search, principal.user.getAuthorizedLibraryIds(null))
|
else -> referentialRepository.findAuthorsByName(search, principal.user.getAuthorizedLibraryIds(null))
|
||||||
}.map { it.toDto() }
|
}.map { it.toDto() }
|
||||||
|
|
||||||
@GetMapping("/authors/names")
|
@PageableWithoutSortAsQueryParam
|
||||||
|
@GetMapping("v2/authors")
|
||||||
|
fun getAuthors(
|
||||||
|
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||||
|
@RequestParam(name = "search", defaultValue = "") search: String,
|
||||||
|
@RequestParam(name = "role") role: String?,
|
||||||
|
@RequestParam(name = "library_id", required = false) libraryId: String?,
|
||||||
|
@RequestParam(name = "collection_id", required = false) collectionId: String?,
|
||||||
|
@RequestParam(name = "series_id", required = false) seriesId: String?,
|
||||||
|
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
|
||||||
|
@Parameter(hidden = true) page: Pageable,
|
||||||
|
): Page<AuthorDto> {
|
||||||
|
val pageRequest =
|
||||||
|
if (unpaged) Pageable.unpaged()
|
||||||
|
else PageRequest.of(
|
||||||
|
page.pageNumber,
|
||||||
|
page.pageSize,
|
||||||
|
)
|
||||||
|
|
||||||
|
return when {
|
||||||
|
libraryId != null -> referentialRepository.findAuthorsByNameAndLibrary(search, role, libraryId, principal.user.getAuthorizedLibraryIds(null), pageRequest)
|
||||||
|
collectionId != null -> referentialRepository.findAuthorsByNameAndCollection(search, role, collectionId, principal.user.getAuthorizedLibraryIds(null), pageRequest)
|
||||||
|
seriesId != null -> referentialRepository.findAuthorsByNameAndSeries(search, role, seriesId, principal.user.getAuthorizedLibraryIds(null), pageRequest)
|
||||||
|
else -> referentialRepository.findAuthorsByName(search, role, principal.user.getAuthorizedLibraryIds(null), pageRequest)
|
||||||
|
}.map { it.toDto() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("v1/authors/names")
|
||||||
fun getAuthorsNames(
|
fun getAuthorsNames(
|
||||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||||
@RequestParam(name = "search", defaultValue = "") search: String
|
@RequestParam(name = "search", defaultValue = "") search: String
|
||||||
): List<String> =
|
): List<String> =
|
||||||
referentialRepository.findAuthorsNamesByName(search, principal.user.getAuthorizedLibraryIds(null))
|
referentialRepository.findAuthorsNamesByName(search, principal.user.getAuthorizedLibraryIds(null))
|
||||||
|
|
||||||
@GetMapping("/authors/roles")
|
@GetMapping("v1/authors/roles")
|
||||||
fun getAuthorsRoles(
|
fun getAuthorsRoles(
|
||||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||||
): List<String> =
|
): List<String> =
|
||||||
referentialRepository.findAuthorsRoles(principal.user.getAuthorizedLibraryIds(null))
|
referentialRepository.findAuthorsRoles(principal.user.getAuthorizedLibraryIds(null))
|
||||||
|
|
||||||
@GetMapping("/genres")
|
@GetMapping("v1/genres")
|
||||||
fun getGenres(
|
fun getGenres(
|
||||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||||
@RequestParam(name = "library_id", required = false) libraryId: String?,
|
@RequestParam(name = "library_id", required = false) libraryId: String?,
|
||||||
|
|
@ -58,7 +90,7 @@ class ReferentialController(
|
||||||
else -> referentialRepository.findAllGenres(principal.user.getAuthorizedLibraryIds(null))
|
else -> referentialRepository.findAllGenres(principal.user.getAuthorizedLibraryIds(null))
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/tags")
|
@GetMapping("v1/tags")
|
||||||
fun getTags(
|
fun getTags(
|
||||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||||
// TODO: remove those parameters once Tachiyomi Extension is using the new /tags/series endpoint (changed in 0.87.4 - 21 Apr 2021)
|
// TODO: remove those parameters once Tachiyomi Extension is using the new /tags/series endpoint (changed in 0.87.4 - 21 Apr 2021)
|
||||||
|
|
@ -73,7 +105,7 @@ class ReferentialController(
|
||||||
else -> referentialRepository.findAllSeriesAndBookTags(principal.user.getAuthorizedLibraryIds(null))
|
else -> referentialRepository.findAllSeriesAndBookTags(principal.user.getAuthorizedLibraryIds(null))
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/tags/book")
|
@GetMapping("v1/tags/book")
|
||||||
fun getBookTags(
|
fun getBookTags(
|
||||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||||
@RequestParam(name = "series_id", required = false) seriesId: String?,
|
@RequestParam(name = "series_id", required = false) seriesId: String?,
|
||||||
|
|
@ -83,7 +115,7 @@ class ReferentialController(
|
||||||
else -> referentialRepository.findAllBookTags(principal.user.getAuthorizedLibraryIds(null))
|
else -> referentialRepository.findAllBookTags(principal.user.getAuthorizedLibraryIds(null))
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/tags/series")
|
@GetMapping("v1/tags/series")
|
||||||
fun getSeriesTags(
|
fun getSeriesTags(
|
||||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||||
@RequestParam(name = "library_id", required = false) libraryId: String?,
|
@RequestParam(name = "library_id", required = false) libraryId: String?,
|
||||||
|
|
@ -95,7 +127,7 @@ class ReferentialController(
|
||||||
else -> referentialRepository.findAllSeriesTags(principal.user.getAuthorizedLibraryIds(null))
|
else -> referentialRepository.findAllSeriesTags(principal.user.getAuthorizedLibraryIds(null))
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/languages")
|
@GetMapping("v1/languages")
|
||||||
fun getLanguages(
|
fun getLanguages(
|
||||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||||
@RequestParam(name = "library_id", required = false) libraryId: String?,
|
@RequestParam(name = "library_id", required = false) libraryId: String?,
|
||||||
|
|
@ -107,7 +139,7 @@ class ReferentialController(
|
||||||
else -> referentialRepository.findAllLanguages(principal.user.getAuthorizedLibraryIds(null))
|
else -> referentialRepository.findAllLanguages(principal.user.getAuthorizedLibraryIds(null))
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/publishers")
|
@GetMapping("v1/publishers")
|
||||||
fun getPublishers(
|
fun getPublishers(
|
||||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||||
@RequestParam(name = "library_id", required = false) libraryId: String?,
|
@RequestParam(name = "library_id", required = false) libraryId: String?,
|
||||||
|
|
@ -119,7 +151,7 @@ class ReferentialController(
|
||||||
else -> referentialRepository.findAllPublishers(principal.user.getAuthorizedLibraryIds(null))
|
else -> referentialRepository.findAllPublishers(principal.user.getAuthorizedLibraryIds(null))
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/age-ratings")
|
@GetMapping("v1/age-ratings")
|
||||||
fun getAgeRatings(
|
fun getAgeRatings(
|
||||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||||
@RequestParam(name = "library_id", required = false) libraryId: String?,
|
@RequestParam(name = "library_id", required = false) libraryId: String?,
|
||||||
|
|
@ -131,7 +163,7 @@ class ReferentialController(
|
||||||
else -> referentialRepository.findAllAgeRatings(principal.user.getAuthorizedLibraryIds(null))
|
else -> referentialRepository.findAllAgeRatings(principal.user.getAuthorizedLibraryIds(null))
|
||||||
}.map { it?.toString() ?: "None" }.toSet()
|
}.map { it?.toString() ?: "None" }.toSet()
|
||||||
|
|
||||||
@GetMapping("/series/release-dates")
|
@GetMapping("v1/series/release-dates")
|
||||||
fun getSeriesReleaseDates(
|
fun getSeriesReleaseDates(
|
||||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||||
@RequestParam(name = "library_id", required = false) libraryId: String?,
|
@RequestParam(name = "library_id", required = false) libraryId: String?,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue