Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return Enumerator of JSON from ReactiveMongo in Play 2

In our project we are using ReactiveMongo with Play 2.2.1.

The problem is, that to stream of data in a form of Enumerator[A], returned by ReactiveMongo is actually a stream of value objects, which are not separated by commas, and do not have stream beginning and end annotations, which can be treated as array opening and close statements.

This creates a problem for JSON consumer JS client, since the expected format is [A1,A2, ...]

So we jumped in hoops, and transformed our Enumeratee[A] to Enumerator[String], with checking, if it's the first element, or not:

    var first:Boolean = true
    val aToStrs = (as.map(a => {
        if(first) {
            first = false;
            Json.stringify(Json.toJson(a))
        } else {
            "," + Json.stringify(Json.toJson(a))
        }
   }))
   Ok.chunked(
       Enumerator.enumInput(Input.El("[")) andThen
       aToStrs andThen
       Enumerator.enumInput(Input.El("]")) andThen
       Enumerator.enumInput(Input.EOF)
   )

This works, but feels like inventing the wheel.

Is there a better solution, for this common problem?

like image 767
mavarazy Avatar asked Nov 11 '22 12:11

mavarazy


1 Answers

If you use comet or EventSource you wont have to hand craft a way to generate output and you will also actually be able to parse the response item for item in the client. Responding with an array would force you to either write your own parsing code or wait until everything has arrived on the client side before you can use the build int JSON parser in your JavaScript.

Streaming with the EventSource protocol is pretty easy with play, you should be able to do something like:

implicit val cometEncoder = new Comet.CometMessage[JsValue](_.toString)
Ok.chunked(yourEnumerator &> EventSource()).as(EVENT_STREAM)

And then in the client html:

<script type="text/javascript">
  var es = new EventSource(jsRouter.controllers.Application.streamIt().url)

  es.addEventListener("message", function (event) {
    var item = JSON.parse(event.data)
    // ... do something with the json value ...
  })
</script>

There is an example of this in the play sample projects that you might want to look at as well $YOUR_PLAY_DIR/samples/scala/eventsource-clock/

like image 103
johanandren Avatar answered Nov 14 '22 22:11

johanandren