1

I'm trying to support arbitrary filters for a REST API that fetches a list of documents from MongoDB. For instance

  • //example.com/users <- list all
  • //example.com/users?age=30 <- all users who are 30
  • //example.com/users?age=30&name=John <- all users who are 30 and called John
  • ...

I'm using Play-ReactiveMongo and dealing with JSONCollection objects only.

So in my routes I put

GET   /users        controllers.Users.list(id: Option[String], name: Option[String], age: Option[Int])

But there are two problem with that, first I'll need to have a pretty long list of optional parameters, and then in my controller I need to use pattern matching on all of them to check whether they're empty or not, and also build the selector that I use to filter my collection.

var filters = JsObject(Nil)
name match {
  case Some(x) => filters += ("name" -> JsString(x))
  case None => None
}

I realized that I can get the full query string from the request object, which is a Map[String, Seq[String]]. But then I don't know a good way to check whether values are String or something else.

Is there another better and idiomatic way to do what I want?

Henry
  • 11
  • 3

2 Answers2

0

Possible solution can be:

  1. Use POST instead of GET: POST /example.com/users "data"={"age":25, "name":"xyz", ... }
  2. OR single param in GET: GET /example.com/users?filter={"age":25, "name":"xyz", ... }

On the server side, just validate against your model class OR just pass the same json in your reactivemongo find method.

0

Maybe request binders will help you to create complex objects from varying request parameters.

https://www.playframework.com/documentation/2.5.x/ScalaRequestBinders#QueryStringBindable

For example, you could build something like this (from the docs):

case class AgeRange(from: Int, to: Int)

for requests like this:

/age?from=1&to=10

Now you could change these attributes to Option and create a function that creates a reactivemongo query based on the values at hand.

rethab
  • 7,170
  • 29
  • 46