Scala 如何使用Spark DataFrames查询JSON数据列?

Scala 如何使用Spark DataFrames查询JSON数据列?,scala,apache-spark,dataframe,apache-spark-sql,spark-cassandra-connector,Scala,Apache Spark,Dataframe,Apache Spark Sql,Spark Cassandra Connector,我有一个Cassandra表,为了简单起见,它看起来像: key: text jsonData: text blobData: blob val df = sqlContext.read .format("org.apache.spark.sql.cassandra") .options(Map("table" -> "mytable", "keyspace" -> "ks1")) .load() //You can define whatever struct ty

我有一个Cassandra表,为了简单起见,它看起来像:

key: text
jsonData: text
blobData: blob
val df = sqlContext.read
  .format("org.apache.spark.sql.cassandra")
  .options(Map("table" -> "mytable", "keyspace" -> "ks1"))
  .load()

//You can define whatever struct type that your json states
val schema = StructType(Seq(
  StructField("key", StringType, true), 
  StructField("value", DoubleType, true)
))

df.withColumn("jsonData", from_json(col("jsonData"), schema))
我可以使用spark和spark cassandra连接器为此创建一个基本数据框,使用:

val df = sqlContext.read
  .format("org.apache.spark.sql.cassandra")
  .options(Map("table" -> "mytable", "keyspace" -> "ks1"))
  .load()

我正在努力将JSON数据扩展到其底层结构中。我最终希望能够基于json字符串中的属性进行过滤,并返回blob数据。类似于jsonData.foo=“bar”并返回blobData。目前可能吗?

Spark>=2.4

如果需要,可以使用函数确定模式(请注意,这假设任意行是模式的有效代表)

火花>=2.1

您可以使用以下功能:

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

val schema = StructType(Seq(
  StructField("k", StringType, true), StructField("v", DoubleType, true)
))

df.withColumn("jsonData", from_json($"jsonData", schema))
火花>=1.6

您可以使用
get\u json\u object
获取列和路径:

import org.apache.spark.sql.functions.get_json_object

val exprs = Seq("k", "v").map(
  c => get_json_object($"jsonData", s"$$.$c").alias(c))

df.select($"*" +: exprs: _*)
并将字段提取到单个字符串,这些字符串可以进一步转换为预期类型

