Enhancements to Wideskies encrypt/decrypt -- closes apache/incubator-pirk#81
diff --git a/src/main/java/org/apache/pirk/querier/wideskies/Querier.java b/src/main/java/org/apache/pirk/querier/wideskies/Querier.java
index 172e1e5..b63e06e 100644
--- a/src/main/java/org/apache/pirk/querier/wideskies/Querier.java
+++ b/src/main/java/org/apache/pirk/querier/wideskies/Querier.java
@@ -19,8 +19,8 @@
 package org.apache.pirk.querier.wideskies;
 
 import java.io.Serializable;
-import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.pirk.encryption.Paillier;
 import org.apache.pirk.query.wideskies.Query;
@@ -42,9 +42,9 @@
   // map to check the embedded selectors in the results for false positives;
   // if the selector is a fixed size < 32 bits, it is included as is
   // if the selector is of variable lengths
-  private HashMap<Integer,String> embedSelectorMap = null;
+  private Map<Integer,String> embedSelectorMap = null;
 
-  public Querier(List<String> selectorsInput, Paillier paillierInput, Query queryInput, HashMap<Integer,String> embedSelectorMapInput)
+  public Querier(List<String> selectorsInput, Paillier paillierInput, Query queryInput, Map<Integer,String> embedSelectorMapInput)
   {
     selectors = selectorsInput;
 
@@ -70,7 +70,7 @@
     return selectors;
   }
 
-  public HashMap<Integer,String> getEmbedSelectorMap()
+  public Map<Integer,String> getEmbedSelectorMap()
   {
     return embedSelectorMap;
   }
diff --git a/src/main/java/org/apache/pirk/querier/wideskies/QuerierDriver.java b/src/main/java/org/apache/pirk/querier/wideskies/QuerierDriver.java
index 4f26a71..c1a4e4a 100644
--- a/src/main/java/org/apache/pirk/querier/wideskies/QuerierDriver.java
+++ b/src/main/java/org/apache/pirk/querier/wideskies/QuerierDriver.java
@@ -33,6 +33,7 @@
 import org.apache.pirk.serialization.LocalFileSystemStore;
 import org.apache.pirk.utils.FileIOUtils;
 import org.apache.pirk.utils.PIRException;
+import org.apache.pirk.utils.QueryResultsWriter;
 import org.apache.pirk.utils.SystemConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -188,13 +189,13 @@
 
       // Perform the encryption
       EncryptQuery encryptQuery = new EncryptQuery(queryInfo, selectors, paillier);
-      encryptQuery.encrypt(numThreads);
+      Querier querier = encryptQuery.encrypt(numThreads);
 
       // Write necessary output files - two files written -
       // (1) Querier object to <outputFile>-QuerierConst.QUERIER_FILETAG
       // (2) Query object to <outputFile>-QuerierConst.QUERY_FILETAG
-      storage.store(outputFile + "-" + QuerierConst.QUERIER_FILETAG, encryptQuery.getQuerier());
-      storage.store(outputFile + "-" + QuerierConst.QUERY_FILETAG, encryptQuery.getQuery());
+      storage.store(outputFile + "-" + QuerierConst.QUERIER_FILETAG, querier);
+      storage.store(outputFile + "-" + QuerierConst.QUERY_FILETAG, querier.getQuery());
     }
     else
     // Decryption
@@ -214,8 +215,7 @@
 
       // Perform decryption and output the result file
       DecryptResponse decryptResponse = new DecryptResponse(response, querier);
-      decryptResponse.decrypt(numThreads);
-      decryptResponse.writeResultFile(outputFile);
+      QueryResultsWriter.writeResultFile(outputFile, decryptResponse.decrypt(numThreads));
     }
   }
 }
