mirror of
https://github.com/gotson/komga.git
synced 2026-02-14 19:33:02 +01:00
fix(api): pagination for collection's series
add unpaged parameter for SeriesController and SeriesCollectionController
This commit is contained in:
parent
b4a80248e3
commit
6dd070436b
13 changed files with 109 additions and 74 deletions
|
|
@ -106,7 +106,7 @@ export default Vue.extend({
|
|||
this.modal = val
|
||||
if (val) {
|
||||
this.newCollection = ''
|
||||
this.collections = (await this.$komgaCollections.getCollections(undefined, undefined, true)).content
|
||||
this.collections = (await this.$komgaCollections.getCollections(undefined, { unpaged: true } as PageRequest)).content
|
||||
}
|
||||
},
|
||||
modal (val) {
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ export default Vue.extend({
|
|||
async dialogReset (collection: CollectionDto) {
|
||||
this.form.name = collection.name
|
||||
this.form.ordered = collection.ordered
|
||||
this.collections = (await this.$komgaCollections.getCollections(undefined, undefined, true)).content
|
||||
this.collections = (await this.$komgaCollections.getCollections(undefined, { unpaged: true } as PageRequest)).content
|
||||
},
|
||||
dialogCancel () {
|
||||
this.$emit('input', false)
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ export default Vue.extend({
|
|||
this.loading = true
|
||||
this.series = (await this.$komgaSeries.getSeries(undefined, { size: this.pageSize }, query)).content
|
||||
this.books = (await this.$komgaBooks.getBooks(undefined, { size: this.pageSize }, query)).content
|
||||
this.collections = (await this.$komgaCollections.getCollections(undefined, { size: this.pageSize }, false, query)).content
|
||||
this.collections = (await this.$komgaCollections.getCollections(undefined, { size: this.pageSize }, query)).content
|
||||
this.showResults = true
|
||||
this.loading = false
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -11,11 +11,10 @@ export default class KomgaCollectionsService {
|
|||
this.http = http
|
||||
}
|
||||
|
||||
async getCollections (libraryIds?: number[], pageRequest?: PageRequest, unpaged?: boolean, search?: string): Promise<Page<CollectionDto>> {
|
||||
async getCollections (libraryIds?: number[], pageRequest?: PageRequest, search?: string): Promise<Page<CollectionDto>> {
|
||||
try {
|
||||
const params = { ...pageRequest } as any
|
||||
if (libraryIds) params.library_id = libraryIds
|
||||
if (unpaged) params.unpaged = unpaged
|
||||
if (search) params.search = search
|
||||
|
||||
return (await this.http.get(API_COLLECTIONS, {
|
||||
|
|
@ -79,7 +78,7 @@ export default class KomgaCollectionsService {
|
|||
}
|
||||
}
|
||||
|
||||
async getSeries (collectionId: number, pageRequest?: PageRequest): Promise<SeriesDto[]> {
|
||||
async getSeries (collectionId: number, pageRequest?: PageRequest): Promise<Page<SeriesDto>> {
|
||||
try {
|
||||
return (await this.http.get(`${API_COLLECTIONS}/${collectionId}/series`)).data
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ interface Sort {
|
|||
interface PageRequest {
|
||||
size?: number,
|
||||
page?: number,
|
||||
sort?: string[]
|
||||
sort?: string[],
|
||||
unpaged?: boolean
|
||||
}
|
||||
|
||||
interface SortOption {
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ export default Vue.extend({
|
|||
methods: {
|
||||
async loadCollection (collectionId: number) {
|
||||
this.collection = await this.$komgaCollections.getOneCollection(collectionId)
|
||||
this.series = await this.$komgaCollections.getSeries(collectionId)
|
||||
this.series = (await this.$komgaCollections.getSeries(collectionId, { unpaged: true } as PageRequest)).content
|
||||
this.seriesCopy = [...this.series]
|
||||
},
|
||||
singleEdit (series: SeriesDto) {
|
||||
|
|
|
|||
|
|
@ -412,7 +412,7 @@ export default Vue.extend({
|
|||
this.series = await this.$komgaSeries.getOneSeries(seriesId)
|
||||
this.collections = await this.$komgaSeries.getCollections(seriesId)
|
||||
for (const c of this.collections) {
|
||||
this.collectionsContent[c.id] = await this.$komgaCollections.getSeries(c.id)
|
||||
this.collectionsContent[c.id] = (await this.$komgaCollections.getSeries(c.id, { unpaged: true } as PageRequest)).content
|
||||
}
|
||||
await this.loadPage(seriesId, this.page, this.sortActive)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ export default Vue.extend({
|
|||
|
||||
this.series = (await this.$komgaSeries.getSeries(undefined, { size: this.pageSize }, search)).content
|
||||
this.books = (await this.$komgaBooks.getBooks(undefined, { size: this.pageSize }, search)).content
|
||||
this.collections = (await this.$komgaCollections.getCollections(undefined, { size: this.pageSize }, undefined, search)).content
|
||||
this.collections = (await this.$komgaCollections.getCollections(undefined, { size: this.pageSize }, search)).content
|
||||
|
||||
this.loading = false
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ 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 java.time.LocalDateTime
|
||||
|
||||
|
|
@ -60,16 +61,17 @@ class SeriesCollectionDao(
|
|||
.apply { if (pageable.isPaged) limit(pageable.pageSize).offset(pageable.offset) }
|
||||
.fetchAndMap(null)
|
||||
|
||||
val pageSort = if (orderBy.size > 1) pageable.sort else Sort.unsorted()
|
||||
return PageImpl(
|
||||
items,
|
||||
if (pageable.isPaged) PageRequest.of(pageable.pageNumber, pageable.pageSize, pageable.sort)
|
||||
else PageRequest.of(0, count.toInt(), pageable.sort),
|
||||
if (pageable.isPaged) PageRequest.of(pageable.pageNumber, pageable.pageSize, pageSort)
|
||||
else PageRequest.of(0, count.toInt(), pageSort),
|
||||
count.toLong()
|
||||
)
|
||||
}
|
||||
|
||||
override fun findAllByLibraries(belongsToLibraryIds: Collection<Long>, filterOnLibraryIds: Collection<Long>?, search: String?, pageable: Pageable): Page<SeriesCollection> {
|
||||
val ids = dsl.select(c.ID)
|
||||
val ids = dsl.selectDistinct(c.ID)
|
||||
.from(c)
|
||||
.leftJoin(cs).on(c.ID.eq(cs.COLLECTION_ID))
|
||||
.leftJoin(s).on(cs.SERIES_ID.eq(s.ID))
|
||||
|
|
@ -77,10 +79,7 @@ class SeriesCollectionDao(
|
|||
.apply { search?.let { and(c.NAME.containsIgnoreCase(it)) } }
|
||||
.fetch(0, Long::class.java)
|
||||
|
||||
val count = dsl.selectCount()
|
||||
.from(c)
|
||||
.where(c.ID.`in`(ids))
|
||||
.fetchOne(0, Long::class.java)
|
||||
val count = ids.size
|
||||
|
||||
val orderBy = pageable.sort.toOrderBy(sorts)
|
||||
|
||||
|
|
@ -92,10 +91,11 @@ class SeriesCollectionDao(
|
|||
.apply { if (pageable.isPaged) limit(pageable.pageSize).offset(pageable.offset) }
|
||||
.fetchAndMap(filterOnLibraryIds)
|
||||
|
||||
val pageSort = if (orderBy.size > 1) pageable.sort else Sort.unsorted()
|
||||
return PageImpl(
|
||||
items,
|
||||
if (pageable.isPaged) PageRequest.of(pageable.pageNumber, pageable.pageSize, pageable.sort)
|
||||
else PageRequest.of(0, count.toInt()),
|
||||
if (pageable.isPaged) PageRequest.of(pageable.pageNumber, pageable.pageSize, pageSort)
|
||||
else PageRequest.of(0, count.toInt(), pageSort),
|
||||
count.toLong()
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ 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 java.math.BigDecimal
|
||||
import java.net.URL
|
||||
|
|
@ -56,7 +57,8 @@ class SeriesDtoDao(
|
|||
"createdDate" to s.CREATED_DATE,
|
||||
"created" to s.CREATED_DATE,
|
||||
"lastModifiedDate" to s.LAST_MODIFIED_DATE,
|
||||
"lastModified" to s.LAST_MODIFIED_DATE
|
||||
"lastModified" to s.LAST_MODIFIED_DATE,
|
||||
"collection.number" to cs.NUMBER
|
||||
)
|
||||
|
||||
override fun findAll(search: SeriesSearchWithReadProgress, userId: Long, pageable: Pageable): Page<SeriesDto> {
|
||||
|
|
@ -67,6 +69,13 @@ class SeriesDtoDao(
|
|||
return findAll(conditions, having, userId, pageable)
|
||||
}
|
||||
|
||||
override fun findByCollectionId(collectionId: Long, userId: Long, pageable: Pageable): Page<SeriesDto> {
|
||||
val conditions = cs.COLLECTION_ID.eq(collectionId)
|
||||
val having = DSL.trueCondition()
|
||||
|
||||
return findAll(conditions, having, userId, pageable, true)
|
||||
}
|
||||
|
||||
override fun findRecentlyUpdated(search: SeriesSearchWithReadProgress, userId: Long, pageable: Pageable): Page<SeriesDto> {
|
||||
val conditions = search.toCondition()
|
||||
.and(s.CREATED_DATE.ne(s.LAST_MODIFIED_DATE))
|
||||
|
|
@ -83,19 +92,25 @@ class SeriesDtoDao(
|
|||
.fetchAndMap(userId)
|
||||
.firstOrNull()
|
||||
|
||||
override fun findByIds(seriesIds: Collection<Long>, userId: Long): List<SeriesDto> =
|
||||
selectBase(userId)
|
||||
.where(s.ID.`in`(seriesIds))
|
||||
.groupBy(*groupFields)
|
||||
.fetchAndMap(userId)
|
||||
|
||||
|
||||
private fun findAll(conditions: Condition, having: Condition, userId: Long, pageable: Pageable): Page<SeriesDto> {
|
||||
val count = dsl.select(s.ID)
|
||||
private fun selectBase(userId: Long, selectCollectionNumber: Boolean = false): SelectOnConditionStep<Record> =
|
||||
dsl.selectDistinct(*groupFields)
|
||||
.select(DSL.countDistinct(b.ID).`as`(BOOKS_COUNT))
|
||||
.apply { if (selectCollectionNumber) select(cs.NUMBER) }
|
||||
.from(s)
|
||||
.leftJoin(b).on(s.ID.eq(b.SERIES_ID))
|
||||
.leftJoin(d).on(s.ID.eq(d.SERIES_ID))
|
||||
.leftJoin(r).on(b.ID.eq(r.BOOK_ID))
|
||||
.and(readProgressCondition(userId))
|
||||
.leftJoin(cs).on(s.ID.eq(cs.SERIES_ID))
|
||||
|
||||
private fun findAll(conditions: Condition, having: Condition, userId: Long, pageable: Pageable, selectCollectionNumber: Boolean = false): Page<SeriesDto> {
|
||||
val count = dsl.selectDistinct(s.ID)
|
||||
.from(s)
|
||||
.leftJoin(b).on(s.ID.eq(b.SERIES_ID))
|
||||
.leftJoin(d).on(s.ID.eq(d.SERIES_ID))
|
||||
.leftJoin(r).on(b.ID.eq(r.BOOK_ID))
|
||||
.and(readProgressCondition(userId))
|
||||
.leftJoin(cs).on(s.ID.eq(cs.SERIES_ID))
|
||||
.where(conditions)
|
||||
.groupBy(s.ID)
|
||||
|
|
@ -105,32 +120,23 @@ class SeriesDtoDao(
|
|||
|
||||
val orderBy = pageable.sort.toOrderBy(sorts)
|
||||
|
||||
val dtos = selectBase(userId)
|
||||
val dtos = selectBase(userId, selectCollectionNumber)
|
||||
.where(conditions)
|
||||
.groupBy(*groupFields)
|
||||
.having(having)
|
||||
.orderBy(orderBy)
|
||||
.limit(pageable.pageSize)
|
||||
.offset(pageable.offset)
|
||||
.apply { if (pageable.isPaged) limit(pageable.pageSize).offset(pageable.offset) }
|
||||
.fetchAndMap(userId)
|
||||
|
||||
val pageSort = if (orderBy.size > 1) pageable.sort else Sort.unsorted()
|
||||
return PageImpl(
|
||||
dtos,
|
||||
PageRequest.of(pageable.pageNumber, pageable.pageSize, pageable.sort),
|
||||
if (pageable.isPaged) PageRequest.of(pageable.pageNumber, pageable.pageSize, pageSort)
|
||||
else PageRequest.of(0, count.toInt(), pageSort),
|
||||
count.toLong()
|
||||
)
|
||||
}
|
||||
|
||||
private fun selectBase(userId: Long): SelectOnConditionStep<Record> =
|
||||
dsl.selectDistinct(*groupFields)
|
||||
.select(DSL.countDistinct(b.ID).`as`(BOOKS_COUNT))
|
||||
.from(s)
|
||||
.leftJoin(b).on(s.ID.eq(b.SERIES_ID))
|
||||
.leftJoin(d).on(s.ID.eq(d.SERIES_ID))
|
||||
.leftJoin(r).on(b.ID.eq(r.BOOK_ID))
|
||||
.leftJoin(cs).on(s.ID.eq(cs.SERIES_ID))
|
||||
.and(readProgressCondition(userId))
|
||||
|
||||
private fun readProgressCondition(userId: Long): Condition = r.USER_ID.eq(userId).or(r.USER_ID.isNull)
|
||||
|
||||
private fun ResultQuery<Record>.fetchAndMap(userId: Long) =
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import org.gotson.komga.domain.service.SeriesCollectionLifecycle
|
|||
import org.gotson.komga.infrastructure.image.MosaicGenerator
|
||||
import org.gotson.komga.infrastructure.jooq.UnpagedSorted
|
||||
import org.gotson.komga.infrastructure.security.KomgaPrincipal
|
||||
import org.gotson.komga.infrastructure.swagger.PageableAsQueryParam
|
||||
import org.gotson.komga.infrastructure.swagger.PageableWithoutSortAsQueryParam
|
||||
import org.gotson.komga.interfaces.rest.dto.CollectionCreationDto
|
||||
import org.gotson.komga.interfaces.rest.dto.CollectionDto
|
||||
|
|
@ -162,21 +161,29 @@ class SeriesCollectionController(
|
|||
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
}
|
||||
|
||||
@PageableAsQueryParam
|
||||
@PageableWithoutSortAsQueryParam
|
||||
@GetMapping("{id}/series")
|
||||
fun getSeriesForCollection(
|
||||
@PathVariable id: Long,
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
|
||||
@Parameter(hidden = true) page: Pageable
|
||||
): List<SeriesDto> =
|
||||
): Page<SeriesDto> =
|
||||
collectionRepository.findByIdOrNull(id, principal.user.getAuthorizedLibraryIds(null))?.let { collection ->
|
||||
if (collection.ordered) {
|
||||
// use map to ensure the order is conserved
|
||||
collection.seriesIds.mapNotNull { seriesDtoRepository.findByIdOrNull(it, principal.user.id) }
|
||||
} else {
|
||||
seriesDtoRepository.findByIds(collection.seriesIds, principal.user.id)
|
||||
.sortedBy { it.metadata.titleSort }
|
||||
}.map { it.restrictUrl(!principal.user.roleAdmin) }
|
||||
val sort =
|
||||
if (collection.ordered) Sort.by(Sort.Order.asc("collection.number"))
|
||||
else Sort.by(Sort.Order.asc("metadata.titleSort"))
|
||||
|
||||
val pageRequest =
|
||||
if (unpaged) UnpagedSorted(sort)
|
||||
else PageRequest.of(
|
||||
page.pageNumber,
|
||||
page.pageSize,
|
||||
sort
|
||||
)
|
||||
|
||||
seriesDtoRepository.findByCollectionId(collection.id, principal.user.id, pageRequest)
|
||||
.map { it.restrictUrl(!principal.user.roleAdmin) }
|
||||
} ?: throw ResponseStatusException(HttpStatus.NOT_FOUND)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import org.gotson.komga.domain.persistence.SeriesCollectionRepository
|
|||
import org.gotson.komga.domain.persistence.SeriesMetadataRepository
|
||||
import org.gotson.komga.domain.persistence.SeriesRepository
|
||||
import org.gotson.komga.domain.service.BookLifecycle
|
||||
import org.gotson.komga.infrastructure.jooq.UnpagedSorted
|
||||
import org.gotson.komga.infrastructure.security.KomgaPrincipal
|
||||
import org.gotson.komga.infrastructure.swagger.PageableAsQueryParam
|
||||
import org.gotson.komga.infrastructure.swagger.PageableWithoutSortAsQueryParam
|
||||
|
|
@ -76,14 +77,20 @@ class SeriesController(
|
|||
@RequestParam(name = "collection_id", required = false) collectionIds: List<Long>?,
|
||||
@RequestParam(name = "status", required = false) metadataStatus: List<SeriesMetadata.Status>?,
|
||||
@RequestParam(name = "read_status", required = false) readStatus: List<ReadStatus>?,
|
||||
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
|
||||
@Parameter(hidden = true) page: Pageable
|
||||
): Page<SeriesDto> {
|
||||
val pageRequest = PageRequest.of(
|
||||
page.pageNumber,
|
||||
page.pageSize,
|
||||
if (page.sort.isSorted) Sort.by(page.sort.map { it.ignoreCase() }.toList())
|
||||
else Sort.by(Sort.Order.asc("metadata.titleSort").ignoreCase())
|
||||
)
|
||||
val sort =
|
||||
if (page.sort.isSorted) page.sort
|
||||
else Sort.by(Sort.Order.asc("metadata.titleSort"))
|
||||
|
||||
val pageRequest =
|
||||
if (unpaged) UnpagedSorted(sort)
|
||||
else PageRequest.of(
|
||||
page.pageNumber,
|
||||
page.pageSize,
|
||||
sort
|
||||
)
|
||||
|
||||
val seriesSearch = SeriesSearchWithReadProgress(
|
||||
libraryIds = principal.user.getAuthorizedLibraryIds(libraryIds),
|
||||
|
|
@ -102,13 +109,18 @@ class SeriesController(
|
|||
@GetMapping("/latest")
|
||||
fun getLatestSeries(
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
|
||||
@Parameter(hidden = true) page: Pageable
|
||||
): Page<SeriesDto> {
|
||||
val pageRequest = PageRequest.of(
|
||||
page.pageNumber,
|
||||
page.pageSize,
|
||||
Sort.by(Sort.Direction.DESC, "lastModifiedDate")
|
||||
)
|
||||
val sort = Sort.by(Sort.Order.desc("lastModified"))
|
||||
|
||||
val pageRequest =
|
||||
if (unpaged) UnpagedSorted(sort)
|
||||
else PageRequest.of(
|
||||
page.pageNumber,
|
||||
page.pageSize,
|
||||
sort
|
||||
)
|
||||
|
||||
return seriesDtoRepository.findAll(
|
||||
SeriesSearchWithReadProgress(principal.user.getAuthorizedLibraryIds(null)),
|
||||
|
|
@ -122,13 +134,18 @@ class SeriesController(
|
|||
@GetMapping("/new")
|
||||
fun getNewSeries(
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
|
||||
@Parameter(hidden = true) page: Pageable
|
||||
): Page<SeriesDto> {
|
||||
val pageRequest = PageRequest.of(
|
||||
page.pageNumber,
|
||||
page.pageSize,
|
||||
Sort.by(Sort.Direction.DESC, "createdDate")
|
||||
)
|
||||
val sort = Sort.by(Sort.Order.desc("created"))
|
||||
|
||||
val pageRequest =
|
||||
if (unpaged) UnpagedSorted(sort)
|
||||
else PageRequest.of(
|
||||
page.pageNumber,
|
||||
page.pageSize,
|
||||
sort
|
||||
)
|
||||
|
||||
return seriesDtoRepository.findAll(
|
||||
SeriesSearchWithReadProgress(principal.user.getAuthorizedLibraryIds(null)),
|
||||
|
|
@ -142,13 +159,18 @@ class SeriesController(
|
|||
@GetMapping("/updated")
|
||||
fun getUpdatedSeries(
|
||||
@AuthenticationPrincipal principal: KomgaPrincipal,
|
||||
@RequestParam(name = "unpaged", required = false) unpaged: Boolean = false,
|
||||
@Parameter(hidden = true) page: Pageable
|
||||
): Page<SeriesDto> {
|
||||
val pageRequest = PageRequest.of(
|
||||
page.pageNumber,
|
||||
page.pageSize,
|
||||
Sort.by(Sort.Direction.DESC, "lastModifiedDate")
|
||||
)
|
||||
val sort = Sort.by(Sort.Order.desc("lastModified"))
|
||||
|
||||
val pageRequest =
|
||||
if (unpaged) UnpagedSorted(sort)
|
||||
else PageRequest.of(
|
||||
page.pageNumber,
|
||||
page.pageSize,
|
||||
sort
|
||||
)
|
||||
|
||||
return seriesDtoRepository.findRecentlyUpdated(
|
||||
SeriesSearchWithReadProgress(principal.user.getAuthorizedLibraryIds(null)),
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import org.springframework.data.domain.Pageable
|
|||
|
||||
interface SeriesDtoRepository {
|
||||
fun findAll(search: SeriesSearchWithReadProgress, userId: Long, pageable: Pageable): Page<SeriesDto>
|
||||
fun findByCollectionId(collectionId: Long, userId: Long, pageable: Pageable): Page<SeriesDto>
|
||||
fun findRecentlyUpdated(search: SeriesSearchWithReadProgress, userId: Long, pageable: Pageable): Page<SeriesDto>
|
||||
fun findByIdOrNull(seriesId: Long, userId: Long): SeriesDto?
|
||||
fun findByIds(seriesIds: Collection<Long>, userId: Long): List<SeriesDto>
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue