关联操作

  在许多情况下,有必要将外部数据加入到图中。例如,我们可能有额外的用户属性需要合并到已有的图中或者我们可能想从一个图中取出顶点特征加入到另外一个图中。这些任务可以用join操作完成。
主要的join操作如下所示。

  1. class Graph[VD, ED] {
  2. def joinVertices[U](table: RDD[(VertexId, U)])(map: (VertexId, VD, U) => VD)
  3. : Graph[VD, ED]
  4. def outerJoinVertices[U, VD2](table: RDD[(VertexId, U)])(map: (VertexId, VD, Option[U]) => VD2)
  5. : Graph[VD2, ED]
  6. }

  joinVertices操作join输入RDD和顶点,返回一个新的带有顶点特征的图。这些特征是通过在连接顶点的结果上使用用户定义的map函数获得的。没有匹配的顶点保留其原始值。
下面详细地来分析这两个函数。

1 joinVertices

  1. def joinVertices[U: ClassTag](table: RDD[(VertexId, U)])(mapFunc: (VertexId, VD, U) => VD)
  2. : Graph[VD, ED] = {
  3. val uf = (id: VertexId, data: VD, o: Option[U]) => {
  4. o match {
  5. case Some(u) => mapFunc(id, data, u)
  6. case None => data
  7. }
  8. }
  9. graph.outerJoinVertices(table)(uf)
  10. }

  我们可以看到,joinVertices的实现是通过outerJoinVertices来实现的。这是因为join本来就是outer join的一种特例。

2 outerJoinVertices

  1. override def outerJoinVertices[U: ClassTag, VD2: ClassTag]
  2. (other: RDD[(VertexId, U)])
  3. (updateF: (VertexId, VD, Option[U]) => VD2)
  4. (implicit eq: VD =:= VD2 = null): Graph[VD2, ED] = {
  5. if (eq != null) {
  6. vertices.cache()
  7. // updateF preserves type, so we can use incremental replication
  8. val newVerts = vertices.leftJoin(other)(updateF).cache()
  9. val changedVerts = vertices.asInstanceOf[VertexRDD[VD2]].diff(newVerts)
  10. val newReplicatedVertexView = replicatedVertexView.asInstanceOf[ReplicatedVertexView[VD2, ED]]
  11. .updateVertices(changedVerts)
  12. new GraphImpl(newVerts, newReplicatedVertexView)
  13. } else {
  14. // updateF does not preserve type, so we must re-replicate all vertices
  15. val newVerts = vertices.leftJoin(other)(updateF)
  16. GraphImpl(newVerts, replicatedVertexView.edges)
  17. }
  18. }

  通过以上的代码我们可以看到,如果updateF不改变类型,我们只需要创建改变的顶点即可,否则我们要重新创建所有的顶点。我们讨论不改变类型的情况。
这种情况分三步。

  • 1 修改顶点属性值
  1. val newVerts = vertices.leftJoin(other)(updateF).cache()

  这一步会用顶点RDD join 传入的RDD,然后用updateF作用joinRDD中的所有顶点,改变它们的值。

  • 2 找到发生改变的顶点
  1. val changedVerts = vertices.asInstanceOf[VertexRDD[VD2]].diff(newVerts)
  • 3 更新newReplicatedVertexView中边分区中的顶点属性
  1. val newReplicatedVertexView = replicatedVertexView.asInstanceOf[ReplicatedVertexView[VD2, ED]]
  2. .updateVertices(changedVerts)

  第2、3两步的源码已经在转换操作中详细介绍。