内联映射功能而不是循环

时间:2019-05-12 17:04:18

标签: scala apache-spark

我在数据框中有一个表,其中有三列。 city_name,driver_name,车辆列表中的车辆。

我也有一些其他详细信息,例如mysql中每个驱动程序的驱动程序小时数,驱动程序联系方式等。数据库中的表采用以下格式:city_name.driver_name。

scala> val tables = """
[
                {"vehicles" : ["subaru","mazda"], "city_name" : "seattle", "driver_name" : "x"},
                {"city_name" : "seattle", "driver_name" : "y"},
                {"city_name" : "newyork", "driver_name" : "x"},
                {"city_name" : "dallas", "driver_name" : "y"}                         
]
"""     |      |      |      |      |      |      | 
tables: String =
"
[
                {"vehicles" : ["subaru","mazda"], "city_name" : "seattle", "driver_name" : "x"},
                {"city_name" : "seattle", "driver_name" : "y"},
                {"city_name" : "newyork", "driver_name" : "x"},
                {"city_name" : "dallas", "driver_name" : "y"}
]
"

scala> val metadataRDD = sc.parallelize(tables.split('\n').map(_.trim.filter(_ >= ' ')).mkString :: Nil)   
metadataRDD: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[5] at parallelize at <console>:30

scala>     val metadataDF = spark.read.json(metadataRDD)
metadataDF: org.apache.spark.sql.DataFrame = [city_name: string, driver_name: string ... 1 more field]

scala> metadataDF.show
+---------+-----------+---------------+
|city_name|driver_name|       vehicles|
+---------+-----------+---------------+
|  seattle|          x|[subaru, mazda]|
|  seattle|          y|           null|
|  newyork|          x|           null|
|   dallas|          y|           null|
+---------+-----------+---------------+

对于这些驱动程序中的每一个,我需要应用一个功能并写入镶木地板。我想要做的是使用如下所示的内联函数,但无法正常工作:

metadataDF.map((e) => {
        val path = "s3://test/"
        val df = sparkJdbcReader.option("dbtable",  
                 e.city_name + "." + e.driver_name).load()

        val dir = path + e.driver_name + e.city_name

        if (e.vehicles)
          do something
        else:
          df.write.mode("overwrite").format("parquet").save(dir)
  })

基本上,问题在于如何使用该内联函数。

1 个答案:

答案 0 :(得分:0)

map()函数的调用总是使用提供的函数将类型A的给定输入集合转换为类型B的另一个集合。在地图函数调用中,您将数据框保存到存储层[大概是HDFS]。在DataFrameWriter Class上定义的save()方法的返回类型为 Unit (在Java中将其视为 void )。因此,您的函数将无法正常工作,因为它将DataFrame转换为两种类型:if块返回的数据类型和else块返回的Unit。

您可以重构您的代码并将其分为两个块:

import org.apache.spark.sql.functions.{concat,concat_ws,lit,col}
import org.apache.spark.sql.DataFrame
import org.apache.spark.rdd.RDD    

val metadataRDD: RDD[String] = sc.parallelize(tables.split('\n').map(_.trim.filter(_ >= ' ')).mkString :: Nil)

val metadataDF: DataFrame = spark.read.json(metadataRDD)

val df_new_col: DataFrame = metadataDF
.withColumn("city_driver",concat_ws(".",col("city_name"),col("driver_name")))
.withColumn("dir",concat(lit("s3://test/"),col("city_name"),col("driver_name")))

您现在有两列,其中有表名及其旁边的路径。您可以收集它们并使用它们读取要以Parquet格式存储的数据框。