Spark Select для JSON-массива различных объектов - PullRequest
0 голосов
/ 24 января 2019

В Databricks / Spark / Python (Spark версии 2.4.0 с использованием pyspark) я получаю коллекцию из 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))

1 Ответ

0 голосов
/ 25 января 2019

Функция 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.

...