private Block createCoordinateBlock(Object coordinate) throws IOException, InterruptedException {
    Pair<Integer, Integer> offsets = coord2offsets.get(coordinate);
    if (offsets == null) return null;

    int start = offsets.getFirst();
    int len = offsets.getSecond() - start;
    ByteBuffer inMemBuffer = ByteBuffer.wrap(inputBlock.getByteBuffer().array(), start, len);

    RubixMemoryBlock miniBlock =
        new RubixMemoryBlock(
            null,
            PhaseContext.getConf(),
            inMemBuffer,
            valueClass,
            (CompressionCodec) null,
            BlockSerializationType.DEFAULT);

    // This is because the schema and pivotBy attributes which are set in
    // jsonForCombine also apply to this.
    miniBlock.configure(jsonForCombine);

    return miniBlock;
  }
  @Override
  public void setInput(Configuration conf, Map<String, Block> input, JsonNode json)
      throws IOException, InterruptedException {
    // #1. input block
    inputBlock = (RubixMemoryBlock) input.get(JsonUtils.getText(json, "inputBlock"));

    // #2. lookup column
    String lookupColumn = json.get("lookupColumn").getTextValue();
    BlockSchema inputSchema = inputBlock.getProperties().getSchema();

    coord2offsets = BlockUtils.generateColumnIndex(inputBlock, lookupColumn);

    // #3. meta data relation name
    metaRelationName = new String(JsonUtils.getText(json, "metaRelationName"));
    matchingMetaBlock = (Block) input.get(metaRelationName);
    BlockSchema metaBlockSchema = matchingMetaBlock.getProperties().getSchema();

    // #4. find indexes for coordinate column names in meta relation's schema
    String[] coordinateColumns = JsonUtils.asArray(json.get("coordinateColumns"));
    coordinateColumnIndexes = new int[coordinateColumns.length];
    int idx = 0;
    for (String s : JsonUtils.asArray(json.get("coordinateColumns")))
      coordinateColumnIndexes[idx++] = metaBlockSchema.getIndex(s);

    // #5. find index of identifier column in meta relation's schema
    identifierColumnName = new String(JsonUtils.getText(json, "identifierColumn"));
    identifierColumnIndex = metaBlockSchema.getIndex(identifierColumnName);

    // #6. combine columns
    ArrayNode combineColumns = (ArrayNode) json.get("combineColumns");

    // setup info for sort operator
    /*
     * jsonForSort = JsonUtils.cloneNode(json); ((ObjectNode)
     * jsonForSort).put("sortBy", combineColumns); sortedBlock = new
     * TupleOperatorBlock(sortOp);
     */

    // setup info for combiner operator
    jsonForCombine = JsonUtils.createObjectNode();
    ((ObjectNode) jsonForCombine).put("pivotBy", combineColumns);
    ((ObjectNode) jsonForCombine).put("schema", inputSchema.toJson());
    combinedBlock = new TupleOperatorBlock(combineOp, null);

    // setup info for generate operator
    jsonForGenerate = JsonUtils.createObjectNode();
  }
  private ArrayNode createJsonForGenerate(Object vectorIdentifier) {
    ArrayNode outputTupleJson = JsonUtils.createArrayNode();

    // + First duplicate existing schema
    for (String s : inputBlock.getProperties().getSchema().getColumnNames()) {
      outputTupleJson.add(RewriteUtils.createProjectionExpressionNode(s, s));
    }

    // + Add the new generated column
    JsonNode constNode;
    if (vectorIdentifier instanceof String)
      constNode = RewriteUtils.createStringConstant((String) vectorIdentifier);
    else constNode = RewriteUtils.createIntegerConstant((Integer) vectorIdentifier);

    String outColName = metaRelationName + "___" + identifierColumnName;
    outputTupleJson.add(
        JsonUtils.createObjectNode("col_name", outColName, "expression", constNode));
    return outputTupleJson;
  }