mavarazy
mavarazy

Reputation: 7735

How to conditionally add new Enumerators in existing scala Enumerator

My stack is: Scala 2.11.6, ReaciveMongo 0.11.6, Play 2.4.2, Mongo 3.0.4

I have a list of queries to mongo, which I need to perform conditionally one after another (If number of results is less then MAX possible).

As a solution I'm doing following:

possibleQueries.
    // find returns Enumerator[JsObject]
    map(query => searchableCollection.find(query)).
    // combine Enumerators with andThen
    foldLeft(Enumerator.empty[JsObject])({ (e1, e2) => e1.andThen(e2) }) through 
    // Take only specified amount of suggestions
    take[JsObject](MAX_AMOUNT)
  1. Combine queries with andThen composition
  2. Limiting number of results with Enumeratee.take[Int](n).

The problem with this, is that it eagerly searches for data with searchableCollection.find(query) (maybe I'm wrong), and might not even fetch returned results, if previous query returns MAX_AMOUNT of results.

How to rewrite this, so that search would be called only if previous Enumeration has not filled MAX_AMOUNT of responses?

UPDATE

The solution I went with is

implicit class EnumeratorExtension[E](parent: Enumerator[E]) {

  /**
   * Create an Enumeratee that combines parent & e if parent was empty.
   */
  def andIfEmpty(e: => Enumerator[E]): Enumerator[E] = new Enumerator[E] {
    def apply[A](i: Iteratee[E, A]): Future[Iteratee[E, A]] = {
      var empty = true
      parent.
        map(e => {
          empty = false
          e
        })(defaultExecutionContext).
        apply(i).
        flatMap(r => {
          if (empty)
            e.apply(r)
          else
            Future.successful(r)
        })(defaultExecutionContext)
    }
  }

}

Upvotes: 1

Views: 99

Answers (1)

thirstycrow
thirstycrow

Reputation: 2824

How about wrap the search and make it lazy?

class LazyEnumerator[E](e: => Enumerator[E]) extends Enumerator[E] {
  lazy _e = e
  def apply[A](i: Iteratee[E, A]) = _e.apply(i)
}

possibleQueries.
    map(query => new LazyEnumerator(searchableCollection.find(query))).
    foldLeft(Enumerator.empty[JsObject])({ (e1, e2) => e1.andThen(e2) }) through 
    take[JsObject](MAX_AMOUNT)

Upvotes: 1

Related Questions