public void reduce(
        CompositeKeyWritable key, Iterable<KeyValueArrayWritable> values, Context context)
        throws IOException, InterruptedException {
      /* BERLIN SPARQL BENHCMARK QUERY 8
         ----------------------------------------
      SELECT ?title ?text ?reviewDate ?reviewer ?reviewerName ?rating1 ?rating2 ?rating3 ?rating4
      WHERE {
      	[TP-01] ?review bsbm:reviewFor %ProductXYZ% .
      	[TP-02] ?review dc:title ?title .
      	[TP-03] ?review rev:text ?text .
      	[TP-04] FILTER langMatches( lang(?text), "EN" )
      	[TP-05] ?review bsbm:reviewDate ?reviewDate .
      	[TP-06] ?review rev:reviewer ?reviewer .
      	[TP-07] ?reviewer foaf:name ?reviewerName .
      	[TP-08] OPTIONAL { ?review bsbm:rating1 ?rating1 . }
      	[TP-09] OPTIONAL { ?review bsbm:rating2 ?rating2 . }
      	[TP-10] OPTIONAL { ?review bsbm:rating3 ?rating3 . }
      	[TP-11] OPTIONAL { ?review bsbm:rating4 ?rating4 . }
      }
      ORDER BY DESC(?reviewDate)
      LIMIT 20
         --------------------------------------- */

      List<KeyValue> finalKeyValues = new ArrayList<KeyValue>();

      // Find the keys for the vendor/publisher
      KeyValue kv_reviewer = null;
      for (KeyValueArrayWritable array : values) {
        for (KeyValue kv : (KeyValue[]) array.toArray()) {
          if (Arrays.equals(kv.getValue(), "rev_reviewer".getBytes())) {
            kv_reviewer = kv;
            finalKeyValues.add(kv);
          } else {
            finalKeyValues.add(kv);
          }
        }
      }
      // TP-07
      Result reviewerResult = table.get(new Get(kv_reviewer.getQualifier()));
      boolean foundReviewerName = false;
      for (KeyValue kv : reviewerResult.list()) {
        if (Arrays.equals(kv.getQualifier(), "foaf_name".getBytes())) {
          finalKeyValues.add(kv);
          foundReviewerName = true;
          break;
        }
      }
      if (foundReviewerName == false) {
        return;
      }

      // Format and output the values
      StringBuilder builder = new StringBuilder();
      builder.append("\n");
      for (KeyValue kv : finalKeyValues) {
        String[] triple = null;
        try {
          triple = SharedServices.keyValueToTripleString(kv);
        } catch (ClassNotFoundException e) {
          e.printStackTrace();
        }
        builder.append(triple[0] + "\t" + triple[1] + "\t" + triple[2] + "\n");
      }

      context.write(new Text(key.getValue()), new Text(builder.toString()));
    }
    public void map(ImmutableBytesWritable row, Result value, Context context)
        throws InterruptedException, IOException {
      /* BERLIN SPARQL BENHCMARK QUERY 8
        ----------------------------------------
      SELECT ?title ?text ?reviewDate ?reviewer ?reviewerName ?rating1 ?rating2 ?rating3 ?rating4
      WHERE {
      	[TriplePattern-01] ?review bsbm:reviewFor %ProductXYZ% .
      	[TP-02] ?review dc:title ?title .
      	[TP-03] ?review rev:text ?text .
      	[TP-04] FILTER langMatches( lang(?text), "EN" )
      	[TP-05] ?review bsbm:reviewDate ?reviewDate .
      	[TP-06] ?review rev:reviewer ?reviewer .
      	[TP-07] ?reviewer foaf:name ?reviewerName .
      	[TP-08] OPTIONAL { ?review bsbm:rating1 ?rating1 . }
      	[TP-09] OPTIONAL { ?review bsbm:rating2 ?rating2 . }
      	[TP-10] OPTIONAL { ?review bsbm:rating3 ?rating3 . }
      	[TP-11] OPTIONAL { ?review bsbm:rating4 ?rating4 . }
      }
      ORDER BY DESC(?reviewDate)
      LIMIT 20
        ---------------------------------------*/
      String rowKey = new String(value.getRow());

      ArrayList<KeyValue> keyValuesToTransmit = new ArrayList<KeyValue>();
      List<KeyValue> reviewRow = value.list();
      byte[] predicate = value.getValue(SharedServices.CF_AS_BYTES, ProductXYZ.getBytes());
      if (!Arrays.equals(predicate, "bsbm-voc_reviewFor".getBytes())) {
        return;
      }

      int requiredColumns = 0;
      for (KeyValue kv : reviewRow) {
        // TP-01
        if (Arrays.equals(kv.getValue(), "bsbm-voc_reviewFor".getBytes())) {
          keyValuesToTransmit.add(kv);
          requiredColumns++;
        }
        // TP-02
        else if (Arrays.equals(kv.getQualifier(), "dc_title".getBytes())) {
          keyValuesToTransmit.add(kv);
          requiredColumns++;
        }
        // TP-03
        else if (Arrays.equals(kv.getQualifier(), "rev_text".getBytes())) {
          keyValuesToTransmit.add(kv);
          requiredColumns++;
        }
        // TP-04
        else if (Arrays.equals(kv.getValue(), "rdfs_lang".getBytes())) {
          if (!Arrays.equals(kv.getQualifier(), "@en".getBytes())) {
            return;
          }
          keyValuesToTransmit.add(kv);
          requiredColumns++;
        }
        // TP-05
        else if (Arrays.equals(kv.getQualifier(), "bsbm-voc_reviewDate".getBytes())) {
          keyValuesToTransmit.add(kv);
          requiredColumns++;
        }
        // TP-06
        else if (Arrays.equals(kv.getValue(), "rev_reviewer".getBytes())) {
          keyValuesToTransmit.add(kv);
          requiredColumns++;
        }
        // OPTIONAL TP-08, TP-09, TP-10, TP-11
        else if (Arrays.equals(kv.getQualifier(), "bsbm-voc_rating1".getBytes())) {
          keyValuesToTransmit.add(kv);
        } else if (Arrays.equals(kv.getQualifier(), "bsbm-voc_rating2".getBytes())) {
          keyValuesToTransmit.add(kv);
        } else if (Arrays.equals(kv.getQualifier(), "bsbm-voc_rating3".getBytes())) {
          keyValuesToTransmit.add(kv);
        } else if (Arrays.equals(kv.getQualifier(), "bsbm-voc_rating4".getBytes())) {
          keyValuesToTransmit.add(kv);
        }
      }
      if (requiredColumns < 6) {
        return;
      }
      context.write(
          new CompositeKeyWritable(rowKey, 1),
          new KeyValueArrayWritable(SharedServices.listToArray(keyValuesToTransmit)));
    }