Skip to content

Commit 9fe9901

Browse files
committed
**feat(controller/cache): Add caching to VariantBrowserController for improved performance**
- Implemented caching for search results and detail panels with configurable durations. - Added helper methods `getCachedSearchResults` and `getCachedDetailPanel` to fetch from cache or fallback to database queries. - Updated variant views to utilize cached data, enhancing response times for public variant browsing. - Adjusted `main.css` to modify code styling for better readability.
1 parent 548ead1 commit 9fe9901

File tree

2 files changed

+59
-20
lines changed

2 files changed

+59
-20
lines changed

app/controllers/VariantBrowserController.scala

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,46 @@
11
package controllers
22

33
import jakarta.inject.{Inject, Singleton}
4+
import models.domain.genomics.VariantGroup
45
import org.webjars.play.WebJarsUtil
6+
import play.api.cache.AsyncCacheApi
57
import play.api.i18n.I18nSupport
68
import play.api.mvc.*
79
import repositories.{HaplogroupVariantRepository, VariantAliasRepository, VariantRepository}
810

11+
import scala.concurrent.duration.*
912
import scala.concurrent.{ExecutionContext, Future}
1013

1114
/**
1215
* Public controller for browsing variants (read-only).
1316
* Provides a searchable variant database for researchers.
17+
* Results are cached to improve performance for the public view.
1418
*/
1519
@Singleton
1620
class VariantBrowserController @Inject()(
1721
val controllerComponents: ControllerComponents,
1822
variantRepository: VariantRepository,
1923
variantAliasRepository: VariantAliasRepository,
20-
haplogroupVariantRepository: HaplogroupVariantRepository
24+
haplogroupVariantRepository: HaplogroupVariantRepository,
25+
cache: AsyncCacheApi
2126
)(using webJarsUtil: WebJarsUtil, ec: ExecutionContext)
2227
extends BaseController with I18nSupport {
2328

2429
private val DefaultPageSize = 25
2530

31+
// Cache durations - public view can be stale
32+
private val SearchCacheDuration = 15.minutes
33+
private val DetailCacheDuration = 1.hour
34+
private val TotalCountCacheDuration = 30.minutes
35+
2636
/**
2737
* Main variant browser page with search functionality.
2838
*/
2939
def index(query: Option[String], page: Int, pageSize: Int): Action[AnyContent] = Action.async {
3040
implicit request: Request[AnyContent] =>
3141
val offset = (page - 1) * pageSize
3242
for {
33-
(variantGroups, totalCount) <- variantRepository.searchGroupedPaginated(query.getOrElse(""), offset, pageSize)
43+
(variantGroups, totalCount) <- getCachedSearchResults(query.getOrElse(""), offset, pageSize)
3444
} yield {
3545
val totalPages = Math.max(1, (totalCount + pageSize - 1) / pageSize)
3646
Ok(views.html.variants.browser(variantGroups, query, page, totalPages, pageSize, totalCount))
@@ -44,7 +54,7 @@ class VariantBrowserController @Inject()(
4454
implicit request: Request[AnyContent] =>
4555
val offset = (page - 1) * pageSize
4656
for {
47-
(variantGroups, totalCount) <- variantRepository.searchGroupedPaginated(query.getOrElse(""), offset, pageSize)
57+
(variantGroups, totalCount) <- getCachedSearchResults(query.getOrElse(""), offset, pageSize)
4858
} yield {
4959
val totalPages = Math.max(1, (totalCount + pageSize - 1) / pageSize)
5060
Ok(views.html.variants.listFragment(variantGroups, query, page, totalPages, pageSize, totalCount))
@@ -55,23 +65,46 @@ class VariantBrowserController @Inject()(
5565
* HTMX fragment for variant detail panel (read-only).
5666
*/
5767
def detailPanel(id: Int): Action[AnyContent] = Action.async { implicit request: Request[AnyContent] =>
58-
for {
59-
variantOpt <- variantRepository.findByIdWithContig(id)
60-
allVariantsInGroup <- variantOpt match {
61-
case Some(vwc) =>
62-
val groupKey = vwc.variant.commonName.orElse(vwc.variant.rsId).getOrElse(s"variant_${id}")
63-
variantRepository.getVariantsByGroupKey(groupKey)
64-
case None => Future.successful(Seq.empty)
65-
}
66-
aliases <- variantAliasRepository.findByVariantId(id)
67-
haplogroups <- haplogroupVariantRepository.getHaplogroupsByVariant(id)
68-
} yield {
69-
variantOpt match {
70-
case Some(variantWithContig) =>
71-
val variantGroup = variantRepository.groupVariants(allVariantsInGroup).headOption
72-
Ok(views.html.variants.detailPanel(variantWithContig, variantGroup, aliases, haplogroups))
73-
case None =>
74-
NotFound("Variant not found")
68+
getCachedDetailPanel(id)
69+
}
70+
71+
// === Caching helpers ===
72+
73+
/**
74+
* Get cached search results or fetch from database.
75+
* Cache key includes query, offset, and limit for proper pagination caching.
76+
*/
77+
private def getCachedSearchResults(query: String, offset: Int, limit: Int): Future[(Seq[VariantGroup], Int)] = {
78+
val cacheKey = s"variant-search:${query.toLowerCase.trim}:$offset:$limit"
79+
cache.getOrElseUpdate(cacheKey, SearchCacheDuration) {
80+
variantRepository.searchGroupedPaginated(query, offset, limit)
81+
}
82+
}
83+
84+
/**
85+
* Get cached detail panel or fetch from database.
86+
*/
87+
private def getCachedDetailPanel(id: Int)(implicit request: Request[AnyContent]): Future[Result] = {
88+
val cacheKey = s"variant-detail:$id"
89+
cache.getOrElseUpdate(cacheKey, DetailCacheDuration) {
90+
for {
91+
variantOpt <- variantRepository.findByIdWithContig(id)
92+
allVariantsInGroup <- variantOpt match {
93+
case Some(vwc) =>
94+
val groupKey = vwc.variant.commonName.orElse(vwc.variant.rsId).getOrElse(s"variant_${id}")
95+
variantRepository.getVariantsByGroupKey(groupKey)
96+
case None => Future.successful(Seq.empty)
97+
}
98+
aliases <- variantAliasRepository.findByVariantId(id)
99+
haplogroups <- haplogroupVariantRepository.getHaplogroupsByVariant(id)
100+
} yield {
101+
variantOpt match {
102+
case Some(variantWithContig) =>
103+
val variantGroup = variantRepository.groupVariants(allVariantsInGroup).headOption
104+
Ok(views.html.variants.detailPanel(variantWithContig, variantGroup, aliases, haplogroups))
105+
case None =>
106+
NotFound("Variant not found")
107+
}
75108
}
76109
}
77110
}

public/stylesheets/main.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ body {
77
padding-bottom: 56px;
88
}
99

10+
/* Override Bootstrap's default pink/cyan code styling to use gray */
11+
code {
12+
color: #495057;
13+
background-color: #f8f9fa;
14+
}
15+
1016
.logo-placeholder {
1117
max-width: 900px; /* Increased for more horizontal span */
1218
display: block;

0 commit comments

Comments
 (0)