Scala/Play/Squeryl Retrieve multiple params

2019-09-03 23:12发布

问题:

I have the following url : http://localhost/api/books/?bookId=21&bookId=62?authorId=2

I want to retrieve all the bookId values with Scala and then use Squeryl to do a fetch in a the database.

I'm using the PlayFrameWork as the WebServer, so here's my code :

val params = request.queryString.map { case (k, v) => k -> v(0) } // Retrieve only one the first occurence of a param

So params.get("bookId") will only get the last value in the bookId params. e-g : 62.

To retrieve all my bookId params i tried this : val params = request.queryString.map { case (k, v) => k -> v } so i can get a Seq[String], but what about the authorId which is not a Seq[String]? .

At the end i want to fetch the bookIds and authorId in my DB using Squeryl :

(a.author_id === params.get("authorId").?) and
(params.get("bookId").map(bookIds: Seq[String] => b.bookId in bookIds))

In my controller i get the params and open the DB connection :

val params = request.queryString.map { case (k, v) => k -> v(0) }

DB.withTransaction() { where(Library.whereHelper(params)}

In my model i use the queries :

def whereHelper(params : Map[String,String]) = {

 (a.author_id === params.get("authorId").?) and
 (params.get("bookId").map{bookIds: Seq[String] => b.bookId in bookIds})
}

Since bookIds is a list, i need to use the Seq[String]. There's a way to use request.queryString.map { case (k, v) => k -> v } for both a string (authorId) and a list of strings (bookIds) ?

Thanks,

回答1:

If I really understand what you are trying to do, you want to know how to get the parameters from queryString. This is pretty simple and you can do the following at your controller:

def myAction = Action { request =>
    // get all the values from parameter named bookId and 
    // transforming it to Long. Maybe you don't want the map
    // and then you can just remove it.
    val bookIds: Seq[Long] = request.queryString("bookId").map(_.toLong)

    // Notice that now I'm using getQueryString which is a helper
    // method to access a queryString parameter. It returns an
    // Option[String] which we are mapping to a Option[Long].
    // Again, if you don't need the mapping, just remove it.
    val authorId: Option[Long] = request.getQueryString("authorId").map(_.toLong)

    DB.withTransaction() { where(Library.whereHelper(authorId, bookIds) }

    // Do something with the result
}

At your model you will have:

def whereHelper(authorId: Option[Long], booksId: List[Long]) = authorId match {
  case Some(author_id) =>
      (a.author_id === author_id) and
      (b.bookId in bookIds)
  case None =>
      (b.bookId in bookIds)
}

I've left explicit types to help you understand what is happen. Now, since you have both values, you can just use the values at your query.


Edit after chat:

But, since you want to receive a params: Map[String, Seq[String]] at your models and is just having problems about how to get the authorId, here is what you can do:

def whereHelper(params: Map[String, Seq[String]]) = {
  // Here I'm being defensive to the fact that maybe there is no
  // "booksIds" key at the map. So, if there is not, an Seq.empty
  // will be returned. map method will run only if there is something
  // at the Seq.
  val booksIds = params.getOrElse("booksIds", Seq.empty).map(_.toLong)

  // The same defensive approach is being used here, and also getting
  // the head as an Option, so if the Seq is empty, a None will be 
  // returned. Again, the map will be executed only if the Option
  // is a Some, returning another Some with the value as a Long.
  val authorId = params.getOrElse("authorId", Seq.empty).headOption
  authorId.map(_.toLong) match {
    case Some(author_id) =>
      (a.author_id === author_id) and
      (b.bookId in booksIds)
    case None =>
      (b.bookId in booksIds)
  }
}

Of course, more parameters you have, more complicated this method will be.