Scala 如何使用Spark DataFrames查询JSON数据列?
我有一个Cassandra表,为了简单起见,它看起来像: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
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
的合并架构将其类型设置为stringb
将是一个整数
让我们看看不同的方法是如何比较的。首先,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是tablegivingStructType
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)
}