Querying min,max,avg,sum and also list data
This commit is contained in:
@@ -6,7 +6,14 @@ import cats.effect.unsafe.implicits.global
|
|||||||
import doobie._
|
import doobie._
|
||||||
import doobie.implicits._
|
import doobie.implicits._
|
||||||
import doobie.postgres.implicits._
|
import doobie.postgres.implicits._
|
||||||
|
import io.circe.syntax.EncoderOps
|
||||||
import parse.Aggregate.{AggregateKey, UserQuery}
|
import parse.Aggregate.{AggregateKey, UserQuery}
|
||||||
|
import parse.Aggregate.AggregateValueImplicits.aggregateValueEncoder
|
||||||
|
import parse.Aggregate.userQueryEncoder
|
||||||
|
import io.circe.{Json, Printer}
|
||||||
|
import io.circe.generic.auto._
|
||||||
|
import io.circe.syntax._
|
||||||
|
import parse.Aggregate
|
||||||
|
|
||||||
import java.time.{LocalDate, LocalDateTime}
|
import java.time.{LocalDate, LocalDateTime}
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
@@ -20,6 +27,14 @@ object Main {
|
|||||||
"mysecretpassword"
|
"mysecretpassword"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
implicit class JsonPrettyPrinter(json: Json) {
|
||||||
|
def pretty: String = {
|
||||||
|
val printer = Printer.spaces2.copy(dropNullValues = true)
|
||||||
|
printer.print(json)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private case class ResponseWrapper(result: Map[String, Option[Aggregate.AggregateValue]], query: UserQuery)
|
||||||
|
|
||||||
def main(args: Array[String]): Unit = {
|
def main(args: Array[String]): Unit = {
|
||||||
val xa = transactor[IO]
|
val xa = transactor[IO]
|
||||||
// val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
|
// val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
|
||||||
@@ -30,16 +45,22 @@ object Main {
|
|||||||
// } yield re
|
// } yield re
|
||||||
|
|
||||||
val formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmm")
|
val formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmm")
|
||||||
val from = LocalDateTime.parse("20231212_0000", formatter)
|
val from = LocalDateTime.parse("20231210_0000", formatter)
|
||||||
val to = LocalDateTime.parse("20231214_2359", formatter)
|
val to = LocalDateTime.parse("20231214_2359", formatter)
|
||||||
|
val cities = NonEmptyList.of("Ainaži", "Rīga", "Kolka", "Vičaki")
|
||||||
|
// val cities = NonEmptyList.of("Rīga")
|
||||||
|
|
||||||
val query = UserQuery(NonEmptyList.of("Ainaži", "Rīga", "Kolka", "Vičaki"), "tempAvg", AggregateKey.Max, ChronoUnit.HOURS, from, to)
|
val query = UserQuery(cities, "tempMax", AggregateKey.List, ChronoUnit.DAYS, from, to)
|
||||||
|
|
||||||
val result = for {
|
val re = for {
|
||||||
postgresService <- PostgresService.of(xa)
|
postgresService <- PostgresService.of(xa)
|
||||||
re <- postgresService.query(query)
|
re <- postgresService.query(query)
|
||||||
} yield re
|
} yield re
|
||||||
println(result.unsafeRunSync().toString())
|
|
||||||
|
val jssson = re.map(result => ResponseWrapper(result, query))
|
||||||
|
.map(responseWrapper => responseWrapper.asJson.pretty)
|
||||||
|
|
||||||
|
println(jssson.unsafeRunSync())
|
||||||
|
|
||||||
// val result = createWeatherTable(xa).unsafeRunSync()
|
// val result = createWeatherTable(xa).unsafeRunSync()
|
||||||
// val result = insertInWeatherTable(xa).unsafeRunSync()
|
// val result = insertInWeatherTable(xa).unsafeRunSync()
|
||||||
|
|||||||
@@ -11,9 +11,11 @@ import java.time.{LocalDate, LocalDateTime, OffsetDateTime, ZoneId, ZonedDateTim
|
|||||||
import doobie.postgres.implicits._
|
import doobie.postgres.implicits._
|
||||||
import org.typelevel.log4cats.Logger
|
import org.typelevel.log4cats.Logger
|
||||||
import org.typelevel.log4cats.slf4j.Slf4jLogger
|
import org.typelevel.log4cats.slf4j.Slf4jLogger
|
||||||
import parse.Aggregate.{AggregateKey, AggregateValue, DoubleValue, UserQuery}
|
import parse.Aggregate.{AggregateKey, AggregateValue, DoubleValue, TimeDoubleList, UserQuery}
|
||||||
import parse.{Parser, WeatherStationData}
|
import parse.{Parser, WeatherStationData}
|
||||||
|
|
||||||
|
import java.time.temporal.ChronoUnit
|
||||||
|
|
||||||
object PostgresService {
|
object PostgresService {
|
||||||
def of(transactor: Transactor[IO]): IO[PostgresService] = {
|
def of(transactor: Transactor[IO]): IO[PostgresService] = {
|
||||||
Slf4jLogger.create[IO].map(logger => new PostgresService(transactor, logger))
|
Slf4jLogger.create[IO].map(logger => new PostgresService(transactor, logger))
|
||||||
@@ -35,40 +37,24 @@ class PostgresService(transactor: Transactor[IO], log: Logger[IO]) extends DataS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
SELECT city, AVG(tempmax) AS tempmax -- MAX MIN AVG SUM
|
|
||||||
FROM weather
|
|
||||||
WHERE city IN ('Rīga', 'Rēzekne', 'Kolka')
|
|
||||||
AND dateTime BETWEEN '2023-12-10 00:00:00' AND '2023-12-10 23:59:59'
|
|
||||||
GROUP BY city;
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
SELECT city, dateTime, tempmax
|
|
||||||
FROM weather
|
|
||||||
WHERE city IN ('Rīga', 'Rēzekne', 'Kolka')
|
|
||||||
AND dateTime BETWEEN '2023-12-10 00:00:00' AND '2023-12-10 23:59:59'
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
SELECT city, DATE(dateTime) as day, MAX(tempmax) as max_temp
|
|
||||||
FROM weather
|
|
||||||
WHERE city IN ('Rīga', 'Rēzekne', 'Kolka')
|
|
||||||
AND dateTime BETWEEN '2023-12-01' AND '2023-12-31'
|
|
||||||
GROUP BY city, DATE(dateTime)
|
|
||||||
ORDER BY city, day;
|
|
||||||
*/
|
|
||||||
def query(userQuery: UserQuery): IO[Map[String, Option[AggregateValue]]] = {
|
def query(userQuery: UserQuery): IO[Map[String, Option[AggregateValue]]] = {
|
||||||
if (List(
|
if (userQuery.field == "phenomena") { // this handles strings
|
||||||
|
// TODO query list and distinct values from phenomena
|
||||||
|
println("EMPTY RESULT!!!!!111")
|
||||||
|
IO(Map()) // empty result
|
||||||
|
} else if (List(
|
||||||
AggregateKey.Max,
|
AggregateKey.Max,
|
||||||
AggregateKey.Min,
|
AggregateKey.Min,
|
||||||
AggregateKey.Avg,
|
AggregateKey.Avg,
|
||||||
AggregateKey.Sum
|
AggregateKey.Sum,
|
||||||
|
AggregateKey.Distinct
|
||||||
).contains(userQuery.key)) {
|
).contains(userQuery.key)) {
|
||||||
val query =
|
val query =
|
||||||
(fr"SELECT city, ROUND(CAST(" ++ Fragment.const(userQuery.key.toString.toUpperCase) ++ fr"(" ++ Fragment.const(userQuery.field) ++ fr") AS NUMERIC), 1) AS value FROM weather WHERE " ++
|
fr"SELECT city, ROUND(CAST(" ++ Fragment.const(userQuery.key.toString.toUpperCase) ++ fr"(" ++ Fragment.const(userQuery.field) ++ fr") AS NUMERIC), 1) AS value" ++
|
||||||
Fragments.in(fr"city", userQuery.cities) ++
|
fr" FROM weather" ++
|
||||||
fr" AND dateTime BETWEEN ${userQuery.from} AND ${userQuery.to} GROUP BY city")
|
fr" WHERE " ++ Fragments.in(fr"city", userQuery.cities) ++
|
||||||
|
fr" AND dateTime BETWEEN ${userQuery.from} AND ${userQuery.to}" ++
|
||||||
|
fr" GROUP BY city"
|
||||||
|
|
||||||
query.query[(String, Option[Double])]
|
query.query[(String, Option[Double])]
|
||||||
.to[List]
|
.to[List]
|
||||||
@@ -79,8 +65,88 @@ class PostgresService(transactor: Transactor[IO], log: Logger[IO]) extends DataS
|
|||||||
city -> maybeValue.map(DoubleValue)
|
city -> maybeValue.map(DoubleValue)
|
||||||
}.toMap: Map[String, Option[AggregateValue]]
|
}.toMap: Map[String, Option[AggregateValue]]
|
||||||
}
|
}
|
||||||
|
} else if (userQuery.key == AggregateKey.List) {
|
||||||
|
val byField = userQuery.field match {
|
||||||
|
case "tempMax" => "MAX(tempMax)"
|
||||||
|
case "tempMin" => "MIN(tempMin)"
|
||||||
|
case "tempAvg" => "AVG(tempAvg)"
|
||||||
|
case "precipitation" => "SUM(precipitation)"
|
||||||
|
case "windAvg" => "AVG(windAvg)"
|
||||||
|
case "windMax" => "MAX(windMax)"
|
||||||
|
case "visibilityMin" => "MIN(visibilityMin)"
|
||||||
|
case "visibilityAvg" => "AVG(visibilityAvg)"
|
||||||
|
case "snowAvg" => "AVG(snowAvg)"
|
||||||
|
case "atmPressure" => "AVG(atmPressure)"
|
||||||
|
case "dewPoint" => "AVG(dewPoint)"
|
||||||
|
case "humidity" => "AVG(humidity)"
|
||||||
|
case "sunDuration" => "SUM(sunDuration)"
|
||||||
|
}
|
||||||
|
|
||||||
|
val selectField = if(userQuery.granularity == ChronoUnit.HOURS) userQuery.field else byField;
|
||||||
|
val selectTime = userQuery.granularity match {
|
||||||
|
case ChronoUnit.HOURS => "dateTime"
|
||||||
|
case ChronoUnit.DAYS => "DATE(dateTime)"
|
||||||
|
case ChronoUnit.MONTHS => "TO_CHAR(DATE_TRUNC('month', dateTime), 'YYYY-MM')"
|
||||||
|
case ChronoUnit.YEARS => "TO_CHAR(DATE_TRUNC('year', dateTime), 'YYYY')"
|
||||||
|
case _ => ""
|
||||||
|
}
|
||||||
|
val groupBy = if(userQuery.granularity == ChronoUnit.HOURS) "" else "GROUP BY city, " + selectTime;
|
||||||
|
|
||||||
|
val query =
|
||||||
|
fr"SELECT city, " ++ Fragment.const(selectTime) ++ fr" as time, ROUND(CAST(" ++ Fragment.const(selectField) ++ fr" AS NUMERIC), 1) as value" ++
|
||||||
|
fr" FROM weather" ++
|
||||||
|
fr" WHERE " ++ Fragments.in(fr"city", userQuery.cities) ++
|
||||||
|
fr" AND dateTime BETWEEN ${userQuery.from} AND ${userQuery.to}" ++
|
||||||
|
fr" " ++ Fragment.const(groupBy) ++
|
||||||
|
fr" ORDER BY city, time;"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
SELECT city, dateTime, tempmax
|
||||||
|
FROM weather
|
||||||
|
WHERE city IN ('Rīga', 'Rēzekne', 'Kolka')
|
||||||
|
AND dateTime BETWEEN '2023-12-10' AND '2023-12-14'
|
||||||
|
ORDER BY city, dateTime;
|
||||||
|
|
||||||
|
SELECT city, DATE(dateTime) as day, MAX(tempmax) as max_temp
|
||||||
|
FROM weather
|
||||||
|
WHERE city IN ('Rīga', 'Rēzekne', 'Kolka')
|
||||||
|
AND dateTime BETWEEN '2023-12-10' AND '2023-12-14'
|
||||||
|
GROUP BY city, DATE(dateTime)
|
||||||
|
ORDER BY city, day;
|
||||||
|
|
||||||
|
SELECT city, TO_CHAR(DATE_TRUNC('month', dateTime), 'YYYY-MM') as month, MAX(tempmax) as max_temp
|
||||||
|
FROM weather
|
||||||
|
WHERE city IN ('Rīga', 'Rēzekne', 'Kolka')
|
||||||
|
AND dateTime BETWEEN '2023-01-01' AND '2023-12-31'
|
||||||
|
GROUP BY city, TO_CHAR(DATE_TRUNC('month', dateTime), 'YYYY-MM')
|
||||||
|
ORDER BY city, month;
|
||||||
|
|
||||||
|
SELECT city, TO_CHAR(DATE_TRUNC('year', dateTime), 'YYYY') as year, MAX(tempmax) as max_temp
|
||||||
|
FROM weather
|
||||||
|
WHERE city IN ('Rīga', 'Rēzekne', 'Kolka')
|
||||||
|
AND dateTime BETWEEN '2023-01-01' AND '2023-12-31'
|
||||||
|
GROUP BY city, TO_CHAR(DATE_TRUNC('year', dateTime), 'YYYY')
|
||||||
|
ORDER BY city, year;
|
||||||
|
*/
|
||||||
|
|
||||||
|
query.query[(String, String, Option[Double])]
|
||||||
|
.to[List]
|
||||||
|
.transact(transactor)
|
||||||
|
.map { resultList =>
|
||||||
|
resultList
|
||||||
|
.groupBy(_._1)
|
||||||
|
.view.mapValues { list =>
|
||||||
|
TimeDoubleList(list.map { case (_, date, maybeValue) =>
|
||||||
|
(date, maybeValue)
|
||||||
|
}).some
|
||||||
|
}
|
||||||
|
.toMap: Map[String, Option[AggregateValue]]
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
???
|
println("EMPTY RESULT!!!!!")
|
||||||
|
IO(Map()) // empty result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user