feat(api): search authors by name and role

This commit is contained in:
Gauthier Roebroeck 2021-05-31 17:54:34 +08:00
parent c0d7be9627
commit a45a73c8bd
3 changed files with 119 additions and 13 deletions

View file

@ -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>

View file

@ -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)

View file

@ -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?,