Skip to content

Commit eafc62f

Browse files
committed
Continue refactor to wrappers round search client to make testing easier
1 parent d906553 commit eafc62f

File tree

4 files changed

+218
-188
lines changed

4 files changed

+218
-188
lines changed

src/main/kotlin/no/java/conf/plugins/Search.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import com.jillesvangurp.ktsearch.KtorRestClient
55
import com.jillesvangurp.ktsearch.SearchClient
66
import io.github.oshai.kotlinlogging.KotlinLogging
77
import io.ktor.server.application.Application
8-
import io.ktor.server.application.call
98
import io.ktor.server.request.receiveNullable
109
import io.ktor.server.response.respondText
1110
import io.ktor.server.routing.get
@@ -16,6 +15,7 @@ import no.java.conf.model.search.TextSearchRequest
1615
import no.java.conf.service.SearchService
1716
import no.java.conf.service.search.ElasticIndexer
1817
import no.java.conf.service.search.ElasticIngester
18+
import no.java.conf.service.search.ElasticSearcher
1919

2020
private val logger = KotlinLogging.logger {}
2121

@@ -50,24 +50,24 @@ fun Application.searchClient() =
5050
)
5151

5252
fun searchService(
53-
searchClient: SearchClient,
5453
indexer: ElasticIndexer,
5554
ingester: ElasticIngester,
55+
searcher: ElasticSearcher,
5656
skipIndex: Boolean
5757
) = SearchService(
58-
client = searchClient,
5958
indexer = indexer,
6059
ingester = ingester,
60+
searcher = searcher,
6161
skipIndex = skipIndex,
6262
)
6363