diff --git a/src/main/java/org/apache/pirk/querier/wideskies/decrypt/DecryptResponse.java b/src/main/java/org/apache/pirk/querier/wideskies/decrypt/DecryptResponse.java
index dbf2381..eae288f 100644
--- a/src/main/java/org/apache/pirk/querier/wideskies/decrypt/DecryptResponse.java
+++ b/src/main/java/org/apache/pirk/querier/wideskies/decrypt/DecryptResponse.java
@@ -18,16 +18,11 @@
  */
 package org.apache.pirk.querier.wideskies.decrypt;
 
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.TreeMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
@@ -42,6 +37,7 @@
 import org.apache.pirk.response.wideskies.Response;
 import org.apache.pirk.schema.response.QueryResponseJSON;
 import org.apache.pirk.utils.PIRException;
+import org.apache.pirk.utils.SystemConfiguration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -58,14 +54,18 @@
 
   private final Querier querier;
 
-  private final Map<String,List<QueryResponseJSON>> resultMap = new HashMap<>(); // selector -> ArrayList of hits
-
   public DecryptResponse(Response responseInput, Querier querierInput)
   {
     response = responseInput;
     querier = querierInput;
   }
 
+  public Map<String,List<QueryResponseJSON>> decrypt() throws InterruptedException, PIRException
+  {
+    int numThreads = SystemConfiguration.getIntProperty("numThreads", 1);
+    return decrypt(numThreads);
+  }
+
   /**
    * Method to decrypt the response elements and reconstructs the data elements
    * <p>
@@ -86,8 +86,10 @@
    * where D^k_r,l = Y_{r*numPartitionsPerDataElement + l} & (2^{r*numPartitionsPerDataElement} * (2^numBitsPerDataElement - 1))
    *
    */
-  public void decrypt(int numThreads) throws InterruptedException, PIRException
+  public Map<String,List<QueryResponseJSON>> decrypt(int numThreads) throws InterruptedException, PIRException
   {
+    Map<String,List<QueryResponseJSON>> resultMap = new HashMap<>(); // selector -> ArrayList of hits
+    
     QueryInfo queryInfo = response.getQueryInfo();
 
     Paillier paillier = querier.getPaillier();
@@ -159,8 +161,10 @@
     }
 
     es.shutdown();
+    
+    return resultMap;
   }
-
+  
   // Method to perform basic decryption of each raw response element - does not
   // extract and reconstruct the data elements
   private List<BigInteger> decryptElements(TreeMap<Integer,BigInteger> elements, Paillier paillier)
@@ -174,42 +178,4 @@
 
     return decryptedElements;
   }
-
-  /**
-   * Writes elements of the resultMap to output file, one line for each element, where each line is a string representation of the corresponding
-   * QueryResponseJSON object
-   */
-  public void writeResultFile(String filename) throws IOException
-  {
-    try (BufferedWriter bw = new BufferedWriter(new FileWriter(new File(filename))))
-    {
-      for (Entry<String,List<QueryResponseJSON>> entry : resultMap.entrySet())
-      {
-        for (QueryResponseJSON hitJSON : entry.getValue())
-        {
-          bw.write(hitJSON.getJSONString());
-          bw.newLine();
-        }
-      }
-    }
-  }
-
-  /**
-   * Writes elements of the resultMap to output file, one line for each element, where each line is a string representation of the corresponding
-   * QueryResponseJSON object
-   */
-  public void writeResultFile(File file) throws IOException
-  {
-    try (BufferedWriter bw = new BufferedWriter(new FileWriter(file)))
-    {
-      for (Entry<String,List<QueryResponseJSON>> entry : resultMap.entrySet())
-      {
-        for (QueryResponseJSON hitJSON : entry.getValue())
-        {
-          bw.write(hitJSON.getJSONString());
-          bw.newLine();
-        }
-      }
-    }
-  }
 }
diff --git a/src/main/java/org/apache/pirk/querier/wideskies/decrypt/DecryptResponseTask.java b/src/main/java/org/apache/pirk/querier/wideskies/decrypt/DecryptResponseTask.java
index 3f5a982..ab02094 100644
--- a/src/main/java/org/apache/pirk/querier/wideskies/decrypt/DecryptResponseTask.java
+++ b/src/main/java/org/apache/pirk/querier/wideskies/decrypt/DecryptResponseTask.java
@@ -73,7 +73,7 @@
     String selectorName = qSchema.getSelectorName();
 
     // Result is a map of (selector -> List of hits).
