Dynamic SQL Parameters with Anorm and Scala Play Framework
Asked Answered
H

3

15

Is it possible to dynamically create a list for anorm's "on" method?

I have a form with optional inputs and currently I check each Option and create a list with the defined Options and am trying to pass this through to anorm. Currently I get this compilation error

type mismatch; found : List[java.io.Serializable] required: (Any, anorm.ParameterValue[_])

I'm not sure how I would go about creating this list. Current code :

val onList = List(
        'school_id = input.school,
        if(input.rooms isDefined)       ('rooms -> input.rooms) else "None" ,
        if(input.bathrooms isDefined)   ('bathrooms -> input.bathrooms) else "None" ,
        if(input.houseType isDefined)   ('houseType -> input.houseType) else "None" ,
        if(input.priceLow isDefined)    ('priceLow -> input.priceLow) else "None" ,
        if(input.priceHigh isDefined)   ('priceHigh -> input.priceHigh) else "None" ,
        if(input.utilities isDefined)   ('utilities -> input.utilities) else "None" 
).filter(_!="None")
SQL("SELECT * FROM Houses WHERE " + whereString).on(onList).as(sqlToHouse *)

I've tried doing this because initially I thought it would be the same as

.on('rooms -> input.rooms, 'bathroom -> input.bathrooms... etc)

EDIT:

Code is now:

val onList = Seq(
        ('school_id -> input.school),
        if(input.rooms isDefined)       ('rooms -> input.rooms.get)         else None ,
        if(input.bathrooms isDefined)   ('bathrooms -> input.bathrooms.get) else None ,
        if(input.houseType isDefined)   ('houseType -> input.houseType.get) else None ,
        if(input.priceLow isDefined)    ('priceLow -> input.priceLow.get)   else None ,
        if(input.priceHigh isDefined)   ('priceHigh -> input.priceHigh.get) else None ,
        if(input.utilities isDefined)   ('utilities -> input.utilities.get) else None 
).filter(_!=None).asInstanceOf[Seq[(Any,anorm.ParameterValue[_])]]

using SQL command:

SQL("SELECT * FROM Houses WHERE " + whereString).on(onList:_*).as(sqlToHouse *)

Now getting the exception

[ClassCastException: java.lang.Integer cannot be cast to anorm.ParameterValue]
Hebraist answered 23/3, 2013 at 20:3 Comment(1)
How is this supposed to work? What does whereString look like?Chromoprotein
C
12

The important thing is that you have to create values of type ParameterValue. This is normally done using the toParameterValue() function.

One way would be to create a sequence of Options that you flatten:

val onList = Seq(
  Some('school_id -> input.school),
  input.rooms.map('rooms -> _),
  input.bathrooms.map('bathrooms -> _)
).flatten

This sequence can then be mapped to correct values:

SQL(
  "SELECT * FROM Houses WHERE " + whereString
).on(
  onList.map(v => v._1 -> toParameterValue(v._2)): _*
)

This can be simplified like this:

val onList = Seq(
  Some('school_id -> input.school),
  input.rooms.map('rooms -> _),
  input.bathrooms.map('bathrooms -> _)
).flatMap(_.map(v => v._1 -> toParameterValue(v._2)))

SQL(
  "SELECT * FROM Houses WHERE " + whereString
).on(
  onList: _*
)

Or maybe the simplest solution would be this:

val onList = Seq(
  Some('school_id -> toParameterValue(input.school)),
  input.rooms.map('rooms -> toParameterValue(_)),
  input.bathrooms.map('bathrooms -> toParameterValue(_))
).flatten

SQL(
  "SELECT * FROM Houses WHERE " + whereString
).on(
  onList: _*
)
Corpulence answered 25/3, 2013 at 14:49 Comment(1)
That's great! I didn't know about the toParameterValue function. ThanksHebraist
H
1

So I ended up just calling on multiple times.

var query = SQL("SELECT * FROM Houses WHERE " + whereString).on('school_id -> input.school)
if(input.rooms isDefined)       query= query.on('rooms -> input.rooms.get)
if(input.bathrooms isDefined)   query= query.on('bathrooms -> input.bathrooms.get)
if(input.houseType isDefined)   query= query.on('houseType -> input.houseType.get)
if(input.priceLow isDefined)    query= query.on('priceLow -> input.priceLow.get)
if(input.priceHigh isDefined)   query= query.on('priceHigh -> input.priceHigh.get)
if(input.utilities isDefined)   query= query.on('utilities -> input.utilities.get)
query.as(sqlToHouse *)
Hebraist answered 23/3, 2013 at 23:53 Comment(0)
B
1

You can have a look at multivalue parameter is next Anorm (coming Play 2.3/master).

Burkle answered 8/5, 2014 at 10:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.