如何创建一个play.api.libs.iteratee.Enumerator,它在给定的枚举器的项之间插入一些数据?

时间:2013-12-17 22:40:14

标签: mongodb scala playframework reactivemongo

我将Play框架与ReactiveMongo一起使用。大多数ReactiveMongo API都基于Play Enumerator。只要我从MongoDB中获取一些数据并以“原样”异步返回它,一切都很好。此外,使用Enumerator.map对数据进行转换(例如将BSON转换为String)是显而易见的。

但今天我遇到了一个问题,其底线缩小为以下代码。我浪费了一半时间试图创建一个Enumerator来消耗给定Enumerator中的项目并在它们之间插入一些项目。重要的是不要一次加载所有项目,因为可能有很多项目(代码示例只有两个项目“1”和“2”)。但在语义上它类似于集合的mkString。我相信它可以很容易地完成,但我能带来的最好 - 就是这段代码。使用Enumerator创建Concurrent.broadcast的非常相似的代码非常适合WebSockets。但即使这样也行不通。 HTTP响应永远不会回来。当我看Enumeratee时,它看起来应该提供这样的功能,但我找不到办法来做这个。

P.S。试图在chan.eofAndEnd中拨打Iteratee.mapDone,而chunked(enums >>> Enumerator.eof而不是chunked(enums)则没有帮助。有时响应会返回,但不包含正确的数据。我错过了什么?

def trans(in:Enumerator[String]):Enumerator[String] = {
  val (res, chan) = Concurrent.broadcast[String]

  val iter = Iteratee.fold(true) { (isFirst, curr:String) =>
    if (!isFirst)
      chan.push("<-------->")
    chan.push(curr)
    false
  }

  in.apply(iter)

  res
}

def enums:Enumerator[String] = {
  val en12 = Enumerator[String]("1", "2")

  trans(en12)
  //en12 //if I comment the previous line and uncomment this, it prints "12" as expected
}

def enum = Action {
  Ok.chunked(enums)
}

2 个答案:

答案 0 :(得分:2)

这是我的解决方案,我认为这种问题是正确的。欢迎提出意见:

def fill[From](
    prefix: From => Enumerator[From],
    infix: (From, From) => Enumerator[From],
    suffix: From => Enumerator[From]
    )(implicit ec:ExecutionContext) = new Enumeratee[From, From] {
  override def applyOn[A](inner: Iteratee[From, A]): Iteratee[From, Iteratee[From, A]] = {
    //type of the state we will use for fold
    case class State(prev:Option[From], it:Iteratee[From, A])

    Iteratee.foldM(State(None, inner)) { (prevState, newItem:From) =>
      val toInsert = prevState.prev match {
        case None => prefix(newItem)
        case Some(prevItem) => infix (prevItem, newItem)
      }

      for(newIt <- toInsert >>> Enumerator(newItem) |>> prevState.it)
        yield State(Some(newItem), newIt)

    } mapM {
      case State(None, it) => //this is possible when our input was empty
        Future.successful(it)
      case State(Some(lastItem), it) =>
        suffix(lastItem) |>> it
    }
  }
}

// if there are missing integers between from and to, fill that gap with 0
def fillGap(from:Int, to:Int)(implicit ec:ExecutionContext) = Enumerator enumerate List.fill(to-from-1)(0)
def fillFrom(x:Int)(input:Int)(implicit ec:ExecutionContext) = fillGap(x, input)
def fillTo(x:Int)(input:Int)(implicit ec:ExecutionContext) = fillGap(input, x)

val ints = Enumerator(10, 12, 15)
val toStr = Enumeratee.map[Int] (_.toString)

val infill = fill(
  fillFrom(5),
  fillGap,
  fillTo(20)
)

val res = ints &> infill &> toStr // res will have 0,0,0,0,10,0,12,0,0,15,0,0,0,0

答案 1 :(得分:0)

您写道,您正在使用WebSockets,那么为什么不为that使用专用解决方案?您编写的内容更适合Server-Sent-Events而不是WS。据我了解,您希望在将结果发送回客户端之前过滤结果?如果它是正确的那么你Enumeratee而不是枚举器。 Enumeratee是从 - 转变为。这是一段非常好的代码how to use Enumeratee。可能不是直接关于你需要什么,但我找到了我的项目的灵感。也许当您分析给定的代码时,您会找到最佳解决方案。

相关问题