-    Map<String,List<QueryResponseJSON>> resultMap = new HashMap<>();
+    Map<String,List<QueryResponseJSON>> resultMap = new HashMap<>(selectors.size());
     for (String selector : selectors.values())
     {
       resultMap.put(selector, new ArrayList<QueryResponseJSON>());
diff --git a/src/main/java/org/apache/pirk/querier/wideskies/encrypt/EncryptQuery.java b/src/main/java/org/apache/pirk/querier/wideskies/encrypt/EncryptQuery.java
index 4dd90f7..60ba859 100644
--- a/src/main/java/org/apache/pirk/querier/wideskies/encrypt/EncryptQuery.java
+++ b/src/main/java/org/apache/pirk/querier/wideskies/encrypt/EncryptQuery.java
@@ -56,103 +56,90 @@
 {
   private static final Logger logger = LoggerFactory.getLogger(EncryptQuery.class);
 
-  private final QueryInfo queryInfo; // contains basic query information and functionality
+  // Contains basic query information.
+  private final QueryInfo queryInfo;
 
-  private Query query = null; // contains the query vectors
+  // Selectors for this query.
+  private final List<String> selectors;
 
-  private Querier querier = null; // contains the query vectors and encryption object
+  // Paillier encryption functionality.
+  private final Paillier paillier;
 
-  private final Paillier paillier; // Paillier encryption functionality
-
-  private List<String> selectors = null; // selectors for the query
-
-  // Map to check the embedded selectors in the results for false positives;
-  // if the selector is a fixed size < 32 bits, it is included as is
-  // if the selector is of variable lengths
-  private HashMap<Integer,String> embedSelectorMap = null;
-
-  public EncryptQuery(QueryInfo queryInfoInput, List<String> selectorsInput, Paillier paillierInput)
+  /**
+   * Constructs a query encryptor using the given query information, selectors, and Paillier cryptosystem.
+   * 
+   * @param queryInfo
+   *          Fundamental information about the query.
+   * @param selectors
+   *          the list of selectors for this query.
+   * @param paillier
+   *          the Paillier cryptosystem to use.
+   */
+  public EncryptQuery(QueryInfo queryInfo, List<String> selectors, Paillier paillier)
   {
-    queryInfo = queryInfoInput;
-
-    selectors = selectorsInput;
-
-    paillier = paillierInput;
-
-    embedSelectorMap = new HashMap<>();
-  }
-
-  public Paillier getPaillier()
-  {
-    return paillier;
-  }
-
-  public QueryInfo getQueryInfo()
-  {
-    return queryInfo;
-  }
-
-  public Query getQuery()
-  {
-    return query;
-  }
-
-  public Querier getQuerier()
-  {
-    return querier;
-  }
-
-  public List<String> getSelectors()
-  {
-    return selectors;
-  }
-
-  public HashMap<Integer,String> getEmbedSelectorMap()
-  {
-    return embedSelectorMap;
+    this.queryInfo = queryInfo;
+    this.selectors = selectors;
+    this.paillier = paillier;
   }
 
   /**
-   * Encrypt, building the Query object, calculating and setting the query vectors.
+   * Encrypts the query described by the query information using Paillier encryption.
+   * <p>
+   * The encryption builds a <code>Querier</code> object, calculating and setting the query vectors.
    * <p>
    * Uses the system configured number of threads to conduct the encryption, or a single thread if the configuration has not been set.
    * 
    * @throws InterruptedException
-   *           if the task was interrupted during encryption.
+   *           If the task was interrupted during encryption.
    * @throws PIRException
+   *           If a problem occurs performing the encryption.
+   * @return The querier containing the query, and all information required to perform decryption.
    */
-  public void encrypt() throws InterruptedException, PIRException
+  public Querier encrypt() throws InterruptedException, PIRException
   {
     int numThreads = SystemConfiguration.getIntProperty("numThreads", 1);
-    encrypt(numThreads);
+    return encrypt(numThreads);
   }
 
   /**
-   * Encrypt, building the Query object, calculating and setting the query vectors
+   * Encrypts the query described by the query information using Paillier encryption using the given number of threads.
+   * <p>
+   * The encryption builds a <code>Querier</code> object, calculating and setting the query vectors.
    * <p>
    * If we have hash collisions over our selector set, we will append integers to the key starting with 0 until we no longer have collisions.
    * <p>
    * For encrypted query vector E = <E_0, ..., E_{(2^hashBitSize)-1}>:
    * <p>
    * E_i = 2^{j*dataPartitionBitSize} if i = H_k(selector_j) 0 otherwise
+   * 
+   * @param numThreads
+   *          the number of threads to use when performing the encryption.
+   * @throws InterruptedException
+   *           If the task was interrupted during encryption.
+   * @throws PIRException
+   *           If a problem occurs performing the encryption.
+   * @return The querier containing the query, and all information required to perform decryption.
    */
-  public void encrypt(int numThreads) throws InterruptedException, PIRException
+  public Querier encrypt(int numThreads) throws InterruptedException, PIRException
   {
-    query = new Query(queryInfo, paillier.getN());
+    Query query = new Query(queryInfo, paillier.getN());
 
     // Determine the query vector mappings for the selectors; vecPosition -> selectorNum
     Map<Integer,Integer> selectorQueryVecMapping = computeSelectorQueryVecMap();
 
     // Form the embedSelectorMap
-    populateEmbeddedSelectorMap();
+    // Map to check the embedded selectors in the results for false positives;
+    // if the selector is a fixed size < 32 bits, it is included as is
+    // if the selector is of variable lengths
+    Map<Integer,String> embedSelectorMap = computeEmbeddedSelectorMap();
 
     if (numThreads == 1)
     {
-      serialEncrypt(selectorQueryVecMapping);
+      serialEncrypt(query, selectorQueryVecMapping);
     }
     else
     {
-      parallelEncrypt(Math.max(2, numThreads), selectorQueryVecMapping);
+      parallelEncrypt(Math.max(2, numThreads), query, selectorQueryVecMapping);
     }
 
     // Generate the expTable in Query, if we are using it and if
@@ -163,8 +150,8 @@
       query.generateExpTable();
     }
 
-    // Set the Querier object
-    querier = new Querier(selectors, paillier, query, embedSelectorMap);
+    // Return the Querier object.
+    return new Querier(selectors, paillier, query, embedSelectorMap);
   }
 
   private Map<Integer,Integer> computeSelectorQueryVecMap()
@@ -200,31 +187,30 @@
     return selectorQueryVecMapping;
   }
 
