Scala 将Akka Graph DSL流返回类型转换为Future[T],而不是T(等待)

Scala 将Akka Graph DSL流返回类型转换为Future[T],而不是T(等待),scala,akka,akka-stream,Scala,Akka,Akka Stream,考虑以下类别: class MongoDumpService @Inject()(eventsDao: EventDAO)(implicit val ec: ExecutionContext, mat: Materializer) extends LazyLogging { private[services] def toAssetsWriterSink: Sink[List[Asset], FileDetails] = ParquetService.toParquetSingleFile

考虑以下类别:

class MongoDumpService @Inject()(eventsDao: EventDAO)(implicit val ec: ExecutionContext, mat: Materializer) extends LazyLogging {

  private[services] def toAssetsWriterSink: Sink[List[Asset], FileDetails] = ParquetService.toParquetSingleFile[List[Asset]](AppConfig.AssetsFileName)
  private[services] def toExpenseWriterSink: Sink[List[Expense], FileDetails] = ParquetService.toParquetSingleFile[List[Expense]](AppConfig.ExpensesFileName)
  private[services] def toReportsWriterSink: Sink[List[Report], FileDetails] = ParquetService.toParquetSingleFile[List[Report]](AppConfig.ReportsFileName)
  private[services] def toTransactionsWriterSink: Sink[List[Transaction], FileDetails] = ParquetService.toParquetSingleFile[List[Transaction]](AppConfig.TransactionsFileName)
  private[services] def toEventsWriterSink: Sink[PacificOriginalEvent, FileDetails] = ParquetService.toParquetSingleFile[PacificOriginalEvent](AppConfig.PacificOriginalEventFileName)


  def createMongoDump(recordingId: BSONObjectID, maxDocs: Option[Int] = None): List[FileDetails] = RunnableGraph.fromGraph(
    GraphDSL.create(toAssetsWriterSink, toExpenseWriterSink, toReportsWriterSink, toTransactionsWriterSink, toEventsWriterSink, sharedKillSwitch.flow[Event])((f1,f2,f3,f4,f5,_) => List(f1,f2,f3,f4,f5)) {
      import GraphDSL.Implicits._
      implicit builder =>
        (writeAssets, writeExpenses, writeReports, writeTransactions, writerEvents, sw) =>

          val source    = builder.add(eventsDao.getEventsSource(recordingId.stringify, maxDocs))
          val broadcast = builder.add(Broadcast[Event](5))

          source ~> sw ~> broadcast
                          broadcast.out(Write.PacificEvents).map(_.pacificEvent)                                     ~> writerEvents
                          broadcast.out(Write.Expenses).filter(_.expenses.isDefined).map(_.expenses.get)             ~> writeExpenses
                          broadcast.out(Write.Assets).filter(_.assets.isDefined).map(_.assets.get)                   ~> writeAssets
                          broadcast.out(Write.Reports).filter(_.reports.isDefined).map(_.reports.get)                ~> writeReports
                          broadcast.out(Write.Transactions).filter(_.transactions.isDefined).map(_.transactions.get) ~> writeTransactions

          ClosedShape

    }).run()

}
此代码返回
List[FileDetails]]
,它实际将
事件
对象写入到它应该写入的文件中,该对象包括
选项[List[T]
的一些字段,例如
fieldA~>writerFieldA
等等 问题如下:

我想等待,直到此操作完成,因为这将上载到
S3
文件
0KB

 private[actors] def uploadDataToS3(recording: Recording) = {
    logger.info(s"Uploading data to S3 with recordingId: ${recording._id.stringify}")
    val details = mongoDumpService.createMongoDump(recording._id, recording.limit)
    s3Service.uploadFiles(recording._id.stringify, details)
  }
如果没有图形DSL,我可以执行
runWith
witch返回
Future[…]

如何使用graphDSL实现这一点?(我想返回
Future[List[FileDetails]]]

编辑: 添加了toParquetSingleFile

 def toParquetSingleFile[In](fileName: String)(implicit
                                                ec: ExecutionContext,
                                                mat: Materializer,
                                                writes: Writes[In]): Sink[In, FileDetails] = {
    val absolutePath = TEMP_DIRECTORY + File.separator + s"$fileName.${FileExtension.PARQUET.toSuffix}"
    toJsString[In]
      .log(s"ParquetService", _ => s"[✍️] - Writing element toParquetSingleFile for path: $absolutePath ...")
      .withAttributes(Attributes.logLevels(onFailure = LogLevels.Error, onFinish = LogLevels.Off, onElement = LogLevels.Info))
      .to(
        ParquetStreams.toParquetSingleFile(
          path = absolutePath,
          options = ParquetWriter.Options(
            writeMode = ParquetFileWriter.Mode.OVERWRITE,
            compressionCodecName = CompressionCodecName.GZIP))
      ).mapMaterializedValue(_ => FileDetails(absolutePath, FileExtension.PARQUET))
  }
解决方案:
正如我所看到的,这个toparquestinglefile创建了一个Sink,其中Future[Done]作为物化值。但是,在您的函数中,您通过mapMaterializedValue一个FileDetails实例返回。我认为您使用的mapMaterializedValue函数接受

mapMaterializedValue(mat: Future[Done] => Mat2)
因此,如果您将Future[Done]映射到Future[FileDetails],您将拥有一个列表[Future[FileDetails]],您可以使用Future sequence操作或其他方法将其展平,以获得Future[List[FileDetails]]

尝试模拟您的场景时,您有一个函数,可以创建一个写入文件并实现未来的接收器[完成]:

case class FileDetails(absPath: String, fileExtension: Int)

def sink[In] : Sink[In, Done] = ???
从函数中删除mapMaterializedValue,您将获得类似于上面的内容

然后,创建映射具体化值的函数:

def mapMatValue[In](in: Sink[In, Future[Done]]) =
  in.mapMaterializedValue(result => result.map(_ => FileDetails("path", 0))
使用它,您的createMongoDump应该返回Sink[In,List[Future[FileDetails]]


最后,使用Future.sequence(list)获得Future[list[Future.sequence]]。您也可以使用遍历。

您如何知道它完成了?该值由接收器的物化值固定。您可以添加“toparquetSingleFile”的代码吗?@EmiCareOfCell44已编辑,谢谢!那么需要如何实现
mapMaterializedValue
?我的意思是
Future[Done]。转换(=>FileDetails(absolutePath,FileExtension.PARQUET),identity)
?你能写实现吗?谢谢!谢谢,我做不到,我用接收器重新编辑,你能尝试附加
mapMaterializedValue
?它总是告诉我我得到了
HNil
,这是一个编译错误吗?你不能包含错误吗?它使用的是不成形的,我从来没有使用过这个框架。我添加了t他回答:谢谢!
def mapMatValue[In](in: Sink[In, Future[Done]]) =
  in.mapMaterializedValue(result => result.map(_ => FileDetails("path", 0))