Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB scala driver: what is a best way to return Future when working with Observer callbacks?

I am writing a simple blog on Play Framework and MongoDB using Scala driver. So it works and I'm happy but feel like my code is not good enough. Could you guys review the following snippet which is one of my mongo service methods and tell me if there is a way to make it more clean:

def findByTitle(title:String)(implicit ec:ExecutionContext):Future[Option[Document]] = {
    val collection = db.getCollection("items")
    val results = collection.find(equal("title", title))
    val contentPromise: Promise[Option[Document]] = Promise()
    results.subscribe(new Observer[scala.Document] {
      var empty: Boolean = true
      override def onError(e: Throwable): Unit = println("Error")

      override def onComplete(): Unit = {
        if (empty) contentPromise.success(None)
      }

      override def onNext(result: scala.Document): Unit = {
        empty = false
        contentPromise.success(Some(result))
      }
    })
    contentPromise.future
  }

I decided to return Future[Option[Document]] since there could be a chance that there is nothing by provided title. As far as the only way to work with Observable is via Observer callbacks I need to declare a Promise and then fulfill that promise in the onComplete() callback. To handle the case when there is no document by provided title I have no choice other than declaring this var empty: Boolean = true variable and then using it in onNext() and onComplete() callbacks.

Question: Is there a better way to approach this without using var inside my Observer instance?

like image 545
Alexander Arendar Avatar asked Jul 16 '16 21:07

Alexander Arendar


1 Answers

I think it would be far easier to work with a Future[T] instead of an observable:

import org.mongodb.scala.ObservableImplicits._

def findByTitle(title: String)(implicit ec: ExecutionContext): Future[Option[Document]] = {
  val collection = db.getCollection("it")
  collection.find(equal("title", title))
            .toFuture()
            .recoverWith { case e: Throwable => { Log(e); Future.failed(e) } }
            .map(_.headOption)
}

The T in the future is actually a Seq[T]. This way a simple check can eliminate the need for mutable state.

like image 155
Yuval Itzchakov Avatar answered Nov 19 '22 12:11

Yuval Itzchakov