-  private void populateEmbeddedSelectorMap()
+  private Map<Integer,String> computeEmbeddedSelectorMap() throws PIRException
   {
     QuerySchema qSchema = QuerySchemaRegistry.get(queryInfo.getQueryType());
+    String selectorName = qSchema.getSelectorName();
     DataSchema dSchema = DataSchemaRegistry.get(qSchema.getDataSchemaName());
-    String type = dSchema.getElementType(qSchema.getSelectorName());
+    String type = dSchema.getElementType(selectorName);
+
+    Map<Integer,String> embedSelectorMap = new HashMap<>(selectors.size());
+
     int sNum = 0;
     for (String selector : selectors)
     {
-      String embeddedSelector = null;
-      try
-      {
-        embeddedSelector = QueryUtils.getEmbeddedSelector(selector, type, dSchema.getPartitionerForElement(qSchema.getSelectorName()));
-      } catch (Exception e)
-      {
-        logger.info("Caught exception for selector = " + selector);
-        e.printStackTrace();
-        // TODO: Check: should continue?
-      }
-
+      String embeddedSelector = QueryUtils.getEmbeddedSelector(selector, type, dSchema.getPartitionerForElement(selectorName));
       embedSelectorMap.put(sNum, embeddedSelector);
-      ++sNum;
+      sNum += 1;
     }
+
+    return embedSelectorMap;
   }
 
