package com.thinkaurelius.faunus.formats; import com.thinkaurelius.faunus.FaunusEdge; import com.thinkaurelius.faunus.FaunusVertex; import com.thinkaurelius.faunus.Holder; import com.thinkaurelius.faunus.mapreduce.util.EmptyConfiguration; import com.tinkerpop.blueprints.Direction; import com.tinkerpop.blueprints.Edge; import com.tinkerpop.blueprints.util.ExceptionFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.mapreduce.Reducer; import java.io.IOException; import java.util.List; /** * @author Marko A. Rodriguez (http://markorodriguez.com) */ public class EdgeCopyMapReduce { public static final String FAUNUS_GRAPH_INPUT_EDGE_COPY_DIRECTION = "faunus.graph.input.edge-copy.direction"; public enum Counters { EDGES_COPIED, EDGES_ADDED } public static Configuration createConfiguration(final Direction direction) { final Configuration configuration = new EmptyConfiguration(); configuration.setEnum(FAUNUS_GRAPH_INPUT_EDGE_COPY_DIRECTION, direction); return configuration; } public static class Map extends Mapper<NullWritable, FaunusVertex, LongWritable, Holder<FaunusVertex>> { private final Holder<FaunusVertex> vertexHolder = new Holder<FaunusVertex>(); private final FaunusVertex shellVertex = new FaunusVertex(); private final LongWritable longWritable = new LongWritable(); private Direction direction = Direction.OUT; @Override public void setup(final Mapper.Context context) throws IOException, InterruptedException { this.direction = context.getConfiguration().getEnum(FAUNUS_GRAPH_INPUT_EDGE_COPY_DIRECTION, Direction.OUT); if (this.direction.equals(Direction.BOTH)) throw new InterruptedException(ExceptionFactory.bothIsNotSupported().getMessage()); } @Override public void map(final NullWritable key, final FaunusVertex value, final Mapper<NullWritable, FaunusVertex, LongWritable, Holder<FaunusVertex>>.Context context) throws IOException, InterruptedException { long edgesInverted = 0; for (final Edge edge : value.getEdges(this.direction)) { final long id = (Long) edge.getVertex(this.direction.opposite()).getId(); this.longWritable.set(id); this.shellVertex.reuse(id); this.shellVertex.addEdge(this.direction.opposite(), (FaunusEdge) edge); context.write(this.longWritable, this.vertexHolder.set('s', this.shellVertex)); edgesInverted++; } this.longWritable.set(value.getIdAsLong()); context.write(this.longWritable, this.vertexHolder.set('r', value)); context.getCounter(Counters.EDGES_COPIED).increment(edgesInverted); } } public static class Reduce extends Reducer<LongWritable, Holder<FaunusVertex>, NullWritable, FaunusVertex> { private Direction direction = Direction.OUT; private final FaunusVertex vertex = new FaunusVertex(); @Override public void setup(final Reduce.Context context) throws IOException, InterruptedException { this.direction = context.getConfiguration().getEnum(FAUNUS_GRAPH_INPUT_EDGE_COPY_DIRECTION, Direction.OUT); if (this.direction.equals(Direction.BOTH)) throw new InterruptedException(ExceptionFactory.bothIsNotSupported().getMessage()); } @Override public void reduce(final LongWritable key, final Iterable<Holder<FaunusVertex>> values, final Reducer<LongWritable, Holder<FaunusVertex>, NullWritable, FaunusVertex>.Context context) throws IOException, InterruptedException { long edgesAggregated = 0; this.vertex.reuse(key.get()); for (final Holder<FaunusVertex> holder : values) { if (holder.getTag() == 's') { edgesAggregated = edgesAggregated + ((List) holder.get().getEdges(direction.opposite())).size(); this.vertex.addEdges(direction.opposite(), holder.get()); } else { this.vertex.addAll(holder.get()); } } context.write(NullWritable.get(), this.vertex); context.getCounter(Counters.EDGES_ADDED).increment(edgesAggregated); } } }