Scala 展开RDD并为新元素分配连续ID

Scala 展开RDD并为新元素分配连续ID,scala,apache-spark,Scala,Apache Spark,我想消除Foo的歧义。其中一些需要拆分为单独的实例。每个都需要一个唯一的、连续的Id val maxId: Long = foos.map(_.id).max() foos.flatMap { foo => if (foo.bar) List(foo, foo.copy(id = ???, ...)) else List(foo) } 在普通Scala中,我会使用折叠式。有了Spark,我能想到的最好方法就是将平面映射到(Foo,Option[Long]),按过滤。2.isEm

我想消除
Foo
的歧义。其中一些需要拆分为单独的实例。每个都需要一个唯一的、连续的Id

val maxId: Long = foos.map(_.id).max()

foos.flatMap { foo =>
  if (foo.bar) List(foo, foo.copy(id = ???, ...))
  else List(foo)
}
在普通Scala中,我会使用折叠式。有了Spark,我能想到的最好方法就是将平面映射到
(Foo,Option[Long])
,按
过滤。2.isEmpty
,zipWithIndex并加入。有更聪明的方法吗

e、 g.给定

case class Foo(id: Long) {
  val bar: Boolean = id % 2 == 1
}
这个输入

RDD( Foo(1), Foo(2), Foo(3) )
应该成为

RDD( Foo(1), Foo(2), Foo(3), Foo(4), Foo(5) )

因为
Foo(1)
Foo(3)
扩展并采用了下一个可用的ID(4和5)。

在任何分布式系统中,可以独立生成的标识优于顺序生成器

所以最好的方法是
.copy(id=randomLong)
,最好的方法是
.copy(id=UUID.randomUUID())

但问题是关于连续的ID的。我对那个案子的建议是

import Numeric.Implicits._
import scala.reflect.ClassTag

abstract class UpdateIDS[T: ClassTag, Id: Numeric : ClassTag] extends Serializable {
  def getId(elem: T): Id
  def setId(elem: T, id: Id): T
  def shouldChange(elem: T): Boolean
  val Id = implicitly[Numeric[Id]]

  def apply(xs: RDD[T]): RDD[T] = {
    val next = xs.map(getId).max + Id.one
    val counts: Seq[(Int, Int)] = xs.mapPartitionsWithIndex { (idx, elems) =>
      Iterator.single(idx, elems.count(shouldChange))
    }.collect.view
    val starts = counts.map(_._2).map(Id.fromInt).scanLeft(next)(_ + _)
    val startMapLocal = counts.zip(starts).map { case ((idx, _), start) => (idx, start) }.toMap
    val startMap = xs.context.broadcast(startMapLocal)

    xs.mapPartitionsWithIndex { case (idx, elems) =>
      elems.scanLeft((List.empty[T], startMap.value(idx))) { (pair, elem) =>
        pair match {
          case (_, counter) if shouldChange(elem) => (List(elem, setId(elem, counter)), counter + Id.one)
          case (_, counter) => (List(elem), counter)
        }
      }.flatMap { _._1 }
    }
  }
}
有了它,你可以很容易地定义

object fooUpdateId extends UpdateIDS[Foo, Int] {
  def getId(foo: Foo) = foo.id
  def setId(foo: Foo, id: Int) = foo.copy(id = id)
  def shouldChange(foo: Foo) = foo.id % 2 == 1
}
然后跑

val foosUpdated = fooUpdateId(foos)
重要注意事项此处更改生成集合的顺序以获得更高性能的解决方案。如果您需要订购不太大的RDD,您可以使用
sortBy


还请注意,使用scalaz的
mapAccumL
Lens
实现
UpdadeIDs
可能会更简单,但我选择避免使用外部库。

也许其他人可以理解您的问题,但我无法解决。通常,您在常规Scala集合上使用的所有典型操作(特别是monad操作)都可以在RDD上使用。我在问题中举了一个简洁的例子。@Synesso集合元素的顺序重要吗?@Odomontois不重要我假设zipWithIndex是分区的局部索引,对吗?所以这里的要点是计算每个分区要更改的数量并分配开始索引,将其广播到集群,然后每个分区使用自己的开始索引分配唯一的、连续的索引。我要试一试。@Synesso完全正确,如果你说的是
zipWithIndex
你的意思是
mapPartitionsWithIndex