如何计算Spark JavaRDD中当前行与上一行之间的差异

时间:2016-03-28 14:23:11

标签: java apache-spark rdd

我将.log文件解析为JavaRDD,在对此JavaRDD进行排序之后,现在我已经将oldJavaRDD文件解析为了2016-03-28 | 11:00 | X | object1 | region12016-03-28 | 11:01 | Y | object1 | region1
2016-03-28 | 11:05 | X | object1 | region1
2016-03-28 | 11:09 | X | object1 | region1
2016-03-28 | 11:00 | X | object2 | region1
2016-03-28 | 11:01 | Z | object2 | region1
newJavaRDD

如何将2016-03-28 | 9 | object1 | region1保存到DB? 新的JavaRDD结构必须是:
2016-03-28 | 1 | object2 | region1
X, Y, Z
所以,我必须计算当前行和上一行之间的时间(在某些情况下还要使用标志date, objectName来定义,是否为结果添加时间)并在更改objectRegion或{{{{}}之后向JavaRDD添加新元素1}}。

我可以使用这种类型的代码(地图)来做到这一点,但我认为它不好而且不是最快的方式

    JavaRDD<NewObject> newJavaRDD = oldJavaRDD.map { r -> 
      String datePrev[] = ...
        if (datePrev != dateCurr ...) {
          return newJavaRdd;
        } else {
          return null;
        }
    }

1 个答案:

答案 0 :(得分:0)

首先,您的代码示例在创建 newJavaRDD的转换中引用newJavaRDD - 这在几个不同的级别上是不可能的:

  • 您无法在该变量声明的右侧引用变量...
  • 您不能在RDD的转换中使用RDD(相同的一个或另一个 - 这无关紧要) - 转换中的任何内容都必须由Spark序列化,并且Spark无法序列化自己的RDD(这没什么意义)

那么,你应该怎么做?

<强>假设

  1. 您的目的是为date + object + region
  2. 的每个组合获取一条记录
  3. 每个此类组合的记录不应太多,因此将groupBy这些字段作为键是安全的
  4. 您可以groupBy关键字段,然后mapValues获取第一个和最后一个记录之间的“分钟距离”(传递给mapValues的函数可以更改为包含您的确切内容逻辑,如果我没有做对了)。我将使用 Joda Time 库进行时间计算:

    public static void main(String[] args) {
        // some setup code for this test:
        JavaSparkContext sc = new JavaSparkContext("local", "test");
    
        // input:
        final JavaRDD<String[]> input = sc.parallelize(Lists.newArrayList(
                //              date        time     ?    object     region
                new String[]{"2016-03-28", "11:00", "X", "object1", "region1"},
                new String[]{"2016-03-28", "11:01", "Y", "object1", "region1"},
                new String[]{"2016-03-28", "11:05", "X", "object1", "region1"},
                new String[]{"2016-03-28", "11:09", "X", "object1", "region1"},
                new String[]{"2016-03-28", "11:00", "X", "object2", "region1"},
                new String[]{"2016-03-28", "11:01", "Z", "object2", "region1"}
        ));
    
        // grouping by key:
        final JavaPairRDD<String, Iterable<String[]>> byObjectAndDate = input.groupBy(new Function<String[], String>() {
            @Override
            public String call(String[] record) throws Exception {
                return record[0] + record[3] + record[4]; // date, object, region
            }
        });
    
        // mapping each "value" (all record matching key) to result
        final JavaRDD<String[]> result = byObjectAndDate.mapValues(new Function<Iterable<String[]>, String[]>() {
            @Override
            public String[] call(Iterable<String[]> records) throws Exception {
                final Iterator<String[]> iterator = records.iterator();
                String[] previousRecord = iterator.next();
                int diffMinutes = 0;
    
                for (String[] record : records) {
                    if (record[2].equals("X")) {  // if I got your intention right...
                        final LocalDateTime prev = getLocalDateTime(previousRecord);
                        final LocalDateTime curr = getLocalDateTime(record);
                        diffMinutes += Period.fieldDifference(prev, curr).toStandardMinutes().getMinutes();
                    }
                    previousRecord = record;
                }
    
                return new String[]{
                        previousRecord[0],
                        Integer.toString(diffMinutes),
                        previousRecord[3],
                        previousRecord[4]
                };
            }
        }).values();
    
        // do whatever with "result"...
    }
    
    // extracts a Joda LocalDateTime from a "record"
    static LocalDateTime getLocalDateTime(String[] record) {
        return LocalDateTime.parse(record[0] + " " + record[1], formatter);
    }
    
    static final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm");
    

    P.S。在Scala中,这将需要大约8行...:/