Skip to content

Commit

Permalink
fix(api): pagination for collection's series
Browse files Browse the repository at this point in the history
add unpaged parameter for SeriesController and SeriesCollectionController
  • Loading branch information
gotson committed Jun 27, 2020
1 parent b4a8024 commit 6dd0704
Show file tree
Hide file tree
Showing 13 changed files with 108 additions and 73 deletions.
2 changes: 1 addition & 1 deletion komga-webui/src/components/CollectionAddToDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion komga-webui/src/components/CollectionEditDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion komga-webui/src/components/SearchBox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 2 additions & 3 deletions komga-webui/src/services/komga-collections.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, {
Expand Down Expand Up @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion komga-webui/src/types/pageable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ interface Sort {
interface PageRequest {
size?: number,
page?: number,
sort?: string[]
sort?: string[],
unpaged?: boolean
}

interface SortOption {
Expand Down
2 changes: 1 addition & 1 deletion komga-webui/src/views/BrowseCollection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion komga-webui/src/views/BrowseSeries.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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)
},
Expand Down
2 changes: 1 addition & 1 deletion komga-webui/src/views/Search.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -60,27 +61,25 @@ 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))
.where(s.LIBRARY_ID.`in`(belongsToLibraryIds))
.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)

Expand All @@ -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()
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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> {
Expand All @@ -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))
Expand All @@ -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 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): Page<SeriesDto> {
val count = dsl.select(s.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)
Expand All @@ -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) =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand All @@ -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)),
Expand All @@ -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)),
Expand All @@ -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)),
Expand Down
Loading

0 comments on commit 6dd0704

Please sign in to comment.