/**
   * Creates an m/r job from the supplied link spec and executes it
   *
   * @param linkWalkSpec the Link Walk spec
   * @return {@link MapReduceResult} containing the end of the link and any intermediate bkeys for a
   *     second pass
   * @throws IOException
   */
  private MapReduceResult linkWalkFirstPhase(final LinkWalkSpec linkWalkSpec) throws IOException {
    BucketKeyMapReduce mr = new BucketKeyMapReduce(this);
    mr.addInput(linkWalkSpec.getStartBucket(), linkWalkSpec.getStartKey());
    int size = linkWalkSpec.size();
    int cnt = 0;

    for (LinkWalkStep step : linkWalkSpec) {
      cnt++;
      boolean keep = linkAccumulateToLinkPhaseKeep(step.getKeep(), cnt == size);
      mr.addLinkPhase(step.getBucket(), step.getTag(), keep);
    }

    // this is a bit of a hack. The low level API is using the high level
    // API so must strip out the exception.
    try {
      return mr.execute();
    } catch (RiakException e) {
      throw (IOException) e.getCause();
    }
  }