Merge pull request #2 from apache/reset

Reset
diff --git a/README.md b/README.md
index 60ded2f..9b03b92 100644
--- a/README.md
+++ b/README.md
@@ -134,6 +134,7 @@
 * `/query`
 * `/serialize`
 * `/merge`
+* `/reset`
 * `/status`
 
 Each is described below, along with examples of input and output. As noted above, all calls accepting input may
@@ -358,6 +359,22 @@
 }
 ```
 
+### Reset
+
+A call to `/reset` clears the data from the specified sketch(es), allowing them to resume accepting data from a clean
+state. Especially with the server's constraint on defining new sketches, the `/reset` call allows the server to operate
+in an accumulator model, using pre-defined merge targets that can be reset between merge operations.
+
+The syntax of a `/reset` call is simple, as shown in [reset.json][example/reset.json]:
+```json
+{
+    "name": "theta0"
+}
+```
+
+There is no data returned from a call to `/reset`; a status code 200 signifies success.
+
+
 ### Status
 
 A request to the `/status` page returns a list of the configured sketches. There is no input to this query.
diff --git a/example/reset.json b/example/reset.json
new file mode 100644
index 0000000..d5a3cf4
--- /dev/null
+++ b/example/reset.json
@@ -0,0 +1,3 @@
+{
+    "name": "theta0"
+}
diff --git a/src/main/java/org/apache/datasketches/server/ResetHandler.java b/src/main/java/org/apache/datasketches/server/ResetHandler.java
new file mode 100644
index 0000000..ec22a47
--- /dev/null
+++ b/src/main/java/org/apache/datasketches/server/ResetHandler.java
@@ -0,0 +1,83 @@
+/*
+ * 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.datasketches.server;
+
+import static org.apache.datasketches.server.SketchConstants.QUERY_NAME_FIELD;
+
+
+import org.apache.datasketches.cpc.CpcSketch;
+import org.apache.datasketches.frequencies.ItemsSketch;
+import org.apache.datasketches.hll.HllSketch;
+import org.apache.datasketches.kll.KllFloatsSketch;
+import org.apache.datasketches.sampling.ReservoirItemsSketch;
+import org.apache.datasketches.sampling.VarOptItemsSketch;
+import org.apache.datasketches.theta.Union;
+
+
+import com.google.gson.JsonObject;
+
+public class ResetHandler extends BaseSketchesQueryHandler {
+  ResetHandler(final SketchStorage sketches) {
+    super(sketches, false);
+  }
+
+  @Override
+  @SuppressWarnings("unchecked")
+  protected JsonObject processQuery(final JsonObject query) {
+    if (!query.has(QUERY_NAME_FIELD)) {
+      throw new IllegalArgumentException("Query missing sketch name field");
+    }
+
+    final String key = query.get(QUERY_NAME_FIELD).getAsString();
+    if (!sketches.contains(key)) {
+      throw new IllegalArgumentException("Invalid sketch name: " + key);
+    }
+
+    final SketchStorage.SketchEntry se = sketches.getSketch(key);
+
+    switch (se.family) {
+      case UNION:
+         ((Union) se.sketch).reset();
+        break;
+      case KLL:
+        se.sketch = new KllFloatsSketch(se.configK);
+        break;
+      case FREQUENCY:
+        ((ItemsSketch<String>) se.sketch).reset();
+        break;
+      case HLL:
+        ((HllSketch) se.sketch).reset();
+        break;
+      case CPC:
+        ((CpcSketch) se.sketch).reset();
+        break;
+      case RESERVOIR:
+        ((ReservoirItemsSketch<String>) se.sketch).reset();
+        break;
+      case VAROPT:
+        ((VarOptItemsSketch<String>) se.sketch).reset();
+        break;
+    }
+
+    // nothing to return from reset calls
+    return null;
+  }
+
+}
diff --git a/src/main/java/org/apache/datasketches/server/SketchConstants.java b/src/main/java/org/apache/datasketches/server/SketchConstants.java
index 6081ef3..6ef32c8 100644
--- a/src/main/java/org/apache/datasketches/server/SketchConstants.java
+++ b/src/main/java/org/apache/datasketches/server/SketchConstants.java
@@ -26,6 +26,7 @@
   public static final String STATUS_PATH = "/status";
   public static final String QUERY_PATH = "/query";
   public static final String MERGE_PATH = "/merge";
+  public static final String RESET_PATH = "/reset";
 
   // JSON Query/Update/Merge Field Names
   public static final String QUERY_NAME_FIELD = "name";
diff --git a/src/main/java/org/apache/datasketches/server/SketchServer.java b/src/main/java/org/apache/datasketches/server/SketchServer.java
index 59c3916..4fefb9a 100644
--- a/src/main/java/org/apache/datasketches/server/SketchServer.java
+++ b/src/main/java/org/apache/datasketches/server/SketchServer.java
@@ -54,7 +54,7 @@
     // Error page unless you have a correct URL
     final ContextHandler contextRoot = new ContextHandler("/");
     contextRoot.setContextPath("/");
-    contextRoot.setHandler(new ErrorHandler());
+    contextRoot.setErrorHandler(new ErrorHandler());
 
     final ContextHandler contextStatus = new ContextHandler(STATUS_PATH);
     contextStatus.setHandler(new StatusHandler(sketches));
@@ -71,13 +71,17 @@
     final ContextHandler contextQuery = new ContextHandler(QUERY_PATH);
     contextQuery.setHandler(new DataQueryHandler(sketches));
 
+    final ContextHandler contextReset = new ContextHandler(RESET_PATH);
+    contextReset.setHandler(new ResetHandler(sketches));
+
     final ContextHandlerCollection contexts =
         new ContextHandlerCollection(contextRoot,
             contextStatus,
             contextSerialize,
             contextUpdate,
             contextMerge,
-            contextQuery);
+            contextQuery,
+            contextReset);
     server.setHandler(contexts);
   }
 
diff --git a/src/main/java/org/apache/datasketches/server/SketchStorage.java b/src/main/java/org/apache/datasketches/server/SketchStorage.java
index 5c21de8..c6c8c89 100644
--- a/src/main/java/org/apache/datasketches/server/SketchStorage.java
+++ b/src/main/java/org/apache/datasketches/server/SketchStorage.java
@@ -58,23 +58,26 @@
     public final Family family;
     public final ValueType type;
     public Object sketch;
+    public int configK;
 
-    SketchEntry(final Family family, final ValueType type, final Object sketch) throws IllegalArgumentException {
+    SketchEntry(final Family family, final ValueType type, final Object sketch, final int configK) throws IllegalArgumentException {
       if (isDistinctCounting(family) && type == null)
         throw new IllegalArgumentException("Must specify a value type for distinct counting sketches");
 
       this.family = family;
       this.type = type;
       this.sketch = sketch;
+      this.configK = configK;
     }
 
-    SketchEntry(final Family family, final Object sketch) throws IllegalArgumentException {
+    SketchEntry(final Family family, final Object sketch, final int configK) throws IllegalArgumentException {
       if (isDistinctCounting(family))
         throw new IllegalArgumentException("Must specify a value type for distinct counting sketches");
 
       this.family = family;
       this.type = null;
       this.sketch = sketch;
+      this.configK = configK;
     }
   }
 
@@ -152,33 +155,33 @@
         case QUICKSELECT:
           // make a Union so we can handle merges later
           sketchEntry = new SketchEntry(Family.UNION, ValueType.stringToType(info.type),
-              new SetOperationBuilder().setNominalEntries(1 << info.k).buildUnion());
+              new SetOperationBuilder().setNominalEntries(1 << info.k).buildUnion(), info.k);
           break;
 
         case HLL:
           sketchEntry = new SketchEntry(Family.HLL, ValueType.stringToType(info.type),
-              new HllSketch(info.k));
+              new HllSketch(info.k), info.k);
           break;
 
         case CPC:
           sketchEntry = new SketchEntry(Family.CPC, ValueType.stringToType(info.type),
-              new CpcSketch(info.k));
+              new CpcSketch(info.k), info.k);
           break;
 
         case KLL:
-          sketchEntry = new SketchEntry(Family.KLL, new KllFloatsSketch(info.k));
+          sketchEntry = new SketchEntry(Family.KLL, new KllFloatsSketch(info.k), info.k);
           break;
 
         case FREQUENCY:
-          sketchEntry = new SketchEntry(Family.FREQUENCY, new ItemsSketch<String>(info.k));
+          sketchEntry = new SketchEntry(Family.FREQUENCY, new ItemsSketch<String>(info.k), info.k);
           break;
 
         case RESERVOIR:
-          sketchEntry = new SketchEntry(Family.RESERVOIR, ReservoirItemsSketch.<String>newInstance(info.k));
+          sketchEntry = new SketchEntry(Family.RESERVOIR, ReservoirItemsSketch.<String>newInstance(info.k), info.k);
           break;
 
         case VAROPT:
-          sketchEntry = new SketchEntry(Family.VAROPT, VarOptItemsSketch.<String>newInstance(info.k));
+          sketchEntry = new SketchEntry(Family.VAROPT, VarOptItemsSketch.<String>newInstance(info.k), info.k);
           break;
       }