如何遍历spark数据集并更新Java中的列值?

时间:2018-03-20 22:21:40

标签: java apache-spark

我正在使用POC,我必须使用令牌更新DB中的帐号。我将数据读入数据集dsRecords(约2M记录)。我有另一个例程,它捕获了不同的帐号并获得了令牌,映射存储在HashMap中。

Dataset<Row> applySwappedTokens(Dataset<Row> dsRecords, Map<String, String> mappedTokens){
}

现在,我必须遍历数据集才能执行以下操作 - 1.读取帐号列(accountNumber)值并更新(我知道数据集是不可变的。因此,更新数据集意味着创建具有更新行的数据集的副本),并使用来自mappedTokens的标记值。这可以通过JOIN或其他操作来实现,但由于第二项任务,我没有在此花费精力。 2.阅读另一个XML blob列并找到帐号并进行更新。

到目前为止,我尝试的所有选项都会因非序列化代码而导致编译时错误或测试编译错误。大多数在线资源都是Scala而不是Java。请帮忙。

Spark 2.1 Java 8

Approach1 - 由于序列化错误而无法测试。

Dataset<Row> output = sparkSession.sqlContext().createDataFrame(dsRecords.javaRDD().map(row ->  {
            return RowFactory.create(row.get(0), row.get(1), row.get(2), swapToken(row.get(3)),row.get(4));
        }), dsRecords.schema());

        return output;

String swapToken(Object inputToken) {
        return mappedTokens.get(inputToken);//mappedToken will have to be instance field.
    }

方法2-未完成。

dsRecords.foreach((ForeachFunction<Row>) row -> {
            Integer index = row.fieldIndex("accountNumber");
            String pan = row.getString(index);
            String swap = this.swapToken(pan);
            //TODO: create a dataset with rows from dsRecords but swap value.

        });

方法3 - 将UDF与地图功能一起使用

创建UDF2(需要2个输入参数,即accountNumber和mappedToken并返回令牌)。似乎UDF只能采用列值

更新1 - UDF 所以,我实现了UDF(AFK,稍后会发布代码): 1.定义UDF1'updateToken'以传递xml列值并返回更新的xml值。 2.具有帐户 - 令牌对映射的HashMap实例'mappedTokens'是静态的。在我的UDF函数中访问它以在xml字符串中查找帐户并使用令牌进行更新。

我可以测试我的applySwappedTokens函数,它在数据集'withColumn'上调用上面的UDF。但是当我运行spark程序时,我看到'mappedToken'有'null'数据,因此,xml列会被空数据更新。我认为静态'mappedTokens'要么在另一个JVM或驱动程序中(即使在本地,spark也会创建隔离的驱动程序,执行程序)。令人沮丧的是,没有简单的解决方案来迭代和更新spark中的行。

Dataset<Row> processByRow(Dataset<Row> dsRecords, SparkSession sparkSession) {
        sparkSession.udf().register("updateToken", updateToken, DataTypes.StringType);          
        return ds = dsRecords.withColumn("eventRecordTokenText", callUDF("updateToken", dsRecords.col("eventRecordTokenText")));
            }

static UDF1 updateToken = new UDF1<String, String>() {
        public String call(final String tokenText) throws Exception {
                // xml operations here..
                for (int nodeIndex = 0; nodeIndex < nList.getLength(); nodeIndex++) {
                    Node thisNode = nList.item(nodeIndex);
                    if (thisNode.getAttributes().getNamedItem("ProcessTokenValue") != null && thisNode.getAttributes()
                            .getNamedItem("ProcessTokenValue").getNodeValue().equalsIgnoreCase("true")) {
                        Node valueNode = thisNode.getAttributes().getNamedItem("Value");
                        String thisToken = valueNode.getNodeValue();
                        String newToken = mappedTokens.get(thisToken); // *returns null values from the map*
                        if(newToken != null && !newToken.isEmpty())
                        valueNode.setNodeValue(newToken);
                    }
                }
                // more xml operations here..
                return output;
        }
    };

更新2 - 迭代&amp;更新 现在,我正在逐行遍历..

Dataset<Row> processByRow1(Dataset<Row> dsRecords, SparkSession sparkSession) {
        List<MongoRecordSmall> newRows = new ArrayList<MongoRecordSmall>();
            dsRecords.foreach((ForeachFunction<Row>) record -> {
            String currentToken = record.getAs(AppConstants.TokenCol);
            String newToken = mappedTokens.get(currentToken);
            newRows.add(new MongoRecordSmall(record.getString(0), record.getString(1), newToken, record.getString(3)));
            logger.error(“Size plus=“+newRows.size());
        });
       return sparkSession.createDataFrame(newRows, MongoRecordSmall.class);
    }

这会引发serliazation错误。似乎(https://databricks.gitbooks.io/databricks-spark-knowledge-base/content/troubleshooting/javaionotserializableexception.html)我的类存在上述逻辑,正在进行serlalized并发送到工作节点并且没有这样做。

1 个答案:

答案 0 :(得分:0)

由于我没有找到更好的答案,我会用我实施的解决方案回答我的问题(看起来很难看!) -

IVehicle