1

I have a query I'm conditionally enhancing dependent on the presence or absence of count and offset parameters.

val retrieveCustomer: (Option[String], Option[Int], Option[Int]) => List[Customer] = { ( customerId : Option[String], count : Option[Int], offset : Option[Int] ) =>
    val initialQ: Query[CustomerTable.type, Customer] = customerId.map( c => CustomerTable.where(_.id === c) ).getOrElse( CustomerTable.map { c => c } )
    val qPlusOffset = offset.map ( offset => initialQ.drop(offset) ).getOrElse(initialQ)
    val qoPlusLimit = count.map( count => qPlusOffset.take(count) ).getOrElse(qPlusOffset)

    DBGlobal.db.withTransaction { qoPlusLimit.list }

}

I'm curious if there's a more concise way to write this compared to this approach.

Pray tell.

Rasputin Jones
  • 1,427
  • 2
  • 16
  • 24

2 Answers2

2

What is the point of writing this?

val retrieveCustomer: (Option[String], Option[Int], Option[Int]) => List[Customer] = { ( customerId : Option[String], count : Option[Int], offset : Option[Int] )

When you can write something like this:

def getCustomer(id: String, count: Option[Int], offset: Option[Int]): List[Customer]

That is obviously more concise, although likely has nothing to do with the concision you're after. I'd be inclined to go with Either Right projections on each operation in a for{...} block, "failed" Left outcomes executing the base or non-fully refined query, and successful Right outcome executing the full drop + take refinements.

def getCustomer(id: String, count: Option[Int], offset: Option[Int]): List[Customer] = { 
    val base = Customers.createFinder(_.id)
    val q = for{
      cust    <- base(id) toRight( List[Customer]() )
      dropped <- cust.drop(offset) toRight(cust)
      taken   <- dropped.take(count) toRight(dropped)
    } yield taken
    DBGlobal.db.withTransaction { q.list map { _.fold(_,_) } }
}

Unlikely that this compiles ;-), but the general principle is to thread through Left/Right query outcomes that both return a List of Customers.

Note: I tend to name mapper companion object in the plural form of case class, so in this case, instead of CustomerTable, I used Customers. Also, Customers.createFinder(_.id) desugars to:

for{
  id <- Parameters[String]
  c  <- Customers if c.id is id
} yield c

which lazily generates an efficient prepared statement for underlying DBMS.

What you have works of course, looks fine for one-off cases. Personally, I'm digging for{} + Either right projections, can have your cake and eat it too -- i.e. get both point-of-failure Left outcome and successful Right outcome, as opposed to for{...} through Option, which only yields a successful event, providing no information at all about which step actually went wrong.

virtualeyes
  • 11,147
  • 6
  • 56
  • 91
0

Here's a one liner.

val qFinal = initialQ.drop(offset.getOrElse(0)).take(count.getOrElse(Int.MaxValue))
Rasputin Jones
  • 1,427
  • 2
  • 16
  • 24