0

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,

user708683
  • 500
  • 3
  • 9
  • 26
  • Have you tried `params.get("authorId")`? If there a single value, you will get a `Seq` with only one value, (that can be turned into `Option[String]` using `.headOption`). – cchantep May 04 '16 at 15:33
  • Possible duplicate of [Have a List in Play framework web service parameters](http://stackoverflow.com/questions/35814866/have-a-list-in-play-framework-web-service-parameters) – marcospereira May 04 '16 at 17:13
  • It's not a duplicate since i don't want to change the routes – user708683 May 04 '16 at 17:17
  • What do you want to query for? Could you describe it because I don't understand why you would want to query for two bookIds and one authorId. Do you want to search for authors or books? – Martijn May 04 '16 at 20:11
  • Are the multiple BookIds `OR` or `AND`. That's what I want to know. – Martijn May 04 '16 at 20:14
  • I want to search for both ! if param authorId is not empty, i search for authorId, if param booksId is not empty, i search for booksId, if authorId and booksId are not empty, i search for both of them. – user708683 May 04 '16 at 20:20

1 Answers1

0

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.

marcospereira
  • 12,045
  • 3
  • 46
  • 52
  • I want to send the values from my controller to my model. I updated my question :) – user708683 May 06 '16 at 20:14
  • If the values are coming from a request, you will have to get them at the controller and then pass them to your model. – marcospereira May 06 '16 at 20:16
  • That's what i'm trying to do, see my updated question – user708683 May 06 '16 at 20:20
  • Can i do something like this : whereHelper(params: Map[String, Seq[String]]) (a.author_id === params.getLong("author_id").?) and (b.bookId in params.get("books_id").?) cause that will avoid me to pass all my params in the the whereHelper. AuthorId can me a Seq and i will need to get his first occurence – user708683 May 06 '16 at 20:33
  • You can do something very similar, but I would advocate to have separated parameters as I show at the edited answer. Any reasons to receive a `params: Map[String, Seq[String]]` at your models instead of separated parameters? – marcospereira May 06 '16 at 20:45
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/111257/discussion-between-user708683-and-marcospereira). – user708683 May 06 '16 at 20:46
  • See edits after chat. Also, considering using a more typesafe code here, maybe using an `params: Map[String, Seq[Long]]` instead of a `params: Map[String, Seq[String]]`. – marcospereira May 06 '16 at 21:15
  • There's a way to just not use the cases and match? i just wanna keep it simple and have only where clauses in the helperWhere function, eg a.author_id ===params.getOrElse("authorId", Seq.empty).headOption.?) and a.params_2 ===params.getOrElse("params_2", Seq.empty).headOption.?) and a.param_3 ===params.getOrElse("params_3", Seq.empty).headOption.?) etc. – user708683 May 06 '16 at 21:32
  • Now i have a new param page : .page(Try(params.getOrElse("page", Seq.empty).map(_.toInt)).getOrElse(0) and i got an error now value * is not a member of Any ! – user708683 May 09 '16 at 14:49