Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion docker/init.sql
Original file line number Diff line number Diff line change
@@ -1 +1,11 @@
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS postgis;

-- Spatial GIST index for bbox queries (ST_Contains)
CREATE INDEX IF NOT EXISTS idx_flower_spot_pin_point_gist
ON t_flower_spot USING GIST (pin_point)
WHERE deleted_at IS NULL;

-- Spatial GIST index for radius queries (ST_DWithin)
CREATE INDEX IF NOT EXISTS idx_flower_spot_pin_point_geog_gist
ON t_flower_spot USING GIST ((pin_point::geography))
WHERE deleted_at IS NULL;
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
package com.pida.category

import com.fasterxml.jackson.core.type.TypeReference
import com.pida.support.cache.CacheAdvice
import org.springframework.stereotype.Service

@Service
class MapCategoryService(
private val mapCategoryRepository: MapCategoryRepository,
private val cacheAdvice: CacheAdvice,
) {
companion object {
private const val ALL_CATEGORY_KEY = "category:all"
private const val CATEGORY_TTL = 600L
}

suspend fun readBy(categoryId: Long): MapCategory = mapCategoryRepository.findBy(categoryId)

suspend fun findAll(): List<MapCategory> = mapCategoryRepository.findAll()
suspend fun findAll(): List<MapCategory> =
cacheAdvice.invoke(
ttl = CATEGORY_TTL,
key = ALL_CATEGORY_KEY,
typeReference = object : TypeReference<List<MapCategory>>() {},
) {
mapCategoryRepository.findAll()
}

suspend fun findAllByCategoryLabel(categoryLabel: CategoryLabel): List<MapCategory> =
mapCategoryRepository.findAllByCategoryLabel(categoryLabel)
findAll().filter { it.categoryLabel == categoryLabel }
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,32 @@ class FlowerSpotFacade(
suspend fun findAllFlowerSpot(
region: Region?,
location: FlowerSpotLocation,
): List<FlowerSpotDetails> {
val flowerSpots = flowerSpotService.readAllFlowerSpot(region, location)
if (flowerSpots.isEmpty()) return emptyList()
): List<FlowerSpotDetails> =
coroutineScope {
val flowerSpots = flowerSpotService.readAllFlowerSpot(region, location)
if (flowerSpots.isEmpty()) return@coroutineScope emptyList()

val bloomingDeferred = async { bloomingService.recentlyBloomingBySpotIds(flowerSpots.map { it.id }) }
val previewDeferred =
flowerSpots.map { spot ->
async {
spot.id to
imageS3Caller.getPreviewImage(
prefix = ImagePrefix.FLOWERSPOT.value,
prefixId = spot.id,
)
}
}

val recentlyBlooming = bloomingService.recentlyBloomingBySpotIds(flowerSpots.map { it.id })
val bloomingBySpotId = recentlyBlooming.groupBy { it.flowerSpotId }
val bloomingBySpotId = bloomingDeferred.await().groupBy { it.flowerSpotId }
val previewBySpotId = previewDeferred.associate { it.await() }

return flowerSpots.map { flowerSpot ->
FlowerSpotDetails.of(
flowerSpot = flowerSpot,
bloomings = bloomingBySpotId[flowerSpot.id] ?: emptyList(),
images =
listOfNotNull(
imageS3Caller.getPreviewImage(
prefix = ImagePrefix.FLOWERSPOT.value,
prefixId = flowerSpot.id,
),
),
)
flowerSpots.map { flowerSpot ->
FlowerSpotDetails.of(
flowerSpot = flowerSpot,
bloomings = bloomingBySpotId[flowerSpot.id] ?: emptyList(),
images = listOfNotNull(previewBySpotId[flowerSpot.id]),
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.pida.flowerspot

import com.fasterxml.jackson.core.type.TypeReference
import com.pida.support.cache.CacheAdvice
import com.pida.support.geo.GeoJson
import com.pida.support.geo.Region
import org.springframework.stereotype.Component

Expand All @@ -25,7 +24,7 @@ class FlowerSpotFinder(
flowerSpotRepository.findAll()
}

suspend fun readAllByRegion(region: Region): List<FlowerSpot> = readAll().filterBy(region = region)
suspend fun readAllByRegion(region: Region): List<FlowerSpot> = readAll().filter { it.region == region }

suspend fun readBy(spotId: Long): FlowerSpot = flowerSpotRepository.findBy(spotId)

Expand All @@ -37,12 +36,12 @@ class FlowerSpotFinder(
is FindFlowerSpotPolicyCondition.ByRegionAndLocation -> readAllByLocationAndRegion(condition.region, condition.location)
}

suspend fun readAllByLocation(location: FlowerSpotLocation): List<FlowerSpot> = readAll().filterBy(location = location)
suspend fun readAllByLocation(location: FlowerSpotLocation): List<FlowerSpot> = flowerSpotRepository.findAllByLocation(location)

suspend fun readAllByLocationAndRegion(
region: Region,
location: FlowerSpotLocation,
): List<FlowerSpot> = readAll().filterBy(region = region, location = location)
): List<FlowerSpot> = flowerSpotRepository.findAllByLocationAndRegion(region, location)

suspend fun searchByStreetName(streetName: String): List<FlowerSpot> =
cacheAdvice.invoke(
Expand All @@ -52,28 +51,4 @@ class FlowerSpotFinder(
) {
flowerSpotRepository.findByStreetNameContaining(streetName)
}

private fun List<FlowerSpot>.filterBy(
region: Region? = null,
location: FlowerSpotLocation? = null,
): List<FlowerSpot> =
asSequence()
.filter { region == null || it.region == region }
.filter { location == null || it.isWithin(location) }
.toList()

private fun FlowerSpot.isWithin(location: FlowerSpotLocation): Boolean {
if (!location.hasBounds()) return true

val point = pinPoint as? GeoJson.Point ?: return false
if (point.coordinates.size < 2) return false

val longitude = point.coordinates[0]
val latitude = point.coordinates[1]

return longitude > location.swLng!! &&
longitude < location.neLng!! &&
latitude > location.swLat!! &&
latitude < location.neLat!!
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@ import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.EnumType
import jakarta.persistence.Enumerated
import jakarta.persistence.Index
import jakarta.persistence.Table
import org.locationtech.jts.geom.LineString
import org.locationtech.jts.geom.Point

@Entity
@Table(name = "t_flower_spot")
@Table(
name = "t_flower_spot",
indexes = [
Index(name = "idx_flower_spot_region_deleted_at", columnList = "region, deleted_at"),
],
)
class FlowerSpotEntity(
val streetName: String,
val address: String?,
Expand Down
Loading