6464
fun Application.searchService(): SearchService {
6565
val searchClient = searchClient()
6666

6767
return searchService(
68-
searchClient = searchClient,
6968
indexer = ElasticIndexer(searchClient),
7069
ingester = ElasticIngester(searchClient),
70+
searcher = ElasticSearcher(searchClient),
7171
skipIndex =
7272
environment.config
7373
.property("elastic.skipindex")

src/main/kotlin/no/java/conf/service/SearchService.kt

+4-171
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,17 @@ package no.java.conf.service
33
import arrow.core.raise.Raise
44
import arrow.core.raise.ensure
55
import arrow.core.raise.ensureNotNull
6-
import com.jillesvangurp.jsondsl.json
7-
import com.jillesvangurp.ktsearch.Aggregations
8-
import com.jillesvangurp.ktsearch.SearchClient
9-
import com.jillesvangurp.ktsearch.count
10-
import com.jillesvangurp.ktsearch.parseHits
11-
import com.jillesvangurp.ktsearch.parsedBuckets
12-
import com.jillesvangurp.ktsearch.search
13-
import com.jillesvangurp.ktsearch.termsResult
14-
import com.jillesvangurp.searchdsls.querydsl.SearchDSL
15-
import com.jillesvangurp.searchdsls.querydsl.TermsAgg
16-
import com.jillesvangurp.searchdsls.querydsl.agg
17-
import com.jillesvangurp.searchdsls.querydsl.bool
18-
import com.jillesvangurp.searchdsls.querydsl.exists
19-
import com.jillesvangurp.searchdsls.querydsl.nested
20-
import com.jillesvangurp.searchdsls.querydsl.simpleQueryString
21-
import com.jillesvangurp.searchdsls.querydsl.terms
226
import io.github.oshai.kotlinlogging.KotlinLogging
23-
import no.java.conf.model.AggregationsNotFound
247
import no.java.conf.model.ApiError
258
import no.java.conf.model.IndexNotReady
269
import no.java.conf.model.SearchMissing
27-
import no.java.conf.model.search.AggregateResponse
28-
import no.java.conf.model.search.FormatAggregate
29-
import no.java.conf.model.search.LanguageAggregate
3010
import no.java.conf.model.search.SearchResponse
31-
import no.java.conf.model.search.SessionResponse
3211
import no.java.conf.model.search.TextSearchRequest
3312
import no.java.conf.model.search.VideoSearchResponse
34-
import no.java.conf.model.search.YearAggregate
35-
import no.java.conf.model.search.hasFilter
36-
import no.java.conf.model.search.hasQuery
3713
import no.java.conf.model.sessions.Session
3814
import no.java.conf.service.search.ElasticIndexer
3915
import no.java.conf.service.search.ElasticIngester
16+
import no.java.conf.service.search.ElasticSearcher
4017
import kotlin.time.measureTimedValue
4118

4219
private const val INDEX_NAME = "javazone"
@@ -50,9 +27,9 @@ enum class State {
5027
}
5128

5229
class SearchService(
53-
private val client: SearchClient,
5430
private val indexer: ElasticIndexer,
5531
private val ingester: ElasticIngester,
32+
private val searcher: ElasticSearcher,
5633
private val skipIndex: Boolean,
5734
) {
5835
private var readyState = State.NEW
@@ -105,15 +82,7 @@ class SearchService(
10582
suspend fun allVideos(): List<VideoSearchResponse> {
10683
ensureReady(readyState)
10784

108-
val docCount = client.totalDocs()
109-
110-
val sessions =
111-
client.search(INDEX_NAME) {
112-
resultSize = docCount.toInt()
113-
query = exists("video")
114-
}
115-
116-
return sessions.parseHits<VideoSearchResponse>().sortedBy { -it.year }
85+
return searcher.allVideos(INDEX_NAME)
11786
}
11887

11988
fun state(): State = readyState
@@ -126,27 +95,7 @@ class SearchService(
12695

12796
ensureReady(readyState)
12897

129-
val docCount = client.totalDocs()
130-
131-
val searchResult =
132-
client.search(INDEX_NAME) {
133-
addQuery(docCount, searchRequest)
134-
135-
addLanguageAggregate(docCount)
136-
addFormatAggregate(docCount)
137-
addYearAggregate(docCount)
138-
139-
logger.debug { this.json() }
140-
}
141-
142-
ensure(searchResult.aggregations != null) {
143-
raise(AggregationsNotFound)
144-
}
145-
146-
return SearchResponse(
147-
searchResult.parseHits<SessionResponse>().sortedBy { -it.year },
148-
searchResult.aggregations!!.buildResponse()
149-
)
98+
return searcher.textSearch(INDEX_NAME, searchRequest)
15099
}
151100
}
152101

@@ -155,119 +104,3 @@ private fun Raise<ApiError>.ensureReady(readyState: State) {
155104
raise(IndexNotReady)
156105
}
157106
}
158-
159-
private fun Aggregations.buildResponse() =
160-
AggregateResponse(
161-
languages =
162-
this
163-
.termsResult("by-language")
164-
.parsedBuckets
165-
.map { LanguageAggregate(it.parsed.key, it.parsed.docCount) },
166-
formats =
167-
this
168-
.termsResult("by-format")
169-
.parsedBuckets
170-
.map { FormatAggregate(it.parsed.key, it.parsed.docCount) },
171-
years =
172-
this
173-
.termsResult("by-year")
174-
.parsedBuckets
175-
.map { YearAggregate(it.parsed.key.toInt(), it.parsed.docCount) }
176-
.sortedBy { -it.year },
177-
)
178-
179-
private fun SearchDSL.addLanguageAggregate(docCount: Long) {
180-
this.agg(
181-
"by-language",
182-
TermsAgg(Session::language.name) {
183-
aggSize = docCount
184-
minDocCount = 1
185-
}
186-
)
187-
}
188-
189-
private fun SearchDSL.addFormatAggregate(docCount: Long) {
190-
this.agg(
191-
"by-format",
192-
TermsAgg(Session::format.name) {
193-
aggSize = docCount
194-
minDocCount = 1
195-
}
196-
)
197-
}
198-
199-
private fun SearchDSL.addYearAggregate(docCount: Long) {
200-
this.agg(
201-
"by-year",
202-
TermsAgg(Session::year) {
203-
aggSize = docCount
204-
minDocCount = 1
205-
}
206-
)
207-
}
208-
209-
private fun SearchDSL.addQuery(
210-
docCount: Long,
211-
searchRequest: TextSearchRequest
212-
) {
213-
logger.debug { "Building query for $searchRequest" }
214-
215-
resultSize =
216-
when (!searchRequest.hasQuery() && !searchRequest.hasFilter()) {
217-
true -> 0
218-
false -> docCount.toInt()
219-
}
220-
221-
val queryString =
222-
when {
223-
searchRequest.hasQuery() -> searchRequest.query
224-
searchRequest.hasFilter() -> "*"
225-
else -> null
226-
}
227-
228-
val textQuery =
229-
queryString?.let { q ->
230-
bool {
231-
should(
232-
simpleQueryString(q, "title", "abstract", "intendedAudience"),
233-
nested {
234-
path = "speakers"
235-
query = simpleQueryString(q, "speakers.name", "speakers.bio")
236-
}
237-
)
238-
}
239-
}
240-
241-
val yearQuery =
242-
searchRequest.years.ifEmpty { null }?.let { years ->
243-
terms(
244-
field = "year",
245-
values = years.map { it.toString() }.toTypedArray()
246-
)
247-
}
248-
249-
val languageQuery =
250-
searchRequest.languages.ifEmpty { null }?.let { languages ->
251-
terms(
252-
field = "language",
253-
values = languages.toTypedArray()
254-
)
255-
}
256-
257-
val formatQuery =
258-
searchRequest.formats.ifEmpty { null }?.let { formats ->
259-
terms(
260-
field = "format",
261-
values = formats.toTypedArray()
262-
)
263-
}
264-
265-
this.query =
266-
bool {
267-
must(
268-
listOfNotNull(textQuery, yearQuery, languageQuery, formatQuery)
269-
)
270-
}
271-
}
272-
273-
private suspend fun SearchClient.totalDocs() = this.count(INDEX_NAME).count

0 commit comments

Comments
 (0)