Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用Spark数据框架演化模式_Java_Apache Spark_Dataframe_Apache Spark Sql - Fatal编程技术网

Java 使用Spark数据框架演化模式

Java 使用Spark数据框架演化模式,java,apache-spark,dataframe,apache-spark-sql,Java,Apache Spark,Dataframe,Apache Spark Sql,我正在使用Spark dataframe,它可以从三个不同的模式版本之一加载数据: // Original { "A": {"B": 1 } } // Addition "C" { "A": {"B": 1 }, "C": 2 } // Additional "A.D" { "A": {"B": 1, "D": 3 }, "C": 2 } 我可以通过检查模式是否包含字段“C”来处理额外的“C”,如果没有,则向数据帧添加新列。但是,我无法确定如何为子对象创建字段 public void evol

我正在使用Spark dataframe,它可以从三个不同的模式版本之一加载数据:

// Original
{ "A": {"B": 1 } }
// Addition "C"
{ "A": {"B": 1 }, "C": 2 }
// Additional "A.D"
{ "A": {"B": 1, "D": 3 }, "C": 2 }
我可以通过检查模式是否包含字段“C”来处理额外的“C”,如果没有,则向数据帧添加新列。但是,我无法确定如何为子对象创建字段

public void evolvingSchema() {
    String versionOne = "{ \"A\": {\"B\": 1 } }";
    String versionTwo = "{ \"A\": {\"B\": 1 }, \"C\": 2 }";
    String versionThree = "{ \"A\": {\"B\": 1, \"D\": 3 }, \"C\": 2 }";

    process(spark.getContext(), "1", versionOne);
    process(spark.getContext(), "2", versionTwo);
    process(spark.getContext(), "2", versionThree);
}

private static void process(JavaSparkContext sc, String version, String data) {
    SQLContext sqlContext = new SQLContext(sc);
    DataFrame df = sqlContext.read().json(sc.parallelize(Arrays.asList(data)));
    if(!Arrays.asList(df.schema().fieldNames()).contains("C")) {
        df = df.withColumn("C", org.apache.spark.sql.functions.lit(null));
    }
    // Not sure what to put here. The fieldNames does not contain the "A.D"

    try {
        df.select("C").collect();
    } catch(Exception e) {
        System.out.println("Failed to C for " + version);
    }
    try {
        df.select("A.D").collect();
    } catch(Exception e) {
        System.out.println("Failed to A.D for " + version);
    }
}

JSON源不太适合具有演进模式的数据(改为Avro或Parquet如何),但简单的解决方案是对所有源使用相同的模式,并使新字段可选/可空:

import org.apache.spark.sql.types.{StructType, StructField, LongType}

val schema = StructType(Seq(
  StructField("A", StructType(Seq(
    StructField("B", LongType, true), 
    StructField("D", LongType, true)
  )), true),
  StructField("C", LongType, true)))
您可以像这样将
schema
传递给
DataFrameReader

val rddV1 = sc.parallelize(Seq("{ \"A\": {\"B\": 1 } }"))
val df1 = sqlContext.read.schema(schema).json(rddV1)

val rddV2 = sc.parallelize(Seq("{ \"A\": {\"B\": 1 }, \"C\": 2 }"))
val df2 = sqlContext.read.schema(schema).json(rddV2)

val rddV3 = sc.parallelize(Seq("{ \"A\": {\"B\": 1, \"D\": 3 }, \"C\": 2 }"))
val df3 = sqlContext.read.schema(schema).json(rddV3)
您将获得独立于变体的一致结构:

require(df1.schema == df2.schema && df2.schema == df3.schema)
自动将缺少的列设置为
null

df1.printSchema
// root
//  |-- A: struct (nullable = true)
//  |    |-- B: long (nullable = true)
//  |    |-- D: long (nullable = true)
//  |-- C: long (nullable = true)

df1.show
// +--------+----+
// |       A|   C|
// +--------+----+
// |[1,null]|null|
// +--------+----+

df2.show
// +--------+---+
// |       A|  C|
// +--------+---+
// |[1,null]|  2|
// +--------+---+

df3.show
// +-----+---+
// |    A|  C|
// +-----+---+
// |[1,3]|  2|
// +-----+---+
注意

此解决方案依赖于数据源。它可以与其他源一起工作,也可以不与其他源一起工作,或者。

已经回答了这个问题,但是是在Scala中。这是同样的事情,但在Java中

public void evolvingSchema() {
    String versionOne = "{ \"A\": {\"B\": 1 } }";
    String versionTwo = "{ \"A\": {\"B\": 1 }, \"C\": 2 }";
    String versionThree = "{ \"A\": {\"B\": 1, \"D\": 3 }, \"C\": 2 }";

    process(spark.getContext(), "1", versionOne);
    process(spark.getContext(), "2", versionTwo);
    process(spark.getContext(), "2", versionThree);
}

private static void process(JavaSparkContext sc, String version, String data) {
    StructType schema = DataTypes.createStructType(Arrays.asList(
            DataTypes.createStructField("A",
                    DataTypes.createStructType(Arrays.asList(
                            DataTypes.createStructField("B", DataTypes.LongType, true),
                    DataTypes.createStructField("D", DataTypes.LongType, true))), true),
            DataTypes.createStructField("C", DataTypes.LongType, true)));

    SQLContext sqlContext = new SQLContext(sc);
    DataFrame df = sqlContext.read().schema(schema).json(sc.parallelize(Arrays.asList(data)));

    try {
        df.select("C").collect();
    } catch(Exception e) {
        System.out.println("Failed to C for " + version);
    }
    try {
        df.select("A.D").collect();
    } catch(Exception e) {
        System.out.println("Failed to A.D for " + version);
    }
}

为什么你说JSON不适合演化模式?@mlk我们演化模式和JSON的主要问题是,各种JSON客户端可能会做一些意想不到的事情,比如将一个空部分呈现为一个空字符串数组(即“”)。这真的会搞砸你的模式管理。。。我想zero323与Ewan的注释有类似的关注点。JSON既不是自描述的,也不支持模式。当然,您可以为JSON文档使用或创建自定义超媒体格式,但其中没有语义的一部分。当我们处理JSONL时,它变得特别烦人。如果没有一个完整的文件,就不可能推断出模式。此外,如果数据格式不正确,这是我们在运行时遇到的问题。正如我们所说的,如果您读取两个不同分区的拼花文件夹,spark 2.1.0将不起作用。即使您在阅读前指定了模式以供参考,列顺序也会混乱,我处理不断发展的模式的方式是:我公开并使用private Structype.mergeSchema手动合并来自不同来源的模式(从ParquetFileFormat.mergeschemasinparllel读取,并将其与文件的子集合并),然后我阅读给出显式模式的数据帧,最后使用select(col:*)技巧对列重新排序。只有到那时,我才能表演联盟。谢谢你。JavaAPI与更常见的scala OneHanks有很大的不同,对Java感兴趣的开发人员非常少。