-  private void serialEncrypt(Map<Integer,Integer> selectorQueryVecMapping) throws PIRException
+  /*
+   * Perform the encryption using a single thread, and avoiding the overhead of thread management.
+   */
+  private void serialEncrypt(Query query, Map<Integer,Integer> selectorQueryVecMapping) throws PIRException
   {
     int numElements = 1 << queryInfo.getHashBitSize(); // 2^hashBitSize
 
@@ -234,7 +220,10 @@
     logger.info("Completed serial creation of encrypted query vectors");
   }
 
-  private void parallelEncrypt(int numThreads, Map<Integer,Integer> selectorQueryVecMapping) throws InterruptedException, PIRException
+  /*
+   * Performs the encryption with numThreads.
+   */
+  private void parallelEncrypt(int numThreads, Query query, Map<Integer,Integer> selectorQueryVecMapping) throws InterruptedException, PIRException
   {
     // Encrypt and form the query vector
     ExecutorService es = Executors.newCachedThreadPool();
diff --git a/src/main/java/org/apache/pirk/schema/data/partitioner/PrimitiveTypePartitioner.java b/src/main/java/org/apache/pirk/schema/data/partitioner/PrimitiveTypePartitioner.java
index 7559441..d222ec0 100644
--- a/src/main/java/org/apache/pirk/schema/data/partitioner/PrimitiveTypePartitioner.java
+++ b/src/main/java/org/apache/pirk/schema/data/partitioner/PrimitiveTypePartitioner.java
@@ -32,7 +32,7 @@
  * Class for partitioning objects with primitive Java types
  * 
  */
-public class PrimitiveTypePartitioner implements DataPartitioner
+public final class PrimitiveTypePartitioner implements DataPartitioner
 {
   private static final long serialVersionUID = 1L;
 
diff --git a/src/main/java/org/apache/pirk/test/distributed/testsuite/DistTestSuite.java b/src/main/java/org/apache/pirk/test/distributed/testsuite/DistTestSuite.java
index f44815a..d02bb87 100644
--- a/src/main/java/org/apache/pirk/test/distributed/testsuite/DistTestSuite.java
+++ b/src/main/java/org/apache/pirk/test/distributed/testsuite/DistTestSuite.java
@@ -42,6 +42,7 @@
 import org.apache.pirk.test.utils.BaseTests;
 import org.apache.pirk.test.utils.Inputs;
 import org.apache.pirk.test.utils.TestUtils;
+import org.apache.pirk.utils.QueryResultsWriter;
 import org.apache.pirk.utils.SystemConfiguration;
 import org.apache.spark.launcher.SparkLauncher;
 import org.json.simple.JSONObject;
@@ -434,16 +435,12 @@
     // Perform the encryption
     logger.info("Performing encryption of the selectors - forming encrypted query vectors:");
     EncryptQuery encryptQuery = new EncryptQuery(queryInfo, selectors, paillier);
-    encryptQuery.encrypt(numThreads);
+    Querier querier = encryptQuery.encrypt(numThreads);
     logger.info("Completed encryption of the selectors - completed formation of the encrypted query vectors:");
 
-    // Grab the necessary objects
-    Querier querier = encryptQuery.getQuerier();
-    Query query = encryptQuery.getQuery();
-
-    // Write the Querier object to a file
+    // Write the Query object to a file
     Path queryInputDirPath = new Path(queryInputDir);
-    new HadoopFileSystemStore(fs).store(queryInputDirPath, query);
+    new HadoopFileSystemStore(fs).store(queryInputDirPath, querier.getQuery());
     fs.deleteOnExit(queryInputDirPath);
 
     // Grab the original data and query schema properties to reset upon completion
@@ -542,8 +539,7 @@
 
     // Perform decryption and output the result file
     DecryptResponse decryptResponse = new DecryptResponse(response, querier);
-    decryptResponse.decrypt(numThreads);
-    decryptResponse.writeResultFile(fileFinalResults);
+    QueryResultsWriter.writeResultFile(fileFinalResults, decryptResponse.decrypt(numThreads));
     logger.info("Completed performing decryption and writing final results file");
 
     // Read in results
diff --git a/src/main/java/org/apache/pirk/test/utils/StandaloneQuery.java b/src/main/java/org/apache/pirk/test/utils/StandaloneQuery.java
index afac2b0..b86d78c 100644
--- a/src/main/java/org/apache/pirk/test/utils/StandaloneQuery.java
+++ b/src/main/java/org/apache/pirk/test/utils/StandaloneQuery.java
@@ -22,8 +22,8 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.pirk.encryption.Paillier;
 import org.apache.pirk.querier.wideskies.Querier;
@@ -40,6 +40,7 @@
 import org.apache.pirk.schema.response.QueryResponseJSON;
 import org.apache.pirk.serialization.LocalFileSystemStore;
 import org.apache.pirk.utils.PIRException;
+import org.apache.pirk.utils.QueryResultsWriter;
 import org.apache.pirk.utils.SystemConfiguration;
 import org.json.simple.JSONObject;
 import org.slf4j.Logger;
@@ -94,22 +95,21 @@
     // Perform the encryption
     logger.info("Performing encryption of the selectors - forming encrypted query vectors:");
     EncryptQuery encryptQuery = new EncryptQuery(queryInfo, selectors, paillier);
-    encryptQuery.encrypt(numThreads);
+    Querier querier = encryptQuery.encrypt(numThreads);
     logger.info("Completed encryption of the selectors - completed formation of the encrypted query vectors:");
 
     // Dork with the embedSelectorMap to generate a false positive for the last valid selector in selectors
     if (testFalsePositive)
     {
-      Querier querier = encryptQuery.getQuerier();
-      HashMap<Integer,String> embedSelectorMap = querier.getEmbedSelectorMap();
+      Map<Integer,String> embedSelectorMap = querier.getEmbedSelectorMap();
       logger.info("embedSelectorMap((embedSelectorMap.size()-2)) = " + embedSelectorMap.get((embedSelectorMap.size() - 2)) + " selector = "
           + selectors.get((embedSelectorMap.size() - 2)));
       embedSelectorMap.put((embedSelectorMap.size() - 2), "fakeEmbeddedSelector");
     }
 
     // Write necessary output files
-    storage.store(fileQuerier, encryptQuery.getQuerier());
-    storage.store(fileQuery, encryptQuery.getQuery());
+    storage.store(fileQuerier, querier);
+    storage.store(fileQuery, querier.getQuery());
 
     // Perform the PIR query and build the response elements
     logger.info("Performing the PIR Query and constructing the response elements:");
@@ -141,12 +141,11 @@
     // Reconstruct the necessary objects from the files
     logger.info("Performing decryption; writing final results file");
     Response responseIn = storage.recall(fileResponse, Response.class);
-    Querier querier = storage.recall(fileQuerier, Querier.class);
+    querier = storage.recall(fileQuerier, Querier.class);
 
     // Perform decryption and output the result file
     DecryptResponse decryptResponse = new DecryptResponse(responseIn, querier);
-    decryptResponse.decrypt(numThreads);
-    decryptResponse.writeResultFile(fileFinalResults);
+    QueryResultsWriter.writeResultFile(fileFinalResults, decryptResponse.decrypt(numThreads));
     logger.info("Completed performing decryption and writing final results file");
 
     // Read in results
diff --git a/src/main/java/org/apache/pirk/test/utils/TestUtils.java b/src/main/java/org/apache/pirk/test/utils/TestUtils.java
index 1ea01fb..6573a12 100644
--- a/src/main/java/org/apache/pirk/test/utils/TestUtils.java
+++ b/src/main/java/org/apache/pirk/test/utils/TestUtils.java
@@ -21,6 +21,7 @@
 import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
@@ -267,8 +268,10 @@
 
   /**
    * Converts the result file into an ArrayList of QueryResponseJSON objects
+   * @throws IOException 
+   * @throws FileNotFoundException 
    */
-  public static List<QueryResponseJSON> readResultsFile(File file)
+  public static List<QueryResponseJSON> readResultsFile(File file) throws FileNotFoundException, IOException
   {
     List<QueryResponseJSON> results = new ArrayList<>();
     try (BufferedReader br = new BufferedReader(new FileReader(file)))
@@ -279,9 +282,6 @@
         QueryResponseJSON jsonResult = new QueryResponseJSON(line);
         results.add(jsonResult);
       }
-    } catch (Exception e)
-    {
-      logger.error(e.toString());
     }
 
     return results;
diff --git a/src/main/java/org/apache/pirk/utils/FileIOUtils.java b/src/main/java/org/apache/pirk/utils/FileIOUtils.java
index 8924a86..9fd0dec 100644
--- a/src/main/java/org/apache/pirk/utils/FileIOUtils.java
+++ b/src/main/java/org/apache/pirk/utils/FileIOUtils.java
@@ -119,28 +119,18 @@
 
   public static void writeArrayList(ArrayList<String> aList, File file) throws IOException
   {
-    FileOutputStream fos = new FileOutputStream(file);
-    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos));
-
-    for (String s : aList)
+    try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))))
     {
-      bw.write(s);
-      bw.newLine();
+      for (String s : aList)
+      {
+        bw.write(s);
+        bw.newLine();
+      }
     }
