使用scala从spark中的数组数组中的结构中提取值

时间:2017-04-15 09:42:59

标签: apache-spark apache-spark-sql spark-dataframe

我正在使用scala将json数据读入spark数据框。 架构如下:

 root
 |-- metadata: array (nullable = true)
 |    |-- element: struct (containsNull = true)
 |    |    |-- playerId: string (nullable = true)
 |    |    |-- sources: array (nullable = true)
 |    |    |    |-- element: struct (containsNull = true)
 |    |    |    |    |-- matchId: long (nullable = true)

数据如下:

{ "metadata" : [ { "playerId" : "1234", "sources" : [ { "matchId": 1 } ] }, { "playerId": "1235", "sources": [ { "matchId": 1 } ] } ] }
{ "metadata" : [ { "playerId" : "1234", "sources" : [ { "matchId": 2 } ] }, { "playerId": "1248", "sources": [ { "score": 12.2 , "matchId": 1 } ] } ] }
{ "metadata" : [ { "playerId" : "1234", "sources" : [ { "matchId": 3 } ] }, { "playerId": "1248", "sources": [ { "matchId": 3 } ] } ] }

目标是找到playerId是否为1234且matchId是否为1,然后将isPlayed返回为true。源的结构不固定。可能有matchId以外的字段。

我写了一个udf,考虑元数据为WrappedArray [String]类型,并且能够读取playerId列

def hasPlayer = udf((metadata: WrappedArray[String], playerId: String) => { 
  metadata.contains(playerId)
  })

df.withColumn("hasPlayer", hasPlayer(col("metadata"), col("superPlayerId")))

但我无法弄清楚如何查询给定playerId的matchId字段。我尝试将字段作为WrappedArray [WrappedArray [Long]]读取,但它在metadata.sources.matchId列的withColumn中给出了类型转换异常。

我对Spark相对较新。任何帮助都将深表感谢。

干杯!

1 个答案:

答案 0 :(得分:2)

当您处理JSON时,请了解内置函数explode,它将包含WrappedArray的单个单元格转换为表示内部结构的多行。我认为这有帮助(两次):

df.select(explode($"metadata").as("metadata"))
  .select($"metadata.playerId", explode($"metadata.sources.matchId").as("matchId"))
  .filter($"matchId".equalTo(1))
  .select($"matchId", lit(true).as("isPlayed"))

基本上我使用explode创建多行(并重命名为方便的东西),将对象树导航到我想要的JSON字段,重复explode /重命名进程{{1} },并过滤掉所有不是matchId的内容。这样我最终可以使用1函数对lit的值进行“硬编码”,以获得名为true的全新列因为所有不是isPlayed的东西都消失了。

如果这不是您正在寻找的,希望它能为您提供一些指导。当您了解Spark时,1 library对您非常有帮助。