path
参数使用点语法表示,前导的
$。
表示文档根(因为上面的代码使用字符串插值
$
必须转义,因此
$。


Spark底层JSON字符串是

"{ \"column_name1\":\"value1\",\"column_name2\":\"value2\",\"column_name3\":\"value3\",\"column_name5\":\"value5\"}";
下面是过滤JSON并将所需数据加载到Cassandra的脚本

  sqlContext.read.json(rdd).select("column_name1 or fields name in Json", "column_name2","column_name2")
            .write.format("org.apache.spark.sql.cassandra")
            .options(Map("table" -> "Table_name", "keyspace" -> "Key_Space_name"))
            .mode(SaveMode.Append)
            .save()

from_json的
函数正是您想要的。您的代码将类似于:

key: text
jsonData: text
blobData: blob
val df = sqlContext.read
  .format("org.apache.spark.sql.cassandra")
  .options(Map("table" -> "mytable", "keyspace" -> "ks1"))
  .load()

//You can define whatever struct type that your json states
val schema = StructType(Seq(
  StructField("key", StringType, true), 
  StructField("value", DoubleType, true)
))

df.withColumn("jsonData", from_json(col("jsonData"), schema))
我使用以下方法

(从2.2.0开始提供,我假设您的json字符串列位于列索引0处)

它将自动推断JSON中的模式。此处记录:

是彻底的,但遗漏了Spark 2.1+中可用的一种方法,它比使用json()的模式更简单、更健壮。:

import org.apache.spark.sql.functions.from_json
val json_schema=spark.read.json(df.select(“jsonData”).as[String]).schema
withColumn(“jsonData”,来自_json($“jsonData”,json_模式))
下面是Python的等价物:

from pyspark.sql.functions从_json导入
json_schema=spark.read.json(df.select(“jsonData”).rdd.map(lambda x:x[0]).schema
withColumn(“jsonData”,来自json(“jsonData”,json\U模式))
正如zero323指出的那样,json()的模式的问题在于它检查单个字符串并从中派生模式。如果JSON数据具有不同的模式,那么从
schema\u of_JSON()
返回的模式将不会反映如果将所有JSON数据的模式合并到数据帧中会得到什么。使用来自_json()的
解析该数据将产生大量的
null
或空值,其中_json()
schema\u返回的模式与数据不匹配

通过使用Spark从JSON字符串的RDD派生出全面的JSON模式的能力,我们可以保证所有JSON数据都可以被解析

示例:
schema\u of_json()
vs.
spark.read.json()
下面是一个示例(在Python中,代码与Scala非常相似),以说明使用
schema\u of_json()
从单个元素派生模式与使用
spark.read.json()
从所有数据派生模式之间的区别

df=spark.createDataFrame( ... [ …(1,“{a:true}”), …(2,“{a:“你好”}”), …(3,“{b:22}”), ... ], …架构=['id','jsonData'], ... )
a
在一行中有一个布尔值,在另一行中有一个字符串值。
a
的合并架构将其类型设置为string
b
将是一个整数

让我们看看不同的方法是如何比较的。首先,json()的
schema\u方法:

>>json\u schema=schema\u of_json(df.select(“jsonData”).take(1)[0][0])
>>>解析的_json_df=df.withColumn(“jsonData”,来自于_json(“jsonData”,json_模式))
>>>已解析的_json_df.printSchema()
根
|--id:long(nullable=true)
|--jsonData:struct(nullable=true)
||--a:布尔值(nullable=true)
>>>解析的_json_df.show()
+---+--------+
|id| jsonData|
+---+--------+
|1 |[正确]|
|2 |零|
|  3|      []|
+---+--------+
如您所见,我们派生的JSON模式非常有限<代码>“a”:“hello”
无法解析为布尔值并返回
null
“b”:22
刚刚被删除,因为它不在我们的架构中

现在使用
spark.read.json()

json_schema=spark.read.json(df.select(“jsonData”).rdd.map(lambda x:x[0]).schema >>>解析的_json_df=df.withColumn(“jsonData”,来自于_json(“jsonData”,json_模式)) >>>已解析的_json_df.printSchema() 根 |--id:long(nullable=true) |--jsonData:struct(nullable=true) ||--a:string(nullable=true) ||--b:long(nullable=true) >>>解析的_json_df.show() +---+--------+ |id| jsonData| +---+--------+ |1 |[正确,]| |2 |[你好,]| | 3| [, 22]| +---+--------+ 在这里,我们保留了所有的数据,并且有一个全面的模式来解释所有的数据
“a”:true
被强制转换为字符串,以匹配
“a”:“hello”
的架构

使用
spark.read.json()
的主要缺点是spark将扫描所有数据以派生模式。根据您拥有的数据量,这种开销可能会很大。如果您知道所有JSON数据都有一个一致的模式,那么可以继续对单个元素使用
schema\u of_JSON()
。如果您有模式可变性,但不想扫描所有数据,可以在调用
spark.read.json()
时将
samplingario
设置为小于
1.0
的值,以查看数据的子集


以下是
spark.read.json()
:/

的文档
key
是唯一标识符吗?是的,key是tablegiving
StructType
  sqlContext.read.json(rdd).select("column_name1 or fields name in Json", "column_name2","column_name2")
            .write.format("org.apache.spark.sql.cassandra")
            .options(Map("table" -> "Table_name", "keyspace" -> "Key_Space_name"))
            .mode(SaveMode.Append)
            .save()
val df = sqlContext.read
  .format("org.apache.spark.sql.cassandra")
  .options(Map("table" -> "mytable", "keyspace" -> "ks1"))
  .load()

//You can define whatever struct type that your json states
val schema = StructType(Seq(
  StructField("key", StringType, true), 
  StructField("value", DoubleType, true)
))

df.withColumn("jsonData", from_json(col("jsonData"), schema))
def parse(df: DataFrame, spark: SparkSession): DataFrame = {
    val stringDf = df.map((value: Row) => value.getString(0), Encoders.STRING)
    spark.read.json(stringDf)
}