Add city prefix search API using local pincode DB
All checks were successful
build-and-deploy / build-deploy (push) Successful in 35s

This commit is contained in:
androidlover5842
2026-02-05 20:02:35 +05:30
parent a0e354b464
commit 71c10193a3
3 changed files with 88 additions and 0 deletions

View File

@@ -121,6 +121,33 @@ AUTH + SYSTEM APIS
- 401 Unauthorized (missing principal / user not found)
- City search API is this one:
GET /geo/cities/search
What it does:
- Searches city names by prefix from local pincode directory table.
- Returns unique city + state rows only.
- Uses local DB only (no external API call in this endpoint).
Request body:
- None.
Query params:
- q (required, minimum 2 characters)
- limit (optional, default 20, min 1, max 100)
- Allowed roles: authenticated Firebase user.
Error Codes
- 400 Bad Request (q too short)
- 401 Unauthorized
================================================================================
PROPERTY + USER APIS
================================================================================

View File

@@ -0,0 +1,39 @@
package com.android.trisolarisserver.controller.system
import com.android.trisolarisserver.controller.common.requirePrincipal
import com.android.trisolarisserver.repo.property.IndiaPincodeCityStateRepo
import com.android.trisolarisserver.security.MyPrincipal
import org.springframework.data.domain.PageRequest
import org.springframework.http.HttpStatus
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ResponseStatusException
@RestController
class GeoSearch(
private val indiaPincodeCityStateRepo: IndiaPincodeCityStateRepo
) {
@GetMapping("/geo/cities/search")
fun searchCities(
@AuthenticationPrincipal principal: MyPrincipal?,
@RequestParam("q") q: String,
@RequestParam("limit", required = false, defaultValue = "20") limit: Int
): List<CityStateResponse> {
requirePrincipal(principal)
val query = q.trim()
if (query.length < 2) {
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "q must be at least 2 characters")
}
val boundedLimit = limit.coerceIn(1, 100)
return indiaPincodeCityStateRepo
.searchCityStateByPrefix(query, PageRequest.of(0, boundedLimit))
.map { CityStateResponse(city = it.city, state = it.state) }
}
}
data class CityStateResponse(
val city: String,
val state: String
)

View File

@@ -25,6 +25,23 @@ interface IndiaPincodeCityStateRepo : JpaRepository<IndiaPincodeCityState, UUID>
@Param("pincode") pincode: Int,
pageable: Pageable
): List<PincodeCityStateCandidate>
@Query(
"""
select p.city as city,
p.state as state
from IndiaPincodeCityState p
where trim(p.city) <> ''
and trim(p.state) <> ''
and lower(p.city) like lower(concat(:prefix, '%'))
group by p.city, p.state
order by p.city asc, p.state asc
"""
)
fun searchCityStateByPrefix(
@Param("prefix") prefix: String,
pageable: Pageable
): List<CityStateRow>
}
interface PincodeCityStateCandidate {
@@ -32,3 +49,8 @@ interface PincodeCityStateCandidate {
val state: String
val hitCount: Long
}
interface CityStateRow {
val city: String
val state: String
}