Spark 使用纯 SQL 查询提取嵌套 JSON 数组项

y2k*_*ham 5 apache-spark-sql

注意:这不是以下内容的重复(或其他几个类似的讨论)


我有一个Hive表,我必须纯粹通过Spark-SQL-query读取和处理。该表有一个string-type 列,其中包含JSON来自 API 的转储;因此,正如预期的那样,它具有深度嵌套的字符串化 JSON

让我们看这个例子(它描述了我正在尝试处理的数据的确切深度/复杂性)

{
    "key1": ..
    "key2": ..
    ..
    "bill_summary": {
        "key1": ..
        "key2": ..
        ..
        "items": [
            {
                "item": {
                    "key1": ..
                    "key2": ..
                    ..
                    "type": "item_type_1"
                    ..
                    "total_cost": 57.65
                    ..
                }
            },
            {
                "item": {
                    "key1": ..
                    "key2": ..
                    ..
                    "total_cost": 23.31
                    ..
                }
            }
            ..
            {
                "item": {
                    "key1": ..
                    "key2": ..
                    ..
                    "type": "item_type_1"
                    ..
                }
            }
        ]
        ..
    }
    ..
}
Run Code Online (Sandbox Code Playgroud)

我对items数组感兴趣。我可以通过以下方式访问它

get_json_object(get_json_object('$.bill_summary'), '$.items') AS items
Run Code Online (Sandbox Code Playgroud)

现在问题来了

  • 我需要从数组中取出所有 ( type, ) 元组total_cost
  • 但我只需要那些都存在的条目,而几个item对象具有其中一个或没有一个

  • 虽然我还设法将所有type字段和total_cost字段分别选取到两个单独的数组中,但由于上面的第二个限制(缺少字段),我最终失去了关系
  • 我最终得到的(使用下面的代码片段)是两个数组(可能具有不同的长度),但不确定每个数组的相应元素是否属于同一个项目

这段代码仅列出了我相当长的 SQL 查询的一部分。它采用CTE

..
split(get_json_object(get_json_object(var6, '$.bill_summary'), '$.items[*].item.type'), ',') AS types_array,
split(get_json_object(get_json_object(var6, '$.bill_summary'), '$.items[*].item.total_cost'), ',') AS total_cost_array
..
Run Code Online (Sandbox Code Playgroud)

现在这是限制

  • 我无法控制源Hive表架构或其数据
  • 我想使用纯粹的SQL 查询来做到这一点Spark
  • 无法使用DataFrame操纵
  • 我不想雇用注册udf人员(我将其作为最后的手段)

我在文档和论坛上花了几个小时,但Spark-SQL 文档很少,而且讨论主要围绕DataFrameAPI,而我无法使用它。这个问题仅仅通过 SQL 查询就可以解决吗?

y2k*_*ham 7

经过几个小时的网络搜索后,这个答案暗示我可以将字符串化的JSON 数组转换为 Spark-sql 中的结构数组。最后这就是我所做的

     ..
     var6_items AS
  (SELECT hash_id,
          entity1,
          dt,
          get_json_object(get_json_object(var6,'$.bill_summary'), '$.items[*].item') AS items_as_string
   FROM rows_with_appversion
   WHERE appversion >= 14),

     filtered_var6_items AS
  (SELECT *
   FROM var6_items
   WHERE items_as_string IS NOT NULL
     AND items_as_string != '')

    SELECT from_json(items_as_string, 'array<struct<type:string,total_cost:string>>') AS items_as_struct_array
    FROM filtered_var6_items
     ..
Run Code Online (Sandbox Code Playgroud)

解释

  • 表达式get_json_object(get_json_object(var6,'$.bill_summary'), '$.items[*].item') AS items_as_string结果items_as_string包含以下(字符串化)JSON(请注意,每个周围的一层冗余嵌套item也已被删除)
[
  {
    "key1": "val1",
    "key2": "val2",
    "type": "item_type_1",
    "total_cost": 57.65
  },
  {
    "key1": "val1",
    "key2": "val2",
    "total_cost": 57.65
  }
  ..
  {
    "key1": "val1",
    "key2": "val2",
    "type": "item_type_1"
  }
]
Run Code Online (Sandbox Code Playgroud)
  • 此后,from_json函数允许将上述结构转换为arrayof structs。一旦获得,我就可以filter构造同时具有typetotal_cost不具有的结构NULL

参考