diff --git a/README.md b/README.md index 07c2962..e28a686 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Simple SQL queries in Scala 3. No DSLs, no fuss, just plain SQL. Supports *any* JDBC driver. -Additional support for Postgres, MySql, MariaDb, Oracle. +Additional support for Postgres, MySql, MariaDb, Oracle, H2. --- diff --git a/docs/src/files/howtos/HowToPage.scala b/docs/src/files/howtos/HowToPage.scala index 87e0847..286d3c2 100644 --- a/docs/src/files/howtos/HowToPage.scala +++ b/docs/src/files/howtos/HowToPage.scala @@ -3,8 +3,6 @@ package files.howtos import utils.* import Bundle.* -// TODO how to map flat result to List[stuff] groupByOrdered :) - trait HowToPage extends DocPage { override def categoryPosts = List( diff --git a/docs/src/files/howtos/MapToObject.scala b/docs/src/files/howtos/MapToObject.scala index 06d41d0..a08d121 100644 --- a/docs/src/files/howtos/MapToObject.scala +++ b/docs/src/files/howtos/MapToObject.scala @@ -15,32 +15,43 @@ object MapToObject extends HowToPage { "How To Map Flat Rows To Objects", frag( s""" - You can do arbitrary SQL commands here. - The most common one is `UPDATE`-ing some rows: + Suppose you have these data `class`-es: ```scala - // returns number of affected rows - def updateCustomers: Int = ctx.run { - sql${Consts.tq} - UPDATE customers - SET name = 'whatever' - WHERE name LIKE 'xyz_%' - ${Consts.tq}.update() - } + // table rows: + case class Customer(id: Int, name: String) derives SqlReadRow + case class Address(id: Int, name: String) derives SqlReadRow + + // left join result: + case class CustomerWithAddressOpt(c: Customer, a: Option[Address]) derives SqlReadRow ``` - --- + Now, when you do a `SELECT` you'll get a `Seq[CustomerWithPhoneOpt]`, a "flat" result, named tuple. + But, on your REST API you'd like to return a structured object: + ```scala + case class CustomerDTO(id: Int, name: String, addresses: Seq[String]) + ``` - But of course you can do other commands as well: + Squery has utilities for exactly that. + Let's see how `groupByOrderedOpt` works: ```scala - def createTable: Unit = ctx.run { - sql${Consts.tq} - CREATE TABLE customers( - id SERIAL PRIMARY KEY, - name VARCHAR - ) - ${Consts.tq}.update() - } + import ba.sake.squery.utils.* + + val groupedByCustomer: Map[Customer, Seq[Address]] = rowsLeftJoin.groupByOrderedOpt(_.c, _.a) + val dtos = groupedByCustomer.map { case (c, addresses) => + CustomerDTO(c.id, c.name, addresses.map(_.name)) + }.toSeq ``` + We can see that it returns a `Map[Customer, Seq[Address]]`, just as we wanted. + Then we just map over it and populate the DTO object, can't be simpler! + + --- + + This does a few thing for us: + - keeps the list of results *ordered*, so you don't have to sort it twice (once in DB, and again in memory) + - extracts the value that we need from the raw row result + - handles the `None` case + - handles the starting, empty Seq of results case + """.md ) diff --git a/squery/test/src/ba/sake/squery/dataTypes.scala b/squery/test/src/ba/sake/squery/dataTypes.scala index 3abce90..a700c71 100644 --- a/squery/test/src/ba/sake/squery/dataTypes.scala +++ b/squery/test/src/ba/sake/squery/dataTypes.scala @@ -3,6 +3,7 @@ package ba.sake.squery import java.util.UUID import java.time.Instant +// TODO remove street !? case class Customer(id: Int, name: String, street: Option[String]) derives SqlReadRow: def insertTuple = sql"(${name}, ${street})" diff --git a/squery/test/src/ba/sake/squery/utils/SeqUtilsSuite.scala b/squery/test/src/ba/sake/squery/utils/SeqUtilsSuite.scala index cf2dfef..10346b7 100644 --- a/squery/test/src/ba/sake/squery/utils/SeqUtilsSuite.scala +++ b/squery/test/src/ba/sake/squery/utils/SeqUtilsSuite.scala @@ -52,6 +52,10 @@ class SeqUtilsSuite extends munit.FunSuite { test("groupByOrderedOpt") { val groupedByCustomer = rowsLeftJoin.groupByOrderedOpt(_.c, _.a) + + val dtos = groupedByCustomer.map { case (c, addresses) => + CustomerDTO(c.id, c.name, addresses.map(_.name.get)) + }.toSeq assertEquals( groupedByCustomer, Map( @@ -63,4 +67,6 @@ class SeqUtilsSuite extends munit.FunSuite { ) } + case class CustomerDTO(id: Int, name: String, addresses: Seq[String]) + }