-
-
Notifications
You must be signed in to change notification settings - Fork 206
/
SeriesDtoDao.kt
192 lines (162 loc) · 6.44 KB
/
SeriesDtoDao.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package org.gotson.komga.infrastructure.jooq
import org.gotson.komga.domain.model.ReadStatus
import org.gotson.komga.domain.model.SeriesSearchWithReadProgress
import org.gotson.komga.interfaces.rest.dto.SeriesDto
import org.gotson.komga.interfaces.rest.dto.SeriesMetadataDto
import org.gotson.komga.interfaces.rest.dto.toUTC
import org.gotson.komga.interfaces.rest.persistence.SeriesDtoRepository
import org.gotson.komga.jooq.Tables
import org.gotson.komga.jooq.tables.records.SeriesMetadataRecord
import org.gotson.komga.jooq.tables.records.SeriesRecord
import org.jooq.AggregateFunction
import org.jooq.Condition
import org.jooq.DSLContext
import org.jooq.Record
import org.jooq.ResultQuery
import org.jooq.SelectOnConditionStep
import org.jooq.impl.DSL
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.stereotype.Component
import java.math.BigDecimal
import java.net.URL
const val BOOKS_COUNT = "booksCount"
const val BOOKS_UNREAD_COUNT = "booksUnreadCount"
const val BOOKS_IN_PROGRESS_COUNT = "booksInProgressCount"
const val BOOKS_READ_COUNT = "booksReadCount"
@Component
class SeriesDtoDao(
private val dsl: DSLContext
) : SeriesDtoRepository {
companion object {
private val s = Tables.SERIES
private val b = Tables.BOOK
private val d = Tables.SERIES_METADATA
private val r = Tables.READ_PROGRESS
val countUnread: AggregateFunction<BigDecimal> = DSL.sum(DSL.`when`(r.COMPLETED.isNull, 1).otherwise(0))
val countRead: AggregateFunction<BigDecimal> = DSL.sum(DSL.`when`(r.COMPLETED.isTrue, 1).otherwise(0))
val countInProgress: AggregateFunction<BigDecimal> = DSL.sum(DSL.`when`(r.COMPLETED.isFalse, 1).otherwise(0))
}
private val groupFields = arrayOf(
*s.fields(),
*d.fields()
)
private val sorts = mapOf(
"metadata.titleSort" to DSL.lower(d.TITLE_SORT),
"createdDate" to s.CREATED_DATE,
"created" to s.CREATED_DATE,
"lastModifiedDate" to s.LAST_MODIFIED_DATE,
"lastModified" to s.LAST_MODIFIED_DATE
)
override fun findAll(search: SeriesSearchWithReadProgress, userId: Long, pageable: Pageable): Page<SeriesDto> {
val conditions = search.toCondition()
val having = search.readStatus.toCondition()
return findAll(conditions, having, userId, pageable)
}
override fun findRecentlyUpdated(search: SeriesSearchWithReadProgress, userId: Long, pageable: Pageable): Page<SeriesDto> {
val conditions = search.toCondition()
.and(s.CREATED_DATE.ne(s.LAST_MODIFIED_DATE))
val having = search.readStatus.toCondition()
return findAll(conditions, having, userId, pageable)
}
override fun findByIdOrNull(seriesId: Long, userId: Long): SeriesDto? =
selectBase(userId)
.where(s.ID.eq(seriesId))
.groupBy(*groupFields)
.fetchAndMap()
.firstOrNull()
private fun findAll(conditions: Condition, having: Condition, userId: Long, pageable: Pageable): Page<SeriesDto> {
val count = dsl.select(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))
.where(conditions)
.groupBy(s.ID)
.having(having)
.fetch()
.size
val orderBy = pageable.sort.toOrderBy(sorts)
val dtos = selectBase(userId)
.where(conditions)
.groupBy(*groupFields)
.having(having)
.orderBy(orderBy)
.limit(pageable.pageSize)
.offset(pageable.offset)
.fetchAndMap()
return PageImpl(
dtos,
PageRequest.of(pageable.pageNumber, pageable.pageSize, pageable.sort),
count.toLong()
)
}
private fun selectBase(userId: Long): SelectOnConditionStep<Record> =
dsl.select(*groupFields)
.select(DSL.count(b.ID).`as`(BOOKS_COUNT))
.select(countUnread.`as`(BOOKS_UNREAD_COUNT))
.select(countRead.`as`(BOOKS_READ_COUNT))
.select(countInProgress.`as`(BOOKS_IN_PROGRESS_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))
.and(readProgressCondition(userId))
private fun readProgressCondition(userId: Long): Condition = r.USER_ID.eq(userId).or(r.USER_ID.isNull)
private fun ResultQuery<Record>.fetchAndMap() =
fetch()
.map { r ->
val sr = r.into(s)
val dr = r.into(d)
val booksCount = r.get(BOOKS_COUNT, Int::class.java)
val booksUnreadCount = r.get(BOOKS_UNREAD_COUNT, Int::class.java)
val booksReadCount = r.get(BOOKS_READ_COUNT, Int::class.java)
val booksInProgressCount = r.get(BOOKS_IN_PROGRESS_COUNT, Int::class.java)
sr.toDto(booksCount, booksReadCount, booksUnreadCount, booksInProgressCount, dr.toDto())
}
private fun SeriesSearchWithReadProgress.toCondition(): Condition {
var c: Condition = DSL.trueCondition()
if (libraryIds.isNotEmpty()) c = c.and(s.LIBRARY_ID.`in`(libraryIds))
searchTerm?.let { c = c.and(d.TITLE.containsIgnoreCase(searchTerm)) }
if (metadataStatus.isNotEmpty()) c = c.and(d.STATUS.`in`(metadataStatus))
return c
}
private fun Collection<ReadStatus>.toCondition(): Condition =
if (isNotEmpty()) {
map {
when (it) {
ReadStatus.UNREAD -> countUnread.ge(1.toBigDecimal())
ReadStatus.READ -> countRead.ge(1.toBigDecimal())
ReadStatus.IN_PROGRESS -> countInProgress.ge(1.toBigDecimal())
}
}.reduce { acc, condition -> acc.or(condition) }
} else DSL.trueCondition()
private fun SeriesRecord.toDto(booksCount: Int, booksReadCount: Int, booksUnreadCount: Int, booksInProgressCount: Int, metadata: SeriesMetadataDto) =
SeriesDto(
id = id,
libraryId = libraryId,
name = name,
url = URL(url).toURI().path,
created = createdDate.toUTC(),
lastModified = lastModifiedDate.toUTC(),
fileLastModified = fileLastModified.toUTC(),
booksCount = booksCount,
booksReadCount = booksReadCount,
booksUnreadCount = booksUnreadCount,
booksInProgressCount = booksInProgressCount,
metadata = metadata
)
private fun SeriesMetadataRecord.toDto() =
SeriesMetadataDto(
status = status,
statusLock = statusLock,
created = createdDate.toUTC(),
lastModified = lastModifiedDate.toUTC(),
title = title,
titleLock = titleLock,
titleSort = titleSort,
titleSortLock = titleSortLock
)
}