apache-spark - Spark Select 对不同对象的 JSON 数组进行选择
问题描述
在 Databricks/Spark/Python(使用 pyspark 的 Spark 版本 2.4.0)中,我从 MongoDB 中获取了一个集合,其中包含一个可以嵌套的不同对象数组。我想将其转换为我可以选择的某种架构/结构。
我尝试了许多不同的方法,但找不到将其转换为模式/结构的优雅方法。
简化的 JSON:
{
"id" : "abc123",
"parent" : [
{
"field1" : "1"
},
{
"field1" : "11"
},
{
"field2" : "2",
"field3" : {
"field3a" : "3a",
"field3b" : "3b"
},
},
{
"field4" : "4",
"field5" : "5",
},
{
"field4" : "44",
"field5" : "55",
}
]
}
父级下的对象在父级之间可能不同,因此为所有情况定义特定模式过于复杂。另请注意,对于父项,字段可能会出现多次。
方法 1:自动模式。使用 spark.read.format("com.mongodb.spark.sql.DefaultSource") 会产生一个父字段,该字段混合了具有大量空值的所有字段。
方法 2: JSON 函数。Databricks 有一篇关于转换复杂数据类型的好文章。它读起来像 struct("*") 或 json_tuple 或其他函数可以在这里使用,但我找不到任何成功的组合。
方法 3:动态模式。使用此模式有些成功,但不处理嵌套字段,并且还强制所有字段值都为字符串。
schema = (StructType()
.add("id", StringType())
.add("parent", StringType())
)
df = get_my_mongdb_collection_with_schema_function(..., schema)
parent_schema = ArrayType(
MapType(StringType(), StringType())
)
df = df.withColumn('parent', from_json(df['parent'], parent_schema))
解决方案
get_json_object 函数通常可以实现这里所需要的。如果支持所有 JSONPath 运算符,那将是理想的。但是,似乎只支持以下运算符(但很难确认)。
$ Root object
. Child operator
[] Subscript operator for array
* Wildcard for []
读取数据时,指定模式强制包含json的列输入字符串。
schema = (StructType()
.add("id", StringType())
.add("parent", StringType())
)
我能够使用 withColumn 将列添加到数据框中,或者只是将其用作选择的一部分。例如
df = df.withColumn('field2', get_json_object(df['parent'], '$[2].field2'))
.select(get_json_object(df['parent'], '$[2].field2').alias('field2'))
显然,可以在此处添加正确类型的转换。
因为我的源 JSON 是一个数组,所以我将每个对象作为数组元素访问。所以 field2 在第三个数组元素中,即 index = 2。这种方法感觉很脆弱,因为数据的顺序现在很重要。但是,也可以指定通配符数组元素以在所有数组元素中进行选择,例如 $[*].field2。此外,子运算符可用于获取嵌套数据,例如 $[2].field3.field3a
目前尚不清楚如何最好地处理重复的字段名称,但以下 JSONPath 将返回一个值数组:
$[*].field1
Return value:
["1", "11"]
请注意,我没有考虑/测试使用 get_json_object 对性能的影响。
推荐阅读
- java - 数据未显示在 RecyclerView 中。onCreateViewHolder 和 onBindViewHolder 没有被执行
- google-calendar-api - 未登录时,嵌入式 Google 日历不起作用
- javascript - 具有可拖动节点的 Vue 树视图
- intellij-idea - Intellij 认为 TypeScript 函数名称是属性
- ruby-on-rails - 显示网站登录保护部分的相关标题(奇怪的设计错误)
- flexbox - 使用 flex-box 显示许多 ipywidget
- jenkins - 验证字符串参数的长度和类型
- r - 为什么 R 中的这个函数在小数点前放置一个反斜杠?
- laravel - Laravel 路由不再接受数组格式的 id 参数
- python - sklearn cross_val_score() 返回 NaN 值