我将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)
}
答案 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。可能不是直接关于你需要什么,但我找到了我的项目的灵感。也许当您分析给定的代码时,您会找到最佳解决方案。