-    bw.close();
   }
 
   public static void writeArrayList(ArrayList<String> aList, String filename) throws IOException
   {
-    File file = new File(filename);
-    FileOutputStream fos = new FileOutputStream(file);
-    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos));
-
-    for (String s : aList)
-    {
-      bw.write(s);
-      bw.newLine();
-    }
-    bw.close();
+    writeArrayList(aList, new File(filename));
   }
 }
diff --git a/src/main/java/org/apache/pirk/utils/QueryResultsWriter.java b/src/main/java/org/apache/pirk/utils/QueryResultsWriter.java
new file mode 100644
index 0000000..65470e4
--- /dev/null
+++ b/src/main/java/org/apache/pirk/utils/QueryResultsWriter.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pirk.utils;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.pirk.schema.response.QueryResponseJSON;
+
+public class QueryResultsWriter
+{
+
+
+  /**
+   * Writes elements of the resultMap to output file, one line for each element, where each line is a string representation of the corresponding
+   * QueryResponseJSON object
+   */
+  public static void writeResultFile(String filename, Map<String,List<QueryResponseJSON>> resultMap) throws IOException
+  {
+    try (BufferedWriter bw = new BufferedWriter(new FileWriter(new File(filename))))
+    {
+      for (Entry<String,List<QueryResponseJSON>> entry : resultMap.entrySet())
+      {
+        for (QueryResponseJSON hitJSON : entry.getValue())
+        {
+          bw.write(hitJSON.getJSONString());
+          bw.newLine();
+        }
+      }
+    }
+  }
+
+  /**
+   * Writes elements of the resultMap to output file, one line for each element, where each line is a string representation of the corresponding
+   * QueryResponseJSON object
+   */
+  public static void writeResultFile(File file, Map<String,List<QueryResponseJSON>> resultMap) throws IOException
+  {
+    try (BufferedWriter bw = new BufferedWriter(new FileWriter(file)))
+    {
+      for (Entry<String,List<QueryResponseJSON>> entry : resultMap.entrySet())
+      {
+        for (QueryResponseJSON hitJSON : entry.getValue())
+        {
+          bw.write(hitJSON.getJSONString());
+          bw.newLine();
+        }
+      }
+    }
+  }
+
+}