Исходное значение Искры 2

97
9

Получение этой нулевой ошибки в искровом окне Dataset.filter


Входной CSV:


name,age,stat
abc,22,m
xyz,,s

Рабочий код:


case class Person(name: String, age: Long, stat: String)

val peopleDS = spark.read.option("inferSchema","true")
.option("header", "true").option("delimiter", ",")
.csv("./people.csv").as[Person]
peopleDS.show()
peopleDS.createOrReplaceTempView("people")
spark.sql("select * from people where age > 30").show()


Ошибка кода (добавление следующей строки возвращает ошибку):


val filteredDS = peopleDS.filter(_.age > 30)
filteredDS.show()

Возвращает нулевую ошибку


java.lang.RuntimeException: Null value appeared in non-nullable field:
- field (class: "scala.Long", name: "age")
- root class: "com.gcp.model.Person"
If the schema is inferred from a Scala tuple/case class, or a Java bean, please try to use scala.Option[_] or other nullable types (e.g. java.lang.Integer instead of int/scala.Int).

спросил(а) 2021-01-19T17:48:46+03:00 2 месяца, 3 недели назад
1
Решение
143

Исключение, которое вы получите, должно объяснить все, но отпустите шаг за шагом:


    При загрузке данных с использованием источника данных csv все поля помечены как nullable:


    val path: String = ???

    val peopleDF = spark.read
    .option("inferSchema","true")
    .option("header", "true")
    .option("delimiter", ",")
    .csv(path)

    peopleDF.printSchema


    root
    |-- name: string (nullable = true)
    |-- age: integer (nullable = true)
    |-- stat: string (nullable = true)

    Отсутствующее поле представляется как SQL NULL


    peopleDF.where($"age".isNull).show

    +----+----+----+
    |name| age|stat|
    +----+----+----+
    | xyz|null| s|
    +----+----+----+

    Затем вы конвертируете Dataset[Row] в Dataset[Person], который использует Long для кодирования поля age. Long в Scala не может быть NULL. Поскольку схема ввода nullable, схема вывода остается nullable, несмотря на это:


    val peopleDS = peopleDF.as[Person]

    peopleDS.printSchema


    root
    |-- name: string (nullable = true)
    |-- age: integer (nullable = true)
    |-- stat: string (nullable = true)

    Обратите внимание, что он as[T] вообще не влияет на схему.


    При запросе Dataset с использованием SQL (в зарегистрированной таблице) или DataFrame API Spark не будет десериализовать объект. Поскольку схема все еще nullable, мы можем выполнить:


    peopleDS.where($"age" > 30).show

    +----+---+----+
    |name|age|stat|
    +----+---+----+
    +----+---+----+

    без каких-либо проблем. Это всего лишь простая логика SQL, а NULL - допустимое значение.


    Когда мы используем статически типизированный Dataset API:


    peopleDS.filter(_.age > 30)

    Искра должна десериализовать объект. Поскольку Long не может быть NULL (SQL NULL), он не работает с исключением, которое вы видели.


    Если бы не это, вы бы получили NPE.


    Правильное статически типизированное представление ваших данных должно использовать Optional types:


    case class Person(name: String, age: Option[Long], stat: String)

    с установленной функцией фильтра:


    peopleDS.filter(_.age.map(_ > 30).getOrElse(false))

    +----+---+----+
    |name|age|stat|
    +----+---+----+
    +----+---+----+

    Если вы предпочитаете использовать сопоставление с образцом:


    peopleDS.filter {
    case Some(age) => age > 30
    case _ => false // or case None => false
    }

    Обратите внимание, что вам необязательно (но было бы рекомендовано в любом случае) использовать необязательные типы для name и stat. Поскольку Scala String - это просто Java String, он может NULL. Конечно, если вы пойдете с таким подходом, вам нужно явно проверить, имеют ли доступные значения NULL или нет.


Связанный Spark 2.0 Dataset vs DataFrame

ответил(а) 2021-01-19T17:48:46+03:00 2 месяца, 3 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема