elasticsearch,apache-spark,etl,apache-spark-sql,Scala,elasticsearch,Apache Spark,Etl,Apache Spark Sql" /> elasticsearch,apache-spark,etl,apache-spark-sql,Scala,elasticsearch,Apache Spark,Etl,Apache Spark Sql" />

Scala 如何向DataFrame添加新的结构列

Scala 如何向DataFrame添加新的结构列,scala,elasticsearch,apache-spark,etl,apache-spark-sql,Scala,elasticsearch,Apache Spark,Etl,Apache Spark Sql,我目前正试图从MongoDB中提取一个数据库,并使用Spark将geo_points引入ElasticSearch Mongo数据库具有纬度和经度值,但ElasticSearch要求将它们转换为geo_point类型 Spark中是否有方法将lat和lon列复制到数组或结构的新列中 感谢您的帮助 我假设您从以下某种平面模式开始: root |-- lat: double (nullable = false) |-- long: double (nullable = false) |-- k

我目前正试图从MongoDB中提取一个数据库,并使用Spark将
geo_points
引入ElasticSearch

Mongo数据库具有纬度和经度值,但ElasticSearch要求将它们转换为
geo_point
类型

Spark中是否有方法将
lat
lon
列复制到
数组或
结构的新列中


感谢您的帮助

我假设您从以下某种平面模式开始:

root
 |-- lat: double (nullable = false)
 |-- long: double (nullable = false)
 |-- key: string (nullable = false)
首先让我们创建示例数据:

import org.apache.spark.sql.Row
import org.apache.spark.sql.functions.{col, udf}
import org.apache.spark.sql.types._

val rdd = sc.parallelize(
    Row(52.23, 21.01, "Warsaw") :: Row(42.30, 9.15, "Corte") :: Nil)

val schema = StructType(
    StructField("lat", DoubleType, false) ::
    StructField("long", DoubleType, false) ::
    StructField("key", StringType, false) ::Nil)

val df = sqlContext.createDataFrame(rdd, schema)
一种简单的方法是使用udf和case类:

case class Location(lat: Double, long: Double)
val makeLocation = udf((lat: Double, long: Double) => Location(lat, long))

val dfRes = df.
   withColumn("location", makeLocation(col("lat"), col("long"))).
   drop("lat").
   drop("long")

dfRes.printSchema
我们得到了

root
 |-- key: string (nullable = false)
 |-- location: struct (nullable = true)
 |    |-- lat: double (nullable = false)
 |    |-- long: double (nullable = false)
一个困难的方法是转换数据,然后应用模式:

val rddRes = df.
    map{case Row(lat, long, key) => Row(key, Row(lat, long))}

val schemaRes = StructType(
    StructField("key", StringType, false) ::
    StructField("location", StructType(
        StructField("lat", DoubleType, false) ::
        StructField("long", DoubleType, false) :: Nil
    ), true) :: Nil 
)

sqlContext.createDataFrame(rddRes, schemaRes).show
我们得到了预期的输出

+------+-------------+
|   key|     location|
+------+-------------+
|Warsaw|[52.23,21.01]|
| Corte|  [42.3,9.15]|
+------+-------------+
从头开始创建嵌套模式可能会很乏味,因此如果可以的话,我推荐第一种方法。如果您需要更复杂的结构,可以轻松扩展:

case class Pin(location: Location)
val makePin = udf((lat: Double, long: Double) => Pin(Location(lat, long))

df.
    withColumn("pin", makePin(col("lat"), col("long"))).
    drop("lat").
    drop("long").
    printSchema
我们得到了预期的产出:

root
 |-- key: string (nullable = false)
 |-- pin: struct (nullable = true)
 |    |-- location: struct (nullable = true)
 |    |    |-- lat: double (nullable = false)
 |    |    |-- long: double (nullable = false)
不幸的是,您无法控制
nullable
字段,所以如果它对您的项目很重要,您就必须指定模式

最后,您可以使用1.4中介绍的
struct
函数:

import org.apache.spark.sql.functions.struct

df.select($"key", struct($"lat", $"long").alias("location"))
试试这个:

import org.apache.spark.sql.functions._

df.registerTempTable("dt")

dfres = sql("select struct(lat,lon) as colName from dt")

感谢@zero323的详尽回答!这对很多人都有帮助。您知道我如何递归地为嵌套类型进行映射吗?这个数据比我希望的更糟糕。我看不出你为什么不能。嗨@zero323-你知道如果新结构中有超过10列,你是否可以使用你的UDF方法来创建一个结构?UDF似乎对10个输入变量有限制。@PatrickMcGloin确实有帮助吗?您好@zero323。实际上,你最后一次对“struct”函数的建议帮助了我。我应该先读到底的!