You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by dp...@apache.org on 2017/06/28 18:17:24 UTC

[01/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Repository: lucene-solr
Updated Branches:
  refs/heads/master 85a27a231 -> d5963bebc


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetExtrasCloudTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetExtrasCloudTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetExtrasCloudTest.java
new file mode 100644
index 0000000..427b50d
--- /dev/null
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetExtrasCloudTest.java
@@ -0,0 +1,253 @@
+/*
+ * 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.solr.analytics.facet;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.solr.client.solrj.request.UpdateRequest;
+import org.apache.solr.common.util.NamedList;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class FieldFacetExtrasCloudTest extends AbstractAnalyticsFacetCloudTest {
+  public static final int INT = 21;
+  public static final int LONG = 22;
+  public static final int FLOAT = 23;
+  public static final int DOUBLE = 24;
+  public static final int DATE = 25;
+  public static final int STRING = 26;
+  public static final int NUM_LOOPS = 100;
+  
+  //INT
+  static ArrayList<ArrayList<Integer>> intLongTestStart; 
+  static ArrayList<ArrayList<Integer>> intFloatTestStart; 
+  static ArrayList<ArrayList<Integer>> intDoubleTestStart; 
+  static ArrayList<ArrayList<Integer>> intStringTestStart; 
+  
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    setupCluster();
+
+    //INT
+    intLongTestStart = new ArrayList<>();
+    intFloatTestStart = new ArrayList<>();
+    intDoubleTestStart = new ArrayList<>();
+    intStringTestStart = new ArrayList<>();
+
+    UpdateRequest req = new UpdateRequest();
+    
+    for (int j = 0; j < NUM_LOOPS; ++j) {
+      int i = j%INT;
+      long l = j%LONG;
+      float f = j%FLOAT;
+      double d = j%DOUBLE;
+      int dt = j%DATE;
+      int s = j%STRING;
+      
+      List<String> fields = new ArrayList<>();
+      fields.add("id"); fields.add("1000"+j);
+      fields.add("int_id"); fields.add("" + i);
+      fields.add("long_ld"); fields.add("" + l);
+      fields.add("float_fd"); fields.add("" + f);
+      fields.add("double_dd"); fields.add("" + d);
+      fields.add("date_dtd"); fields.add((1800+dt) + "-12-31T23:59:59.999Z");
+      fields.add("string_sd"); fields.add("abc" + s);
+      req.add(fields.toArray(new String[0]));
+      
+      //Long
+      if (j-LONG<0) {
+        ArrayList<Integer> list1 = new ArrayList<>();
+        list1.add(i);
+        intLongTestStart.add(list1);
+      } else {
+        intLongTestStart.get((int)l).add(i);
+      }
+      //String
+      if (j-FLOAT<0) {
+        ArrayList<Integer> list1 = new ArrayList<>();
+        list1.add(i);
+        intFloatTestStart.add(list1);
+      } else {
+        intFloatTestStart.get((int)f).add(i);
+      }
+      //String
+      if (j-DOUBLE<0) {
+        ArrayList<Integer> list1 = new ArrayList<>();
+        list1.add(i);
+        intDoubleTestStart.add(list1);
+      } else {
+        intDoubleTestStart.get((int)d).add(i);
+      }
+      //String
+      if (j-STRING<0) {
+        ArrayList<Integer> list1 = new ArrayList<>();
+        list1.add(i);
+        intStringTestStart.add(list1);
+      } else {
+        intStringTestStart.get(s).add(i);
+      }
+    }
+
+    req.commit(cluster.getSolrClient(), COLLECTIONORALIAS);
+  }
+  
+  @Test
+  public void limitTest() throws Exception { 
+    String[] params = new String[] {
+        "o.lr.s.mean", "mean(int_id)",
+        "o.lr.s.median", "median(int_id)",
+        "o.lr.s.count", "count(int_id)",
+        "o.lr.s.percentile_20", "percentile(20,int_id)",
+        "o.lr.ff.long_ld", "long_ld",
+        "o.lr.ff.long_ld.ss", "mean",
+        "o.lr.ff.long_ld.sd", "asc",
+        "o.lr.ff.long_ld.limit", "5",
+        "o.lr.ff.float_fd", "float_fd",
+        "o.lr.ff.float_fd.ss", "median",
+        "o.lr.ff.float_fd.sd", "desc",
+        "o.lr.ff.float_fd.limit", "3",
+        "o.lr.ff.double_dd", "double_dd",
+        "o.lr.ff.double_dd.ss", "count",
+        "o.lr.ff.double_dd.sd", "asc",
+        "o.lr.ff.double_dd.limit", "7",
+        "o.lr.ff.string_sd", "string_sd",
+        "o.lr.ff.string_sd.ss", "percentile_20",
+        "o.lr.ff.string_sd.sd", "desc",
+        "o.lr.ff.string_sd.limit", "1"
+    };
+    
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+
+    Collection<Double> lon = getValueList(response, "lr", "fieldFacets", "long_ld", "mean", false);
+    assertEquals(responseStr, lon.size(),5);
+    Collection<Double> flo = getValueList(response, "lr", "fieldFacets", "float_fd", "median", false);
+    assertEquals(responseStr, flo.size(),3);
+    Collection<Long> doub = getValueList(response, "lr", "fieldFacets", "double_dd", "count", false);
+    assertEquals(responseStr, doub.size(),7);
+    Collection<Integer> string = getValueList(response, "lr", "fieldFacets", "string_sd", "percentile_20", false);
+    assertEquals(responseStr, string.size(),1);
+  }
+  
+  @Test
+  public void offsetTest() throws Exception { 
+    String[] params = new String[] {
+        "o.offAll.s.mean", "mean(int_id)",
+        "o.offAll.ff", "long_ld",
+        "o.offAll.ff.long_ld.ss", "mean",
+        "o.offAll.ff.long_ld.sd", "asc",
+        "o.offAll.ff.long_ld.limit", "7",
+
+        "o.off0.s.mean", "mean(int_id)",
+        "o.off0.ff", "long_ld",
+        "o.off0.ff.long_ld.ss", "mean",
+        "o.off0.ff.long_ld.sd", "asc",
+        "o.off0.ff.long_ld.limit", "2",
+        "o.off0.ff.long_ld.offset", "0",
+
+        "o.off1.s.mean", "mean(int_id)",
+        "o.off1.ff", "long_ld",
+        "o.off1.ff.long_ld.ss", "mean",
+        "o.off1.ff.long_ld.sd", "asc",
+        "o.off1.ff.long_ld.limit", "2",
+        "o.off1.ff.long_ld.offset", "2",
+
+        "o.off2.s.mean", "mean(int_id)",
+        "o.off2.ff", "long_ld",
+        "o.off2.ff.long_ld.ss", "mean",
+        "o.off2.ff.long_ld.sd", "asc",
+        "o.off2.ff.long_ld.limit", "3",
+        "o.off2.ff.long_ld.offset", "4"
+    };
+    
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+
+    Collection<Double> lon;
+   
+    List<Double> all = new ArrayList<>();
+    lon = getValueList(response, "off0", "fieldFacets", "long_ld", "mean", false);
+    assertEquals(responseStr, lon.size(),2);
+    assertArrayEquals(new Double[]{ 1.5,  2.0 }, lon.toArray(new Double[0]));
+    all.addAll(lon);
+    
+    lon = getValueList(response, "off1", "fieldFacets", "long_ld", "mean", false);
+    assertEquals(responseStr, lon.size(),2);
+    assertArrayEquals(new Double[]{ 3.0,  4.0 }, lon.toArray(new Double[0]));
+    all.addAll(lon);
+    
+    lon = getValueList(response, "off2", "fieldFacets", "long_ld", "mean", false);
+    assertEquals(responseStr, lon.size(),3);
+    assertArrayEquals(new Double[]{ 5.0,  5.75, 6.0 }, lon.toArray(new Double[0]));
+    all.addAll(lon);
+    
+    lon = getValueList(response, "offAll", "fieldFacets", "long_ld", "mean", false);
+    assertEquals(responseStr, lon.size(),7);
+    assertArrayEquals(all.toArray(new Double[0]), lon.toArray(new Double[0]));
+  }
+  
+  @SuppressWarnings("unchecked")
+  @Test
+  public void sortTest() throws Exception { 
+    String[] params = new String[] {
+        "o.sr.s.mean", "mean(int_id)",
+        "o.sr.s.median", "median(int_id)",
+        "o.sr.s.count", "count(int_id)",
+        "o.sr.s.percentile_20", "percentile(20,int_id)",
+        "o.sr.ff", "long_ld",
+        "o.sr.ff.long_ld.ss", "mean",
+        "o.sr.ff.long_ld.sd", "asc",
+        "o.sr.ff", "float_fd",
+        "o.sr.ff.float_fd.ss", "median",
+        "o.sr.ff.float_fd.sd", "desc",
+        "o.sr.ff", "double_dd",
+        "o.sr.ff.double_dd.ss", "count",
+        "o.sr.ff.double_dd.sd", "asc",
+        "o.sr.ff", "string_sd",
+        "o.sr.ff.string_sd.ss", "percentile_20",
+        "o.sr.ff.string_sd.sd", "desc"
+    };
+    
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    Collection<Double> lon = getValueList(response, "sr", "fieldFacets", "long_ld", "mean", false);
+    ArrayList<Double> longTest = calculateNumberStat(intLongTestStart, "mean");
+    Collections.sort(longTest);
+    assertEquals(responseStr, longTest,lon);
+    
+    Collection<Double> flo = getValueList(response, "sr", "fieldFacets", "float_fd", "median", false);
+    ArrayList<Double> floatTest = calculateNumberStat(intFloatTestStart, "median");
+    Collections.sort(floatTest,Collections.reverseOrder());
+    assertEquals(responseStr, floatTest,flo);
+    
+    Collection<Long> doub = getValueList(response, "sr", "fieldFacets", "double_dd", "count", false);
+    ArrayList<Long> doubleTest = (ArrayList<Long>)calculateStat(intDoubleTestStart, "count");
+    Collections.sort(doubleTest);
+    assertEquals(responseStr, doubleTest,doub);
+    
+    Collection<Integer> string = getValueList(response, "sr", "fieldFacets", "string_sd", "percentile_20", false);
+    ArrayList<Integer> stringTest = (ArrayList<Integer>)calculateStat(intStringTestStart, "perc_20");
+    Collections.sort(stringTest,Collections.reverseOrder());
+    assertEquals(responseStr, stringTest,string);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetExtrasTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetExtrasTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetExtrasTest.java
index fc7df4b..a7e0012 100644
--- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetExtrasTest.java
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetExtrasTest.java
@@ -26,7 +26,7 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class FieldFacetExtrasTest extends AbstractAnalyticsFacetTest {
-  static String fileName = "/analytics/requestFiles/fieldFacetExtras.txt";
+  static String fileName = "fieldFacetExtras.txt";
 
   public static final int INT = 21;
   public static final int LONG = 22;
@@ -44,7 +44,7 @@ public class FieldFacetExtrasTest extends AbstractAnalyticsFacetTest {
   
   @BeforeClass
   public static void beforeClass() throws Exception {
-    initCore("solrconfig-basic.xml","schema-analytics.xml");
+    initCore("solrconfig-analytics.xml","schema-analytics.xml");
     h.update("<delete><query>*:*</query></delete>");
 
     //INT
@@ -104,7 +104,6 @@ public class FieldFacetExtrasTest extends AbstractAnalyticsFacetTest {
     setResponse(h.query(request(fileToStringArr(FieldFacetExtrasTest.class, fileName))));
   }
   
-  @SuppressWarnings("unchecked")
   @Test
   public void limitTest() throws Exception { 
 
@@ -118,7 +117,6 @@ public class FieldFacetExtrasTest extends AbstractAnalyticsFacetTest {
     assertEquals(getRawResponse(), string.size(),1);
   }
   
-  @SuppressWarnings("unchecked")
   @Test
   public void offsetTest() throws Exception { 
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetTest.java
index bf67213..59f4d88 100644
--- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetTest.java
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetTest.java
@@ -24,10 +24,11 @@ import java.util.List;
 import org.junit.Assert;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.w3c.dom.Node;
 
 
 public class FieldFacetTest extends AbstractAnalyticsFacetTest{
-  static String fileName = "/analytics/requestFiles/fieldFacets.txt";
+  static String fileName = "fieldFacets.txt";
 
   public static final int INT = 71;
   public static final int LONG = 36;
@@ -87,7 +88,7 @@ public class FieldFacetTest extends AbstractAnalyticsFacetTest{
   
   @BeforeClass
   public static void beforeClass() throws Exception {
-    initCore("solrconfig-basic.xml","schema-analytics.xml");
+    initCore("solrconfig-analytics.xml","schema-analytics.xml");
     h.update("<delete><query>*:*</query></delete>");
     
     defaults.put("int", new Integer(0));
@@ -478,46 +479,6 @@ public class FieldFacetTest extends AbstractAnalyticsFacetTest{
   
   @SuppressWarnings("unchecked")
   @Test
-  public void sumOfSquaresFacetAscTest() throws Exception {
-    //Int Date
-    Collection<Double> intDate = getDoubleList("sumOfSquares","fieldFacets", "date_dtd", "double", "int");
-    ArrayList<Double> intDateTest = calculateNumberStat(intDateTestStart, "sumOfSquares");
-    assertEquals(getRawResponse(),intDate,intDateTest);
-    //Int String
-    Collection<Double> intString = getDoubleList("sumOfSquares","fieldFacets", "string_sd", "double", "int");
-    ArrayList<Double> intStringTest = calculateNumberStat(intStringTestStart, "sumOfSquares");
-    assertEquals(getRawResponse(),intString,intStringTest);
-
-    //Long Date
-    Collection<Double> longDate = getDoubleList("sumOfSquares","fieldFacets", "date_dtd", "double", "long");
-    ArrayList<Double> longDateTest = calculateNumberStat(longDateTestStart, "sumOfSquares");
-    assertEquals(getRawResponse(),longDate,longDateTest);
-    //Long String
-    Collection<Double> longString = getDoubleList("sumOfSquares","fieldFacets", "string_sd", "double", "long");
-    ArrayList<Double> longStringTest = calculateNumberStat(longStringTestStart, "sumOfSquares");
-    assertEquals(getRawResponse(),longString,longStringTest);
-
-    //Float Date
-    Collection<Double> floatDate = getDoubleList("sumOfSquares","fieldFacets", "date_dtd", "double", "float");
-    ArrayList<Double> floatDateTest = calculateNumberStat(floatDateTestStart, "sumOfSquares");
-    assertEquals(getRawResponse(),floatDate,floatDateTest);
-    //Float String
-    Collection<Double> floatString = getDoubleList("sumOfSquares","fieldFacets", "string_sd", "double", "float");
-    ArrayList<Double> floatStringTest = calculateNumberStat(floatStringTestStart, "sumOfSquares");
-    assertEquals(getRawResponse(),floatString,floatStringTest);
-
-    //Double Date
-    Collection<Double> doubleDate = getDoubleList("sumOfSquares","fieldFacets", "date_dtd", "double", "double");
-    ArrayList<Double> doubleDateTest = calculateNumberStat(doubleDateTestStart, "sumOfSquares");
-    assertEquals(getRawResponse(),doubleDate,doubleDateTest);
-    //Double String
-    Collection<Double> doubleString = getDoubleList("sumOfSquares","fieldFacets", "string_sd", "double", "double");
-    ArrayList<Double> doubleStringTest = calculateNumberStat(doubleStringTestStart, "sumOfSquares");
-    assertEquals(getRawResponse(),doubleString,doubleStringTest);
-  }
-  
-  @SuppressWarnings("unchecked")
-  @Test
   public void stddevFacetAscTest() throws Exception { 
     //Int Date
     ArrayList<Double> intDate = getDoubleList("stddev","fieldFacets", "date_dtd", "double", "int");
@@ -1037,31 +998,33 @@ public class FieldFacetTest extends AbstractAnalyticsFacetTest{
   public void missingFacetTest() throws Exception { 
     //int MultiDate
     String xPath = "/response/lst[@name='stats']/lst[@name='missingf']/lst[@name='fieldFacets']/lst[@name='date_dtdm']/lst[@name='(MISSING)']";
-    assertNotNull(getRawResponse(), getNode(xPath));
+    Node missingNodeXPath = getNode(xPath);
+    assertNotNull(getRawResponse(), missingNodeXPath);
 
     ArrayList<Double> string = getDoubleList("missingf", "fieldFacets", "date_dtdm", "double", "mean");
-    string.remove(0);
+    //super.removeNodes(xPath, string);
     ArrayList<Double> stringTest = calculateNumberStat(multiDateTestStart, "mean");
     assertEquals(getRawResponse(), string,stringTest);
-    
+
     //Int String
     xPath = "/response/lst[@name='stats']/lst[@name='missingf']/lst[@name='fieldFacets']/lst[@name='string_sd']/lst[@name='(MISSING)']";
-    assertNotNull(getRawResponse(), getNode(xPath));
+    missingNodeXPath = getNode(xPath);
+    String missingNodeXPathStr = xPath;
+    assertNotNull(getRawResponse(), missingNodeXPath);
 
     xPath = "/response/lst[@name='stats']/lst[@name='missingf']/lst[@name='fieldFacets']/lst[@name='string_sd']/lst[@name='str0']";
     assertNull(getRawResponse(), getNode(xPath));
+
     List<Double> intString = getDoubleList("missingf", "fieldFacets", "string_sd", "double", "mean");
-    intString.remove(0);
+    //removeNodes(missingNodeXPathStr, intString);
     ArrayList<Double> intStringTest = calculateNumberStat(intStringTestStart, "mean");
     assertEquals(getRawResponse(), intString,intStringTest);
-    
+
     //Int Date
     Collection<Double> intDate = getDoubleList("missingf", "fieldFacets", "date_dtd", "double", "mean");
     ArrayList<ArrayList<Double>> intDateMissingTestStart = (ArrayList<ArrayList<Double>>) intDateTestStart.clone();
     ArrayList<Double> intDateTest = calculateNumberStat(intDateMissingTestStart, "mean");
     assertEquals(getRawResponse(),intDate,intDateTest);
-    
-    
   }
 
   private void checkStddevs(ArrayList<Double> list1, ArrayList<Double> list2) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/QueryFacetCloudTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/QueryFacetCloudTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/QueryFacetCloudTest.java
new file mode 100644
index 0000000..42b5abe
--- /dev/null
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/QueryFacetCloudTest.java
@@ -0,0 +1,159 @@
+/*
+ * 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.solr.analytics.facet;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.solr.client.solrj.request.UpdateRequest;
+import org.apache.solr.common.util.NamedList;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class QueryFacetCloudTest extends AbstractAnalyticsFacetCloudTest {
+  private static final int INT = 71;
+  private static final int LONG = 36;
+  private static final int FLOAT = 93;
+  private static final int DOUBLE = 49;
+  private static final int DATE = 12;
+  private static final int STRING = 7;
+  private static final int NUM_LOOPS = 100;
+  
+  private static ArrayList<ArrayList<Integer>> int1TestStart = new ArrayList<>();
+  private static ArrayList<ArrayList<Integer>> int2TestStart = new ArrayList<>();
+  private static ArrayList<ArrayList<Long>> longTestStart = new ArrayList<>();
+  private static ArrayList<ArrayList<Float>> floatTestStart = new ArrayList<>();
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    setupCluster();
+    
+    //INT
+    int1TestStart.add(new ArrayList<Integer>());
+    int2TestStart.add(new ArrayList<Integer>());
+    
+    //LONG
+    longTestStart.add(new ArrayList<Long>());
+    longTestStart.add(new ArrayList<Long>());
+    
+    //FLOAT
+    floatTestStart.add(new ArrayList<Float>());
+    floatTestStart.add(new ArrayList<Float>());
+    floatTestStart.add(new ArrayList<Float>());
+
+    UpdateRequest req = new UpdateRequest();
+    
+    for (int j = 0; j < NUM_LOOPS; ++j) {
+      int i = j%INT;
+      long l = j%LONG;
+      float f = j%FLOAT;
+      double d = j%DOUBLE;
+      int dt = j%DATE;
+      int s = j%STRING;
+      
+      List<String> fields = new ArrayList<>();
+      fields.add("id"); fields.add("1000"+j);
+      fields.add("int_id"); fields.add("" + i);
+      fields.add("long_ld"); fields.add("" + l);
+      fields.add("float_fd"); fields.add("" + f);
+      fields.add("double_dd"); fields.add("" + d);
+      fields.add("date_dtd"); fields.add((1000+dt) + "-01-01T23:59:59Z");
+      fields.add("string_sd"); fields.add("abc" + s);
+      req.add(fields.toArray(new String[0]));
+      
+      if (f<=50) {
+        int1TestStart.get(0).add(i);
+      }
+      if (f<=30) {
+        int2TestStart.get(0).add(i);
+      }
+      if (s == 1) {
+        longTestStart.get(0).add(l);
+      }
+      if (s == 2) {
+        longTestStart.get(1).add(l);
+      }
+      if (l>=30) {
+        floatTestStart.get(0).add(f);
+      }
+      if (d<=50) {
+        floatTestStart.get(1).add(f);
+      }
+      if (l>=20) {
+        floatTestStart.get(2).add(f);
+      }
+    }
+
+    req.commit(cluster.getSolrClient(), COLLECTIONORALIAS);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Test
+  public void queryTest() throws Exception { 
+    String[] params = new String[] {
+        "o.ir.s.sum", "sum(int_id)",
+        "o.ir.s.mean", "mean(int_id)",
+        "o.ir.s.median", "median(int_id)",
+        "o.ir.s.percentile_8", "percentile(8,int_id)",
+        "o.ir.qf", "float1",
+        "o.ir.qf.float1.q", "float_fd:[* TO 50]",
+        "o.ir.qf", "float2",
+        "o.ir.qf.float2.q", "float_fd:[* TO 30]",
+
+        "o.lr.s.sum", "sum(long_ld)",
+        "o.lr.s.mean", "mean(long_ld)",
+        "o.lr.s.median", "median(long_ld)",
+        "o.lr.s.percentile_8", "percentile(8,long_ld)",
+        "o.lr.qf", "string",
+        "o.lr.qf.string.q", "string_sd:abc1",
+        "o.lr.qf.string.q", "string_sd:abc2",
+        
+        "o.fr.s.sum", "sum(float_fd)",
+        "o.fr.s.mean", "mean(float_fd)",
+        "o.fr.s.median", "median(float_fd)",
+        "o.fr.s.percentile_8", "percentile(8,float_fd)",
+        "o.fr.qf", "lad",
+        "o.fr.qf.lad.q", "long_ld:[20 TO *]",
+        "o.fr.qf.lad.q", "long_ld:[30 TO *]",
+        "o.fr.qf.lad.q", "double_dd:[* TO 50]"
+    };
+    
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int One
+    ArrayList<Double> int1 = getValueList(response, "ir", "queryFacets", "float1", "sum", false);
+    ArrayList<Double> int1Test = calculateNumberStat(int1TestStart, "sum");
+    assertEquals(responseStr, int1, int1Test);
+    //Int Two
+    ArrayList<Integer> int2 = getValueList(response, "ir", "queryFacets", "float2", "percentile_8", false);
+    ArrayList<Integer> int2Test = (ArrayList<Integer>)calculateStat(int2TestStart, "perc_8");
+    assertEquals(responseStr, int2, int2Test);
+
+    //Long
+    ArrayList<Double> long1 = getValueList(response, "lr", "queryFacets", "string", "median", false);
+    ArrayList<Double> long1Test = calculateNumberStat(longTestStart, "median");
+    assertEquals(responseStr,long1,long1Test);
+
+    //Float
+    ArrayList<Double> float1 = getValueList(response, "fr", "queryFacets", "lad", "mean", false);
+    ArrayList<Double> float1Test = calculateNumberStat(floatTestStart, "mean");
+    assertEquals(responseStr, float1, float1Test);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/QueryFacetTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/QueryFacetTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/QueryFacetTest.java
index f62a82b..e892dc9 100644
--- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/QueryFacetTest.java
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/QueryFacetTest.java
@@ -23,7 +23,7 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class QueryFacetTest extends AbstractAnalyticsFacetTest {
-  static String fileName = "/analytics/requestFiles/queryFacets.txt";
+  static String fileName = "queryFacets.txt";
 
   public final int INT = 71;
   public final int LONG = 36;
@@ -35,7 +35,7 @@ public class QueryFacetTest extends AbstractAnalyticsFacetTest {
 
   @BeforeClass
   public static void beforeClass() throws Exception {
-    initCore("solrconfig-basic.xml","schema-analytics.xml");
+    initCore("solrconfig-analytics.xml","schema-analytics.xml");
   }
 
   @SuppressWarnings("unchecked")
@@ -81,13 +81,13 @@ public class QueryFacetTest extends AbstractAnalyticsFacetTest {
       if (new Integer(s).toString().charAt(0)=='2') {
         longTestStart.get(1).add(l);
       }
-      if (l>=20) {
+      if (l>=30) {
         floatTestStart.get(0).add(f);
       }
-      if (l>=30) {
+      if (d<=50) {
         floatTestStart.get(1).add(f);
       }
-      if (d<=50) {
+      if (l>=20) {
         floatTestStart.get(2).add(f);
       }
       

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/RangeFacetCloudTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/RangeFacetCloudTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/RangeFacetCloudTest.java
new file mode 100644
index 0000000..880324d
--- /dev/null
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/RangeFacetCloudTest.java
@@ -0,0 +1,588 @@
+/*
+ * 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.solr.analytics.facet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.solr.client.solrj.request.UpdateRequest;
+import org.apache.solr.common.util.NamedList;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+public class RangeFacetCloudTest extends AbstractAnalyticsFacetCloudTest{
+  public static final int INT = 71;
+  public static final int LONG = 36;
+  public static final int FLOAT = 93;
+  public static final int DOUBLE = 48;
+  public static final int DATE = 52;
+  public static final int STRING = 28;
+  public static final int NUM_LOOPS = 100;
+  
+  //INT
+  static ArrayList<ArrayList<Integer>> intLongTestStart; 
+  static ArrayList<ArrayList<Integer>> intDoubleTestStart; 
+  static ArrayList<ArrayList<Integer>> intDateTestStart; 
+  
+  //FLOAT
+  static ArrayList<ArrayList<Float>> floatLongTestStart; 
+  static ArrayList<ArrayList<Float>> floatDoubleTestStart; 
+  static ArrayList<ArrayList<Float>> floatDateTestStart; 
+  
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    setupCluster();
+    
+    //INT
+    intLongTestStart = new ArrayList<>();
+    intDoubleTestStart = new ArrayList<>();
+    intDateTestStart = new ArrayList<>();
+    
+    //FLOAT
+    floatLongTestStart = new ArrayList<>();
+    floatDoubleTestStart = new ArrayList<>();
+    floatDateTestStart = new ArrayList<>();
+
+    UpdateRequest req = new UpdateRequest();
+    
+    for (int j = 0; j < NUM_LOOPS; ++j) {
+      int i = j%INT;
+      long l = j%LONG;
+      float f = j%FLOAT;
+      double d = j%DOUBLE;
+      int dt = j%DATE;
+      int s = j%STRING;
+      
+      List<String> fields = new ArrayList<>();
+      fields.add("id"); fields.add("1000"+j);
+      fields.add("int_id"); fields.add("" + i);
+      fields.add("long_ld"); fields.add("" + l);
+      fields.add("float_fd"); fields.add("" + f);
+      fields.add("double_dd"); fields.add("" + d);
+      fields.add("date_dtd"); fields.add((1000+dt) + "-01-01T23:59:59Z");
+      fields.add("string_sd"); fields.add("abc" + s);
+      req.add(fields.toArray(new String[0]));
+      
+      //Longs
+      if (j-LONG<0) {
+        ArrayList<Integer> list1 = new ArrayList<>();
+        list1.add(i);
+        intLongTestStart.add(list1);
+        ArrayList<Float> list2 = new ArrayList<>();
+        list2.add(f);
+        floatLongTestStart.add(list2);
+      } else {
+        intLongTestStart.get((int)l).add(i);
+        floatLongTestStart.get((int)l).add(f);
+      }
+      //Doubles
+      if (j-DOUBLE<0) {
+        ArrayList<Integer> list1 = new ArrayList<>();
+        list1.add(i);
+        intDoubleTestStart.add(list1);
+        ArrayList<Float> list2 = new ArrayList<>();
+        list2.add(f);
+        floatDoubleTestStart.add(list2);
+      } else {
+        intDoubleTestStart.get((int)d).add(i);
+        floatDoubleTestStart.get((int)d).add(f);
+      }
+      //Dates
+      if (j-DATE<0) {
+        ArrayList<Integer> list1 = new ArrayList<>();
+        list1.add(i);
+        intDateTestStart.add(list1);
+        ArrayList<Float> list2 = new ArrayList<>();
+        list2.add(f);
+        floatDateTestStart.add(list2);
+      } else {
+        intDateTestStart.get(dt).add(i);
+        floatDateTestStart.get(dt).add(f);
+      }
+    }
+
+    req.commit(cluster.getSolrClient(), COLLECTIONORALIAS);
+  }
+  
+  @SuppressWarnings("unchecked")
+  @Test
+  public void rangeTest() throws Exception {
+    String[] params = new String[] {
+        "o.ri.s.sum", "sum(int_id)",
+        "o.ri.s.mean", "mean(int_id)",
+        "o.ri.s.median", "median(int_id)",
+        "o.ri.s.count", "count(int_id)",
+        "o.ri.rf", "long_ld",
+        "o.ri.rf.long_ld.st", "5",
+        "o.ri.rf.long_ld.e", "30",
+        "o.ri.rf.long_ld.g", "5",
+        "o.ri.rf.long_ld.ib", "lower",
+        "o.ri.rf.long_ld.or", "all",
+        "o.ri.rf", "double_dd",
+        "o.ri.rf.double_dd.st", "3",
+        "o.ri.rf.double_dd.e", "39",
+        "o.ri.rf.double_dd.g", "7",
+        "o.ri.rf.double_dd.ib", "upper",
+        "o.ri.rf.double_dd.ib", "outer",
+        "o.ri.rf.double_dd.or", "all",
+        "o.ri.rf", "date_dtd",
+        "o.ri.rf.date_dtd.st", "1007-01-01T23:59:59Z",
+        "o.ri.rf.date_dtd.e", "1044-01-01T23:59:59Z",
+        "o.ri.rf.date_dtd.g", "+7YEARS",
+        "o.ri.rf.date_dtd.ib", "lower",
+        "o.ri.rf.date_dtd.ib", "edge",
+        "o.ri.rf.date_dtd.ib", "outer",
+        "o.ri.rf.date_dtd.or", "all",
+
+        "o.rf.s.sum", "sum(float_fd)",
+        "o.rf.s.mean", "mean(float_fd)",
+        "o.rf.s.median", "median(float_fd)",
+        "o.rf.s.count", "count(float_fd)",
+        "o.rf.rf", "long_ld",
+        "o.rf.rf.long_ld.st", "0",
+        "o.rf.rf.long_ld.e", "29",
+        "o.rf.rf.long_ld.g", "4",
+        "o.rf.rf.long_ld.ib", "all",
+        "o.rf.rf.long_ld.or", "all",
+        "o.rf.rf", "double_dd",
+        "o.rf.rf.double_dd.st", "4",
+        "o.rf.rf.double_dd.e", "47",
+        "o.rf.rf.double_dd.g", "11",
+        "o.rf.rf.double_dd.ib", "edge",
+        "o.rf.rf.double_dd.or", "all",
+        "o.rf.rf", "date_dtd",
+        "o.rf.rf.date_dtd.st", "1004-01-01T23:59:59Z",
+        "o.rf.rf.date_dtd.e", "1046-01-01T23:59:59Z",
+        "o.rf.rf.date_dtd.g", "+5YEARS",
+        "o.rf.rf.date_dtd.ib", "upper",
+        "o.rf.rf.date_dtd.ib", "edge",
+        "o.rf.rf.date_dtd.or", "all"
+    };
+    
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int Long
+    ArrayList<Long> intLong = getValueList(response, "ri", "rangeFacets", "long_ld", "count", false);
+    ArrayList<Long> intLongTest = calculateStat(transformLists(intLongTestStart, 5, 30, 5
+                                                        , false, true, false, false, false), "count");
+    assertEquals(responseStr, intLong,intLongTest);
+    //Int Double
+    ArrayList<Double> intDouble = getValueList(response, "ri", "rangeFacets", "double_dd", "mean", false);
+    ArrayList<Double> intDoubleTest = calculateNumberStat(transformLists(intDoubleTestStart, 3, 39, 7
+                                                          , false, false, true, false, true), "mean");
+    assertEquals(responseStr, intDouble,intDoubleTest);
+    //Int Date
+    ArrayList<Long> intDate = getValueList(response, "ri", "rangeFacets", "date_dtd", "count", false);
+    ArrayList<Long> intDateTest = (ArrayList<Long>)calculateStat(transformLists(intDateTestStart, 7, 44, 7
+                                                      , false, true, false, true, true), "count");
+    assertEquals(responseStr, intDate,intDateTest);
+    
+    //Float Long
+    ArrayList<Double> floatLong = getValueList(response, "rf", "rangeFacets", "long_ld", "median", false);
+    ArrayList<Double> floatLongTest = calculateNumberStat(transformLists(floatLongTestStart, 0, 29, 4
+                                                          , false, true, true, true, true), "median");
+    assertEquals(responseStr, floatLong,floatLongTest);
+    //Float Double
+    ArrayList<Long> floatDouble = getValueList(response, "rf", "rangeFacets", "double_dd", "count", false);
+    ArrayList<Long> floatDoubleTest = (ArrayList<Long>)calculateStat(transformLists(floatDoubleTestStart, 4, 47, 11
+                                                                     , false, false, false, true, false), "count");
+    assertEquals(responseStr, floatDouble,floatDoubleTest);
+  }
+  
+
+  @SuppressWarnings("unchecked")
+  @Test
+  public void hardendRangeTest() throws Exception {
+    String[] params = new String[] {
+        "o.hi.s.sum", "sum(int_id)",
+        "o.hi.s.mean", "mean(int_id)",
+        "o.hi.s.median", "median(int_id)",
+        "o.hi.s.count", "count(int_id)",
+        "o.hi.rf", "long_ld",
+        "o.hi.rf.long_ld.st", "5",
+        "o.hi.rf.long_ld.e", "30",
+        "o.hi.rf.long_ld.g", "5",
+        "o.hi.rf.long_ld.he", "true",
+        "o.hi.rf.long_ld.ib", "lower",
+        "o.hi.rf.long_ld.or", "all",
+        "o.hi.rf", "double_dd",
+        "o.hi.rf.double_dd.st", "3",
+        "o.hi.rf.double_dd.e", "39",
+        "o.hi.rf.double_dd.g", "7",
+        "o.hi.rf.double_dd.he", "true",
+        "o.hi.rf.double_dd.ib", "upper",
+        "o.hi.rf.double_dd.ib", "outer",
+        "o.hi.rf.double_dd.or", "all",
+        "o.hi.rf", "date_dtd",
+        "o.hi.rf.date_dtd.st", "1007-01-01T23:59:59Z",
+        "o.hi.rf.date_dtd.e", "1044-01-01T23:59:59Z",
+        "o.hi.rf.date_dtd.g", "+7YEARS",
+        "o.hi.rf.date_dtd.he", "true",
+        "o.hi.rf.date_dtd.ib", "lower",
+        "o.hi.rf.date_dtd.ib", "edge",
+        "o.hi.rf.date_dtd.ib", "outer",
+        "o.hi.rf.date_dtd.or", "all",
+
+        "o.hf.s.sum", "sum(float_fd)",
+        "o.hf.s.mean", "mean(float_fd)",
+        "o.hf.s.median", "median(float_fd)",
+        "o.hf.s.count", "count(float_fd)",
+        "o.hf.rf", "long_ld",
+        "o.hf.rf.long_ld.st", "0",
+        "o.hf.rf.long_ld.e", "29",
+        "o.hf.rf.long_ld.g", "4",
+        "o.hf.rf.long_ld.he", "true",
+        "o.hf.rf.long_ld.ib", "all",
+        "o.hf.rf.long_ld.or", "all",
+        "o.hf.rf", "double_dd",
+        "o.hf.rf.double_dd.st", "4",
+        "o.hf.rf.double_dd.e", "47",
+        "o.hf.rf.double_dd.g", "11",
+        "o.hf.rf.double_dd.he", "true",
+        "o.hf.rf.double_dd.ib", "edge",
+        "o.hf.rf.double_dd.or", "all",
+        "o.hf.rf", "date_dtd",
+        "o.hf.rf.date_dtd.st", "1004-01-01T23:59:59Z",
+        "o.hf.rf.date_dtd.e", "1046-01-01T23:59:59Z",
+        "o.hf.rf.date_dtd.g", "+5YEARS",
+        "o.hf.rf.date_dtd.he", "true",
+        "o.hf.rf.date_dtd.ib", "upper",
+        "o.hf.rf.date_dtd.ib", "edge",
+        "o.hf.rf.date_dtd.or", "all"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int Long
+    ArrayList<Double> intLong = getValueList(response, "hi", "rangeFacets", "long_ld", "sum", false);
+    ArrayList<Double> intLongTest = calculateNumberStat(transformLists(intLongTestStart, 5, 30, 5
+                                                        , true, true, false, false, false), "sum");
+    assertEquals(responseStr, intLong,intLongTest);
+    //Int Double
+    ArrayList<Double> intDouble = getValueList(response, "hi", "rangeFacets", "double_dd", "mean", false);
+    ArrayList<Double> intDoubleTest = calculateNumberStat(transformLists(intDoubleTestStart, 3, 39, 7
+                                                          , true, false, true, false, true), "mean");
+    assertEquals(responseStr, intDouble,intDoubleTest);
+    //Int Date
+    ArrayList<Long> intDate = getValueList(response, "hi", "rangeFacets", "date_dtd", "count", false);
+    ArrayList<Long> intDateTest = (ArrayList<Long>)calculateStat(transformLists(intDateTestStart, 7, 44, 7
+                                                      , true, true, false, true, true), "count");
+    assertEquals(responseStr, intDate,intDateTest);
+    
+    //Float Long
+    ArrayList<Double> floatLong = getValueList(response, "hf", "rangeFacets", "long_ld", "median", false);
+    ArrayList<Double> floatLongTest = calculateNumberStat(transformLists(floatLongTestStart, 0, 29, 4
+                                                          , true, true, true, true, true), "median");
+    assertEquals(responseStr, floatLong,floatLongTest);
+    //Float Double
+    ArrayList<Long> floatDouble = getValueList(response, "hf", "rangeFacets", "double_dd", "count", false);
+    ArrayList<Long> floatDoubleTest = (ArrayList<Long>)calculateStat(transformLists(floatDoubleTestStart, 4, 47, 11
+                                                                     , true, false, false, true, false), "count");
+    assertEquals(responseStr, floatDouble,floatDoubleTest);
+  }
+  
+  @SuppressWarnings("unchecked")
+  @Test
+  public void multiGapTest() throws Exception {
+    String[] params = new String[] {
+        "o.mi.s.sum", "sum(int_id)",
+        "o.mi.s.mean", "mean(int_id)",
+        "o.mi.s.median", "median(int_id)",
+        "o.mi.s.count", "count(int_id)",
+        "o.mi.rf", "long_ld",
+        "o.mi.rf.long_ld.st", "5",
+        "o.mi.rf.long_ld.e", "30",
+        "o.mi.rf.long_ld.g", "4,2,6,3",
+        "o.mi.rf.long_ld.ib", "lower",
+        "o.mi.rf.long_ld.or", "all",
+        "o.mi.rf", "double_dd",
+        "o.mi.rf.double_dd.st", "3",
+        "o.mi.rf.double_dd.e", "39",
+        "o.mi.rf.double_dd.g", "3,1,7",
+        "o.mi.rf.double_dd.ib", "upper",
+        "o.mi.rf.double_dd.ib", "outer",
+        "o.mi.rf.double_dd.or", "all",
+        "o.mi.rf", "date_dtd",
+        "o.mi.rf.date_dtd.st", "1007-01-01T23:59:59Z",
+        "o.mi.rf.date_dtd.e", "1044-01-01T23:59:59Z",
+        "o.mi.rf.date_dtd.g", "+2YEARS,+7YEARS",
+        "o.mi.rf.date_dtd.ib", "lower",
+        "o.mi.rf.date_dtd.ib", "edge",
+        "o.mi.rf.date_dtd.ib", "outer",
+        "o.mi.rf.date_dtd.or", "all",
+
+        "o.mf.s.sum", "sum(float_fd)",
+        "o.mf.s.mean", "mean(float_fd)",
+        "o.mf.s.median", "median(float_fd)",
+        "o.mf.s.count", "count(float_fd)",
+        "o.mf.rf", "long_ld",
+        "o.mf.rf.long_ld.st", "0",
+        "o.mf.rf.long_ld.e", "29",
+        "o.mf.rf.long_ld.g", "1,4",
+        "o.mf.rf.long_ld.ib", "all",
+        "o.mf.rf.long_ld.or", "all",
+        "o.mf.rf", "double_dd",
+        "o.mf.rf.double_dd.st", "4",
+        "o.mf.rf.double_dd.e", "47",
+        "o.mf.rf.double_dd.g", "2,3,11",
+        "o.mf.rf.double_dd.ib", "edge",
+        "o.mf.rf.double_dd.or", "all",
+        "o.mf.rf", "date_dtd",
+        "o.mf.rf.date_dtd.st", "1004-01-01T23:59:59Z",
+        "o.mf.rf.date_dtd.e", "1046-01-01T23:59:59Z",
+        "o.mf.rf.date_dtd.g", "+4YEARS,+5YEARS",
+        "o.mf.rf.date_dtd.ib", "upper",
+        "o.mf.rf.date_dtd.ib", "edge",
+        "o.mf.rf.date_dtd.or", "all"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int Long
+    ArrayList<Double> intLong = getValueList(response, "mi", "rangeFacets", "long_ld", "sum", false);
+    ArrayList<Double> intLongTest = calculateNumberStat(transformLists(intLongTestStart, 5, 30, "4,2,6,3"
+                                                        , false, true, false, false, false), "sum");
+    assertEquals(responseStr, intLong,intLongTest);
+    //Int Double
+    ArrayList<Double> intDouble = getValueList(response, "mi", "rangeFacets", "double_dd", "mean", false);
+    ArrayList<Double> intDoubleTest = calculateNumberStat(transformLists(intDoubleTestStart, 3, 39, "3,1,7"
+                                                          , false, false, true, false, true), "mean");
+    assertEquals(responseStr, intDouble,intDoubleTest);
+    //Int Date
+    ArrayList<Long> intDate = getValueList(response, "mi", "rangeFacets", "date_dtd", "count", false);
+    ArrayList<Long> intDateTest = (ArrayList<Long>)calculateStat(transformLists(intDateTestStart, 7, 44, "2,7"
+                                                      , false, true, false, true, true), "count");
+    assertEquals(responseStr, intDate,intDateTest);
+    
+    //Float Long
+    ArrayList<Double> floatLong = getValueList(response, "mf", "rangeFacets", "long_ld", "median", false);
+    ArrayList<Double> floatLongTest = calculateNumberStat(transformLists(floatLongTestStart, 0, 29, "1,4"
+                                                          , false, true, true, true, true), "median");;
+    assertEquals(responseStr, floatLong,floatLongTest);
+    //Float Double
+    ArrayList<Long> floatDouble = getValueList(response, "mf", "rangeFacets", "double_dd", "count", false);
+    ArrayList<Long> floatDoubleTest = (ArrayList<Long>)calculateStat(transformLists(floatDoubleTestStart, 4, 47, "2,3,11"
+                                                          , false, false, false, true, false), "count");
+    assertEquals(responseStr, floatDouble,floatDoubleTest);
+  }
+  
+  private <T> ArrayList<ArrayList<T>> transformLists(ArrayList<ArrayList<T>> listsStart, int start, int end, int gap
+      , boolean hardend, boolean incLow, boolean incUp, boolean incEdge, boolean incOut) {
+    int off = (end-start)%gap;
+    if (!hardend && off>0) {
+      end+=gap-off;
+    }
+
+    ArrayList<ArrayList<T>> lists = new ArrayList<>();
+    ArrayList<T> between = new ArrayList<>();
+    if (incLow && incUp) {
+      for (int i = start; i<end && i<listsStart.size(); i+=gap) {
+        ArrayList<T> list = new ArrayList<>();
+        for (int j = i; j<=i+gap && j<=end && j<listsStart.size(); j++) {
+          list.addAll(listsStart.get(j));
+        }
+        lists.add(list);
+      }
+      for (int i = start; i<listsStart.size() && i<=end; i++) {
+        between.addAll(listsStart.get(i));
+      }
+    } else if (incLow && !incUp) {
+      for (int i = start; i<end && i<listsStart.size(); i+=gap) {
+        ArrayList<T> list = new ArrayList<>();
+        for (int j = i; j<i+gap && j<end && j<listsStart.size(); j++) {
+          list.addAll(listsStart.get(j));
+        }
+        lists.add(list);
+      }
+      for (int i = start; i<listsStart.size() && i<end; i++) {
+        between.addAll(listsStart.get(i));
+      }
+    } else if (!incLow && incUp) {
+      for (int i = start; i<end && i<listsStart.size(); i+=gap) {
+        ArrayList<T> list = new ArrayList<>();
+        for (int j = i+1; j<=i+gap && j<=end && j<listsStart.size(); j++) {
+          list.addAll(listsStart.get(j));
+        }
+        lists.add(list);
+      }
+      for (int i = start+1; i<listsStart.size() && i<=end; i++) {
+        between.addAll(listsStart.get(i));
+      }
+    } else {
+      for (int i = start; i<end && i<listsStart.size(); i+=gap) {
+        ArrayList<T> list = new ArrayList<>();
+        for (int j = i+1; j<i+gap && j<end && j<listsStart.size(); j++) {
+          list.addAll(listsStart.get(j));
+        }
+        lists.add(list);
+      }
+      for (int i = start+1; i<listsStart.size() && i<end; i++) {
+        between.addAll(listsStart.get(i));
+      }
+    }
+    
+    if (incEdge && !incLow && start>=0) {
+      lists.get(0).addAll(listsStart.get(start));
+      between.addAll(listsStart.get(start));
+    }
+    if (incEdge && !incUp && end<listsStart.size()) {
+      lists.get(lists.size()-1).addAll(listsStart.get(end));
+      between.addAll(listsStart.get(end));
+    }
+    ArrayList<T> before = new ArrayList<>();
+    ArrayList<T> after = new ArrayList<>();
+    if (incOut || !(incLow||incEdge)) {
+      for (int i = 0; i<=start; i++) {
+        before.addAll(listsStart.get(i));
+      }
+    } else {
+      for (int i = 0; i<start; i++) {
+        before.addAll(listsStart.get(i));
+      }
+    }
+    if (incOut || !(incUp||incEdge)) {
+      for (int i = end; i<listsStart.size(); i++) {
+        after.addAll(listsStart.get(i));
+      }
+    } 
+    else {
+      for (int i = end+1; i<listsStart.size(); i++) {
+        after.addAll(listsStart.get(i));
+      }
+    }
+    lists.add(before);
+    lists.add(after);
+    lists.add(between);
+    return lists;
+  }
+  
+  private <T> ArrayList<ArrayList<T>> transformLists(ArrayList<ArrayList<T>> listsStart, int start, int end, String gapString
+      , boolean hardend, boolean incLow, boolean incUp, boolean incEdge, boolean incOut) {
+    String[] stringGaps = gapString.split(",");
+    int[] gaps = new int[stringGaps.length];
+    for (int i = 0; i<gaps.length; i++) {
+      gaps[i] = Integer.parseInt(stringGaps[i]);
+    }
+    int bigGap = 0;
+    int last = gaps[gaps.length-1];
+    for (int i = 0; i<gaps.length-1; i++) {
+      bigGap += gaps[i];
+    }
+    int off = (end-start-bigGap)%last;
+    if (!hardend && off>0) {
+      end+=last-off;
+    }
+    
+    ArrayList<ArrayList<T>> lists = new ArrayList<>();
+    ArrayList<T> between = new ArrayList<>();
+    int gap = 0;
+    int gapCounter = 0;
+    if (incLow && incUp) {
+      for (int i = start; i<end && i<listsStart.size(); i+=gap) {
+        if (gapCounter<gaps.length) {
+          gap = gaps[gapCounter++];
+        }
+        ArrayList<T> list = new ArrayList<>();
+        for (int j = i; j<=i+gap && j<=end && j<listsStart.size(); j++) {
+          list.addAll(listsStart.get(j));
+        }
+        lists.add(list);
+      }
+      for (int i = start; i<listsStart.size() && i<=end; i++) {
+        between.addAll(listsStart.get(i));
+      }
+    } else if (incLow && !incUp) {
+      for (int i = start; i<end && i<listsStart.size(); i+=gap) {
+        if (gapCounter<gaps.length) {
+          gap = gaps[gapCounter++];
+        }
+        ArrayList<T> list = new ArrayList<>();
+        for (int j = i; j<i+gap && j<end && j<listsStart.size(); j++) {
+          list.addAll(listsStart.get(j));
+        }
+        lists.add(list);
+      }
+      for (int i = start; i<listsStart.size() && i<end; i++) {
+        between.addAll(listsStart.get(i));
+      }
+    } else if (!incLow && incUp) {
+      for (int i = start; i<end && i<listsStart.size(); i+=gap) {
+        if (gapCounter<gaps.length) {
+          gap = gaps[gapCounter++];
+        }
+        ArrayList<T> list = new ArrayList<>();
+        for (int j = i+1; j<=i+gap && j<=end && j<listsStart.size(); j++) {
+          list.addAll(listsStart.get(j));
+        }
+        lists.add(list);
+      }
+      for (int i = start+1; i<listsStart.size() && i<=end; i++) {
+        between.addAll(listsStart.get(i));
+      }
+    } else {
+      for (int i = start; i<end && i<listsStart.size(); i+=gap) {
+        if (gapCounter<gaps.length) {
+          gap = gaps[gapCounter++];
+        }
+        ArrayList<T> list = new ArrayList<>();
+        for (int j = i+1; j<i+gap && j<end && j<listsStart.size(); j++) {
+          list.addAll(listsStart.get(j));
+        }
+        lists.add(list);
+      }
+      for (int i = start+1; i<listsStart.size() && i<end; i++) {
+        between.addAll(listsStart.get(i));
+      }
+    }
+    
+    if (incEdge && !incLow && start>=0) {
+      lists.get(0).addAll(listsStart.get(start));
+      between.addAll(listsStart.get(start));
+    }
+    if (incEdge && !incUp && end<listsStart.size()) {
+      lists.get(lists.size()-1).addAll(listsStart.get(end));
+      between.addAll(listsStart.get(end));
+    }
+    ArrayList<T> before = new ArrayList<>();
+    ArrayList<T> after = new ArrayList<>();
+    if (incOut || !(incLow||incEdge)) {
+      for (int i = 0; i<=start; i++) {
+        before.addAll(listsStart.get(i));
+      }
+    } else {
+      for (int i = 0; i<start; i++) {
+        before.addAll(listsStart.get(i));
+      }
+    }
+    if (incOut || !(incUp||incEdge)) {
+      for (int i = end; i<listsStart.size(); i++) {
+        after.addAll(listsStart.get(i));
+      }
+    } 
+    else {
+      for (int i = end+1; i<listsStart.size(); i++) {
+        after.addAll(listsStart.get(i));
+      }
+    }
+    lists.add(before);
+    lists.add(after);
+    lists.add(between);
+    return lists;
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/RangeFacetTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/RangeFacetTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/RangeFacetTest.java
index cda8202..24a7ea2 100644
--- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/RangeFacetTest.java
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/RangeFacetTest.java
@@ -24,7 +24,7 @@ import org.junit.Test;
 
 
 public class RangeFacetTest extends AbstractAnalyticsFacetTest {
-  static String fileName = "/analytics/requestFiles/rangeFacets.txt";
+  static String fileName = "rangeFacets.txt";
 
   public static final int INT = 71;
   public static final int LONG = 36;
@@ -46,7 +46,7 @@ public class RangeFacetTest extends AbstractAnalyticsFacetTest {
   
   @BeforeClass
   public static void beforeClass() throws Exception {
-    initCore("solrconfig-basic.xml","schema-analytics.xml");
+    initCore("solrconfig-analytics.xml","schema-analytics.xml");
     h.update("<delete><query>*:*</query></delete>");
     
     //INT
@@ -145,11 +145,6 @@ public class RangeFacetTest extends AbstractAnalyticsFacetTest {
     ArrayList<Long> floatDoubleTest = (ArrayList<Long>)calculateStat(transformLists(floatDoubleTestStart, 4, 47, 11
                                                                      , false, false, false, true, false), "count");
     assertEquals(getRawResponse(), floatDouble,floatDoubleTest);
-    //Float Date                      
-    ArrayList<Double> floatDate = getDoubleList("rf", "rangeFacets", "date_dtd", "double", "sumOfSquares");
-    ArrayList<Double> floatDateTest = calculateNumberStat(transformLists(floatDateTestStart, 4, 46, 5
-                                                          , false, false, true, true, false), "sumOfSquares");
-    assertEquals(getRawResponse(), floatDate,floatDateTest);
   }
   
 
@@ -182,11 +177,6 @@ public class RangeFacetTest extends AbstractAnalyticsFacetTest {
     ArrayList<Long> floatDoubleTest = (ArrayList<Long>)calculateStat(transformLists(floatDoubleTestStart, 4, 47, 11
                                                                      , true, false, false, true, false), "count");
     assertEquals(getRawResponse(), floatDouble,floatDoubleTest);
-    //Float Date                      
-    ArrayList<Double> floatDate = getDoubleList("hf", "rangeFacets", "date_dtd", "double", "sumOfSquares");
-    ArrayList<Double> floatDateTest = calculateNumberStat(transformLists(floatDateTestStart, 4, 46, 5
-                                                          , true, false, true, true, false), "sumOfSquares");
-    assertEquals(getRawResponse(), floatDate,floatDateTest);
   }
   
   @SuppressWarnings("unchecked")
@@ -218,11 +208,6 @@ public class RangeFacetTest extends AbstractAnalyticsFacetTest {
     ArrayList<Long> floatDoubleTest = (ArrayList<Long>)calculateStat(transformLists(floatDoubleTestStart, 4, 47, "2,3,11"
                                                           , false, false, false, true, false), "count");
     assertEquals(getRawResponse(), floatDouble,floatDoubleTest);
-    //Float Date                      
-    ArrayList<Double> floatDate = getDoubleList("mf", "rangeFacets", "date_dtd", "double", "sumOfSquares");
-    ArrayList<Double> floatDateTest = calculateNumberStat(transformLists(floatDateTestStart, 4, 46, "4,5"
-                                                          , false, false, true, true, false), "sumOfSquares");
-    assertEquals(getRawResponse(), floatDate,floatDateTest);
   }
   
   private <T> ArrayList<ArrayList<T>> transformLists(ArrayList<ArrayList<T>> listsStart, int start, int end, int gap
@@ -309,15 +294,9 @@ public class RangeFacetTest extends AbstractAnalyticsFacetTest {
         after.addAll(listsStart.get(i));
       }
     }
-    if (before.size()>0) {
-      lists.add(before);
-    }
-    if (after.size()>0) {
-      lists.add(after);
-    }
-    if (between.size()>0) {
-      lists.add(between);
-    }
+    lists.add(before);
+    lists.add(after);
+    lists.add(between);
     return lists;
   }
   
@@ -429,15 +408,9 @@ public class RangeFacetTest extends AbstractAnalyticsFacetTest {
         after.addAll(listsStart.get(i));
       }
     }
-    if (before.size()>0) {
-      lists.add(before);
-    }
-    if (after.size()>0) {
-      lists.add(after);
-    }
-    if (between.size()>0) {
-      lists.add(between);
-    }
+    lists.add(before);
+    lists.add(after);
+    lists.add(between);
     return lists;
   }
   

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/util/valuesource/FunctionTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/util/valuesource/FunctionTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/util/valuesource/FunctionTest.java
deleted file mode 100644
index 5981cc0..0000000
--- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/util/valuesource/FunctionTest.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-
-import org.apache.solr.analytics.AbstractAnalyticsStatsTest;
-import org.apache.solr.analytics.facet.AbstractAnalyticsFacetTest;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-public class FunctionTest extends AbstractAnalyticsStatsTest {
-  static String fileName = "/analytics/requestFiles/functions.txt";
-
-  static public final int INT = 71;
-  static public final int LONG = 36;
-  static public final int FLOAT = 93;
-  static public final int DOUBLE = 49;
-  static public final int DATE = 12;
-  static public final int STRING = 28;
-  static public final int NUM_LOOPS = 100;
-
-  @BeforeClass
-  public static void beforeClass() throws Exception {
-    initCore("solrconfig-basic.xml","schema-analytics.xml");
-    h.update("<delete><query>*:*</query></delete>");
-    
-    for (int j = 0; j < NUM_LOOPS; ++j) {
-      int i = j%INT+1;
-      long l = j%LONG+1;
-      float f = j%FLOAT+1;
-      double d = j%DOUBLE+1;
-      double d0 = j%DOUBLE;
-      String dt = (1800+j%DATE) + "-06-30T23:59:59Z";
-      String s = "str" + (j%STRING);
-
-      double add_if = (double)i+f;
-      double add_ldf = (double)l+d+f;
-      double mult_if = (double)i*f;
-      double mult_ldf = (double)l*d*f;
-      double div_if = (double)i/f;
-      double div_ld = (double)l/d;
-      double pow_if = Math.pow(i,f);
-      double pow_ld = Math.pow(l,d);
-      double neg_i = (double)i*-1;
-      double neg_l = (double)l*-1;
-      String dm_2y = (1802+j%DATE) + "-06-30T23:59:59Z";
-      String dm_2m = (1800+j%DATE) + "-08-30T23:59:59Z";
-      String concat_first = "this is the first"+s;
-      String concat_second = "this is the second"+s;
-      String rev = new StringBuilder(s).reverse().toString();
-      
-      assertU(adoc(AbstractAnalyticsFacetTest.filter("id", "1000" + j, "int_id", "" + i, "long_ld", "" + l, "float_fd", "" + f, 
-            "double_dd", "" + d,  "date_dtd", dt, "string_sd", s,
-            "add_if_dd", ""+add_if, "add_ldf_dd", ""+add_ldf, "mult_if_dd", ""+mult_if, "mult_ldf_dd", ""+mult_ldf,
-            "div_if_dd", ""+div_if, "div_ld_dd", ""+div_ld, "pow_if_dd", ""+pow_if, "pow_ld_dd", ""+pow_ld,
-            "neg_i_dd", ""+neg_i, "neg_l_dd", ""+neg_l, "const_8_dd", "8", "const_10_dd", "10", "dm_2y_dtd", dm_2y, "dm_2m_dtd", dm_2m,
-            "const_00_dtd", "1800-06-30T23:59:59Z", "const_04_dtd", "1804-06-30T23:59:59Z", "const_first_sd", "this is the first", "const_second_sd", "this is the second",
-            "concat_first_sd", concat_first, "concat_second_sd", concat_second, "rev_sd", rev, "miss_dd", ""+d0 )));
-      
-      
-      if (usually()) {
-        assertU(commit()); // to have several segments
-      }
-    }
-    
-    assertU(commit()); 
-    
-    setResponse(h.query(request(fileToStringArr(FunctionTest.class, fileName))));
-  }
-      
-  @Test
-  public void addTest() throws Exception { 
-    double result = (Double)getStatResult("ar", "sum", VAL_TYPE.DOUBLE);
-    double calculated = (Double)getStatResult("ar", "sumc", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(), result, calculated, 0.0);
-    // TODO checfk why asserted 2times
-    assertEquals(getRawResponse(), result, calculated, 0.0);
-
-    result = (Double)getStatResult("ar", "mean", VAL_TYPE.DOUBLE);
-    calculated = (Double)getStatResult("ar", "meanc", VAL_TYPE.DOUBLE);
-    assertTrue(result==calculated);
-    assertEquals(getRawResponse(), result, calculated, 0.0);
-  }
-  
-  @Test
-  public void multiplyTest() throws Exception { 
-    double result = (Double)getStatResult("mr", "sum", VAL_TYPE.DOUBLE);
-    double calculated = (Double)getStatResult("mr", "sumc", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(),  result, calculated, 0.0);
-    
-    result = (Double)getStatResult("mr", "mean", VAL_TYPE.DOUBLE);
-    calculated = (Double)getStatResult("mr", "meanc", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(),  result, calculated, 0.0);
-  }
-  
-  @Test
-  public void divideTest() throws Exception { 
-    Double result = (Double)getStatResult("dr", "sum", VAL_TYPE.DOUBLE);
-    Double calculated = (Double)getStatResult("dr", "sumc", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(),  result, calculated, 0.0);
-    
-    result = (Double)getStatResult("dr", "mean", VAL_TYPE.DOUBLE);
-    calculated = (Double)getStatResult("dr", "meanc", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(),  result, calculated, 0.0);
-  }
-  
-  @Test
-  public void powerTest() throws Exception { 
-    double result = (Double)getStatResult("pr", "sum", VAL_TYPE.DOUBLE);
-    double calculated = (Double)getStatResult("pr", "sumc", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(), result, calculated, 0.0);
-    assertEquals(getRawResponse(),  result, calculated, 0.0);
-    
-    result = (Double)getStatResult("pr", "mean", VAL_TYPE.DOUBLE);
-    calculated = (Double)getStatResult("pr", "meanc", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(), result, calculated, 0.0);
-    assertEquals(getRawResponse(), result, calculated, 0.0);
-  }
-  
-  @Test
-  public void negateTest() throws Exception { 
-    double result = (Double)getStatResult("nr", "sum", VAL_TYPE.DOUBLE);
-    double calculated = (Double)getStatResult("nr", "sumc", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(),  result, calculated, 0.0);
-    
-    result = (Double)getStatResult("nr", "mean", VAL_TYPE.DOUBLE);
-    calculated = (Double)getStatResult("nr", "meanc", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(),  result, calculated, 0.0);
-  }
-
-  @Test 
-  public void absoluteValueTest() throws Exception {
-    double result = (Double)getStatResult("avr", "sum", VAL_TYPE.DOUBLE);
-    double calculated = (Double)getStatResult("avr", "sumc", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(),  result, calculated, 0.0);
-    
-    result = (Double)getStatResult("avr", "mean", VAL_TYPE.DOUBLE);
-    calculated = (Double)getStatResult("avr", "meanc", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(),  result, calculated, 0.0);
-  }
-  
-  @Test
-  public void constantNumberTest() throws Exception { 
-    double result = (Double)getStatResult("cnr", "sum", VAL_TYPE.DOUBLE);
-    double calculated = (Double)getStatResult("cnr", "sumc", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(), result, calculated, 0.0);
-    assertEquals(getRawResponse(), result, calculated, 0.0);
-    
-    result = (Double)getStatResult("cnr", "mean", VAL_TYPE.DOUBLE);
-    calculated = (Double)getStatResult("cnr", "meanc", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(), result, calculated, 0.0);
-    assertEquals(getRawResponse(),  result, calculated, 0.0);
-  }
-  
-  @Test
-  public void dateMathTest() throws Exception {
-    String result = (String)getStatResult("dmr", "median", VAL_TYPE.DATE);
-    String calculated = (String)getStatResult("dmr", "medianc", VAL_TYPE.DATE);
-    assertEquals(getRawResponse(), result, calculated);
-    
-    result = (String)getStatResult("dmr", "max", VAL_TYPE.DATE);
-    calculated = (String)getStatResult("dmr", "maxc", VAL_TYPE.DATE);
-    assertEquals(getRawResponse(), result, calculated);
-  }
-  
-  @Test
-  public void constantDateTest() throws Exception { 
-    String result = (String)getStatResult("cdr", "median", VAL_TYPE.DATE);
-    String calculated = (String)getStatResult("cdr", "medianc", VAL_TYPE.DATE);
-    assertEquals(getRawResponse(), result, calculated);
-    assertEquals(getRawResponse(), result, calculated);
-    
-    result = (String)getStatResult("cdr", "max", VAL_TYPE.DATE);
-    calculated = (String)getStatResult("cdr", "maxc", VAL_TYPE.DATE);
-    assertEquals(getRawResponse(), result, calculated);
-  }
-  
-  @Test
-  public void constantStringTest() throws Exception { 
-    String result = (String)getStatResult("csr", "min", VAL_TYPE.STRING);
-    String calculated = (String)getStatResult("csr", "minc", VAL_TYPE.STRING);
-    assertEquals(getRawResponse(), result, calculated);
-    
-    result = (String)getStatResult("csr", "max", VAL_TYPE.STRING);
-    calculated = (String)getStatResult("csr", "maxc", VAL_TYPE.STRING);
-    assertEquals(getRawResponse(), result, calculated);
-  }
-  
-  @Test
-  public void concatenateTest() throws Exception { 
-    String result = (String)getStatResult("cr", "min", VAL_TYPE.STRING);
-    String calculated = (String)getStatResult("cr", "minc", VAL_TYPE.STRING);
-    assertEquals(getRawResponse(), result, calculated);
-    
-    result = (String)getStatResult("cr", "max", VAL_TYPE.STRING);
-    calculated = (String)getStatResult("cr", "maxc", VAL_TYPE.STRING);
-    assertEquals(getRawResponse(), result, calculated);
-  }
-  
-  @Test
-  public void reverseTest() throws Exception { 
-    String result = (String)getStatResult("rr", "min", VAL_TYPE.STRING);
-    String calculated = (String)getStatResult("rr", "minc", VAL_TYPE.STRING);
-    assertEquals(getRawResponse(), result, calculated);
-    
-    result = (String)getStatResult("rr", "max", VAL_TYPE.STRING);
-    calculated = (String)getStatResult("rr", "maxc", VAL_TYPE.STRING);
-    assertEquals(getRawResponse(), result, calculated);
-  }
-  
-  @Test
-  public void missingTest() throws Exception { 
-    double min = (Double)getStatResult("ms", "min", VAL_TYPE.DOUBLE);
-    double max = (Double)getStatResult("ms", "max", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(), 48.0d, max, 0.0);
-    assertEquals(getRawResponse(), 1.0d, min, 0.0);
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java b/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java
index e5eaff9..8e9477b 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/ResponseBuilder.java
@@ -60,6 +60,7 @@ public class ResponseBuilder
   public boolean doExpand;
   public boolean doStats;
   public boolean doTerms;
+  public boolean doAnalytics;
   public MergeStrategy mergeFieldHandler;
 
   private boolean needDocList = false;
@@ -173,6 +174,8 @@ public class ResponseBuilder
   StatsInfo _statsInfo;
   TermsComponent.TermsHelper _termsHelper;
   SimpleOrderedMap<List<NamedList<Object>>> _pivots;
+  Object _analyticsRequestManager;
+  boolean _isOlapAnalytics;
 
   // Context fields for grouping
   public final Map<String, Collection<SearchGroup<BytesRef>>> mergedSearchGroups = new HashMap<>();


[05/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/AnalyticsValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/AnalyticsValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/AnalyticsValue.java
new file mode 100644
index 0000000..54a3003
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/AnalyticsValue.java
@@ -0,0 +1,55 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.util.function.Consumer;
+
+/**
+ * A single-valued analytics value.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #getObject()} and {@link #exists()},
+ * resulting in different values on each call.
+ */
+public interface AnalyticsValue extends AnalyticsValueStream {
+  /**
+   * Check whether the current value exists.
+   * <br>
+   * NOTE: The result of this method is only guaranteed after any {@code get<Type>()} method is called.
+   * 
+   * @return whether the current value exists
+   */
+  boolean exists();
+  /**
+   * Get the object representation of the current value.
+   * 
+   * @return the current value
+   */
+  Object getObject();
+  
+  /**
+   * An abstract base for {@link AnalyticsValue} that automatically casts to castable types.
+   */
+  public static abstract class AbstractAnalyticsValue implements AnalyticsValue {
+    @Override
+    public void streamObjects(Consumer<Object> cons) {
+      Object value = getObject();
+      if (exists()) {
+        cons.accept(value);
+      }
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/AnalyticsValueStream.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/AnalyticsValueStream.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/AnalyticsValueStream.java
new file mode 100644
index 0000000..0c02115
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/AnalyticsValueStream.java
@@ -0,0 +1,133 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.util.Arrays;
+import java.util.Locale;
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.ExpressionFactory;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A multi-valued analytics value, the super-type of all Analytics value types.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #streamObjects},
+ * resulting in different values on each call.
+ */
+public interface AnalyticsValueStream {
+  /**
+   * Get the name of function or value.
+   * 
+   * @return the name of function/value
+   */
+  String getName();
+  /**
+   * Get the expression string of the analytics value stream. Must be unique to the expression. 
+   * If passed to {@link ExpressionFactory#createExpression(String)}, the exact same expression should be created. 
+   * 
+   * @return the name of function/value
+   */
+  String getExpressionStr();
+  /**
+   * Stream the object representations of all current values, if any exist.
+   * 
+   * @param cons The consumer to accept the values
+   */
+  void streamObjects(Consumer<Object> cons);
+  
+  /**
+   * The types of expressions.
+   */
+  static enum ExpressionType {
+    CONST             (true, true),
+    FIELD             (true,  false),
+    UNREDUCED_MAPPING (true,  false),
+    REDUCTION         (false, true),
+    REDUCED_MAPPING   (false, true);
+
+    private final boolean unreduced;
+    private final boolean reduced;
+    
+    ExpressionType(boolean unreduced, boolean reduced) {
+      this.unreduced = unreduced;
+      this.reduced = reduced;
+    }
+
+    public boolean isUnreduced() {
+      return unreduced;
+    }
+    public boolean isReduced() {
+      return reduced;
+    }
+  }
+
+  /**
+   * Get the type of the expression that this class represents.
+   * 
+   * @return the expression type
+   */
+  ExpressionType getExpressionType();
+  
+  /**
+   * Helper to create an expression string for a function.
+   * 
+   * @param funcName the name of the function
+   * @param params the parameters of the function
+   * @return a valid expression string for the function.
+   */
+  static String createExpressionString(String funcName, 
+                                       AnalyticsValueStream... params) {
+    return String.format(Locale.ROOT, "%s(%s)",
+                         funcName,
+                         Arrays.stream(params).
+                                map(param -> param.getExpressionStr()).
+                                reduce((a, b) -> a + "," + b).orElseGet(() -> ""));
+  }
+
+  /**
+   * Determine whether the expression is a unreduced mapping expression, a reduced mapping expression, or a constant.
+   * 
+   * @param exprString the string representing the expression, used when creating exceptions
+   * @param params the parameters 
+   * @return the expression type
+   * @throws SolrException if the params are incompatable types,
+   * for example if reduced and unreduced params are both included
+   */
+  static ExpressionType determineMappingPhase(String exprString, AnalyticsValueStream... params) throws SolrException {
+    boolean unreduced = true;
+    boolean reduced = true;
+    for (AnalyticsValueStream param : params) {
+      unreduced &= param.getExpressionType().isUnreduced();
+      reduced &= param.getExpressionType().isReduced();
+    }
+    if (unreduced && reduced) {
+      return ExpressionType.CONST;
+    }
+    else if (unreduced) {
+      return ExpressionType.UNREDUCED_MAPPING;
+    }
+    else if (reduced) {
+      return ExpressionType.REDUCED_MAPPING;
+    }
+    else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The following expression contains incorrect parameters. " + 
+                            "(ReductionFunctions cannot be in the paramters of other ReductionFunctions): " + exprString);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/BooleanValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/BooleanValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/BooleanValue.java
new file mode 100644
index 0000000..5d58a8b
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/BooleanValue.java
@@ -0,0 +1,85 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+
+/**
+ * A single-valued analytics value that can be represented as a boolean.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #getBoolean()} and {@link #exists()},
+ * resulting in different values on each call.
+ */
+public interface BooleanValue extends BooleanValueStream, AnalyticsValue {
+  /**
+   * Get the boolean representation of the current value.
+   * <p>
+   * NOTE: The value returned is not valid unless calling {@link #exists()} afterwards returns {@code TRUE}.
+   * 
+   * @return the current value
+   */
+  boolean getBoolean();
+  
+  /**
+   * An interface that represents all of the types a {@link BooleanValue} should be able to cast to. 
+   */
+  public static interface CastingBooleanValue extends BooleanValue, StringValue, ComparableValue {}
+  
+  /**
+   * An abstract base for {@link CastingBooleanValue} that automatically casts to all types if {@link #getBoolean()} and {@link #exists()} are implemented.
+   */
+  public static abstract class AbstractBooleanValue implements CastingBooleanValue {
+    @Override
+    public String getString() {
+      boolean val = getBoolean();
+      return exists() ? Boolean.toString(val) : null;
+    }
+    @Override
+    public Object getObject() {
+      boolean val = getBoolean();
+      return exists() ? val : null;
+    }
+    @Override
+    public void streamBooleans(BooleanConsumer cons) {
+      boolean val = getBoolean();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamStrings(Consumer<String> cons) {
+      String val = getString();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamObjects(Consumer<Object> cons) {
+      Object val = getObject();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public ExpressionComparator<Boolean> getObjectComparator(String expression) {
+      return new ExpressionComparator<>(expression);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/BooleanValueStream.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/BooleanValueStream.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/BooleanValueStream.java
new file mode 100644
index 0000000..0827aea
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/BooleanValueStream.java
@@ -0,0 +1,55 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+
+/**
+ * A multi-valued analytics value that can be represented as a boolean.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #streamBooleans},
+ * resulting in different values on each call.
+ */
+public interface BooleanValueStream extends AnalyticsValueStream {
+  /**
+   * Stream the boolean representations of all current values, if any exist.
+   * 
+   * @param cons The consumer to accept the values
+   */
+  void streamBooleans(BooleanConsumer cons);
+  
+  /**
+   * An interface that represents all of the types a {@link BooleanValueStream} should be able to cast to. 
+   */
+  public static interface CastingBooleanValueStream extends BooleanValueStream, StringValueStream {}
+  
+  /**
+   * An abstract base for {@link CastingBooleanValueStream} that automatically casts to all types if {@link #streamBooleans} is implemented.
+   */
+  public static abstract class AbstractBooleanValueStream implements CastingBooleanValueStream {
+    @Override
+    public void streamStrings(Consumer<String> cons) {
+      streamBooleans((boolean val) -> cons.accept(Boolean.toString(val)));
+    }
+    @Override
+    public void streamObjects(Consumer<Object> cons) {
+      streamBooleans((boolean val) -> cons.accept(val));
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/ComparableValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/ComparableValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/ComparableValue.java
new file mode 100644
index 0000000..93cae48
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/ComparableValue.java
@@ -0,0 +1,32 @@
+/*
+ * 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.solr.analytics.value;
+
+import org.apache.solr.analytics.facet.compare.FacetResultsComparator;
+
+/**
+ * A single-valued analytics value that can be compared and used to sort a facet.
+ */
+public interface ComparableValue extends AnalyticsValue {
+  /**
+   * Create an entry comparator used to sort the facet-value buckets of a facet.
+   * 
+   * @param expression the name of the expression in the results array
+   * @return a comparator to sort the buckets with
+   */
+  FacetResultsComparator getObjectComparator(String expression);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DateValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DateValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DateValue.java
new file mode 100644
index 0000000..b105d58
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DateValue.java
@@ -0,0 +1,102 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.time.Instant;
+import java.util.Date;
+import java.util.function.Consumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+
+/**
+ * A single-valued analytics value that can be represented as a date.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #getDate()} and {@link #exists()},
+ * resulting in different values on each call.
+ * <p>
+ * NOTE: Most date expressions work with the {@code long} representation of the date, so that less
+ * objects need to be created during execution.
+ */
+public interface DateValue extends DateValueStream, LongValue {
+  /**
+   * Get the date representation of the current value.
+   * <p>
+   * NOTE: The value returned is not valid unless calling {@link #exists()} afterwards returns {@code TRUE}.
+   * 
+   * @return the current value
+   */
+  Date getDate();
+  
+  /**
+   * An interface that represents all of the types a {@link DateValue} should be able to cast to. 
+   */
+  public static interface CastingDateValue extends DateValue, LongValue, StringValue, ComparableValue {}
+
+  /**
+   * An abstract base for {@link CastingDateValue} that automatically casts to all types if {@link #getLong()} and {@link #exists()} are implemented.
+   */
+  public static abstract class AbstractDateValue implements CastingDateValue {
+    @Override
+    public Date getDate() {
+      long val = getLong();
+      return exists() ? new Date(val) : null;
+    }
+    @Override
+    public String getString() {
+      long val = getLong();
+      return exists() ? Instant.ofEpochMilli(val).toString() : null;
+    }
+    @Override
+    public Object getObject() {
+      long val = getLong();
+      return exists() ? new Date(val) : null;
+    }
+    @Override
+    public void streamDates(Consumer<Date> cons) {
+      Date val = getDate();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamLongs(LongConsumer cons) {
+      long val = getLong();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamStrings(Consumer<String> cons) {
+      String val = getString();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamObjects(Consumer<Object> cons) {
+      Object val = getObject();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public ExpressionComparator<Date> getObjectComparator(String expression) {
+      return new ExpressionComparator<>(expression);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DateValueStream.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DateValueStream.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DateValueStream.java
new file mode 100644
index 0000000..f709264
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DateValueStream.java
@@ -0,0 +1,62 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.time.Instant;
+import java.util.Date;
+import java.util.function.Consumer;
+
+/**
+ * A multi-valued analytics value that can be represented as a date.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #streamDates},
+ * resulting in different values on each call.
+ * <p>
+ * NOTE: Most date expressions work with the {@code long} representation of the date, so that less
+ * objects need to be created during execution.
+ */
+public interface DateValueStream extends LongValueStream {
+  /**
+   * Stream the date representations of all current values, if any exist.
+   * 
+   * @param cons The consumer to accept the values
+   */
+  void streamDates(Consumer<Date> cons);
+  
+  /**
+   * An interface that represents all of the types a {@link DateValueStream} should be able to cast to. 
+   */
+  public static interface CastingDateValueStream extends DateValueStream, LongValueStream, StringValueStream {}
+
+  /**
+   * An abstract base for {@link CastingDateValueStream} that automatically casts to all types if {@link #streamLongs} is implemented.
+   */
+  public static abstract class AbstractDateValueStream implements CastingDateValueStream {
+    @Override
+    public void streamDates(Consumer<Date> cons) {
+      streamLongs((long val) -> cons.accept(new Date(val)));
+    }
+    @Override
+    public void streamStrings(Consumer<String> cons) {
+      streamLongs((long val) -> cons.accept(Instant.ofEpochMilli(val).toString()));
+    }
+    @Override
+    public void streamObjects(Consumer<Object> cons) {
+      streamLongs((long val) -> cons.accept(new Date(val)));
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DoubleValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DoubleValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DoubleValue.java
new file mode 100644
index 0000000..ba527f0
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DoubleValue.java
@@ -0,0 +1,86 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+
+/**
+ * A single-valued analytics value that can be represented as a date.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #getDouble()} and {@link #exists()},
+ * resulting in different values on each call.
+ */
+public interface DoubleValue extends DoubleValueStream, AnalyticsValue {
+  /**
+   * Get the double representation of the current value.
+   * <p>
+   * NOTE: The value returned is not valid unless calling {@link #exists()} afterwards returns {@code TRUE}.
+   * 
+   * @return the current value
+   */
+  double getDouble();
+
+  /**
+   * An interface that represents all of the types a {@link DoubleValue} should be able to cast to. 
+   */
+  public static interface CastingDoubleValue extends DoubleValue, StringValue, ComparableValue {}
+  
+
+  /**
+   * An abstract base for {@link CastingDoubleValue} that automatically casts to all types if {@link #getDouble()} and {@link #exists()} are implemented.
+   */
+  public static abstract class AbstractDoubleValue implements CastingDoubleValue {
+    @Override
+    public String getString() {
+      double val = getDouble();
+      return exists() ? Double.toString(val) : null;
+    }
+    @Override
+    public Object getObject() {
+      double val = getDouble();
+      return exists() ? val : null;
+    }
+    @Override
+    public void streamDoubles(DoubleConsumer cons) {
+      double val = getDouble();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamStrings(Consumer<String> cons) {
+      String val = getString();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamObjects(Consumer<Object> cons) {
+      Object val = getObject();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public ExpressionComparator<Double> getObjectComparator(String expression) {
+      return new ExpressionComparator<>(expression);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DoubleValueStream.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DoubleValueStream.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DoubleValueStream.java
new file mode 100644
index 0000000..a50c834
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/DoubleValueStream.java
@@ -0,0 +1,54 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+
+/**
+ * A multi-valued analytics value that can be represented as a boolean.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #streamDoubles},
+ * resulting in different values on each call.
+ */
+public interface DoubleValueStream extends AnalyticsValueStream {
+  /**
+   * Stream the double representations of all current values, if any exist.
+   * 
+   * @param cons The consumer to accept the values
+   */
+  void streamDoubles(DoubleConsumer cons);
+
+  /**
+   * An interface that represents all of the types a {@link DoubleValueStream} should be able to cast to. 
+   */
+  public static interface CastingDoubleValueStream extends DoubleValueStream, StringValueStream {}
+
+  /**
+   * An abstract base for {@link CastingDoubleValueStream} that automatically casts to all types if {@link #streamDoubles} is implemented.
+   */
+  public static abstract class AbstractDoubleValueStream implements CastingDoubleValueStream {
+    @Override
+    public void streamStrings(Consumer<String> cons) {
+      streamDoubles((double val) -> cons.accept(Double.toString(val)));
+    }
+    @Override
+    public void streamObjects(Consumer<Object> cons) {
+      streamDoubles((double val) -> cons.accept(val));
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/FloatValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/FloatValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/FloatValue.java
new file mode 100644
index 0000000..16883d5
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/FloatValue.java
@@ -0,0 +1,97 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+
+/**
+ * A single-valued analytics value that can be represented as a float.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #getFloat()} and {@link #exists()},
+ * resulting in different values on each call.
+ */
+public interface FloatValue extends FloatValueStream, AnalyticsValue {
+  /**
+   * Get the float representation of the current value.
+   * <p>
+   * NOTE: The value returned is not valid unless calling {@link #exists()} afterwards returns {@code TRUE}.
+   * 
+   * @return the current value
+   */
+  float getFloat();
+
+  /**
+   * An interface that represents all of the types a {@link FloatValue} should be able to cast to. 
+   */
+  public static interface CastingFloatValue extends FloatValue, DoubleValue, StringValue, ComparableValue {}
+
+  /**
+   * An abstract base for {@link CastingFloatValue} that automatically casts to all types if {@link #getFloat()} and {@link #exists()} are implemented.
+   */
+  public static abstract class AbstractFloatValue implements CastingFloatValue {
+    @Override
+    public double getDouble() {
+      return getFloat();
+    }
+    @Override
+    public String getString() {
+      float val = getFloat();
+      return exists() ? Float.toString(val) : null;
+    }
+    @Override
+    public Object getObject() {
+      float val = getFloat();
+      return exists() ? val : null;
+    }
+    @Override
+    public void streamFloats(FloatConsumer cons) {
+      float val = getFloat();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamDoubles(DoubleConsumer cons) {
+      double val = getDouble();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamStrings(Consumer<String> cons) {
+      String val = getString();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamObjects(Consumer<Object> cons) {
+      Object val = getObject();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public ExpressionComparator<Float> getObjectComparator(String expression) {
+      return new ExpressionComparator<>(expression);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/FloatValueStream.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/FloatValueStream.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/FloatValueStream.java
new file mode 100644
index 0000000..cf15a21
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/FloatValueStream.java
@@ -0,0 +1,60 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+
+import org.apache.solr.analytics.util.function.FloatConsumer;
+
+/**
+ * A multi-valued analytics value that can be represented as a float.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #streamFloats},
+ * resulting in different values on each call.
+ */
+public interface FloatValueStream extends AnalyticsValueStream {
+  /**
+   * Stream the float representations of all current values, if any exist.
+   * 
+   * @param cons The consumer to accept the values
+   */
+  void streamFloats(FloatConsumer cons);
+
+  /**
+   * An interface that represents all of the types a {@link FloatValueStream} should be able to cast to. 
+   */
+  public static interface CastingFloatValueStream extends FloatValueStream, DoubleValueStream, StringValueStream { }
+
+  /**
+   * An abstract base for {@link CastingFloatValueStream} that automatically casts to all types if {@link #streamFloats} is implemented.
+   */
+  public static abstract class AbstractFloatValueStream implements CastingFloatValueStream {
+    @Override
+    public void streamDoubles(DoubleConsumer cons) {
+      streamFloats((float val) -> cons.accept(val));
+    }
+    @Override
+    public void streamStrings(Consumer<String> cons) {
+      streamFloats((float val) -> cons.accept(Float.toString(val)));
+    }
+    @Override
+    public void streamObjects(Consumer<Object> cons) {
+      streamFloats((float val) -> cons.accept(val));
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/IntValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/IntValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/IntValue.java
new file mode 100644
index 0000000..5688357
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/IntValue.java
@@ -0,0 +1,121 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+
+/**
+ * A single-valued analytics value that can be represented as an int.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #getInt()} and {@link #exists()},
+ * resulting in different values on each call.
+ */
+public interface IntValue extends IntValueStream, AnalyticsValue {
+  /**
+   * Get the int representation of the current value.
+   * <p>
+   * NOTE: The value returned is not valid unless calling {@link #exists()} afterwards returns {@code TRUE}.
+   * 
+   * @return the current value
+   */
+  int getInt();
+
+  /**
+   * An interface that represents all of the types a {@link IntValue} should be able to cast to. 
+   */
+  public static interface CastingIntValue extends IntValue, LongValue, FloatValue,DoubleValue, StringValue, ComparableValue { }
+
+  /**
+   * An abstract base for {@link CastingIntValue} that automatically casts to all types if {@link #getInt()} and {@link #exists()} are implemented.
+   */
+  public static abstract class AbstractIntValue implements CastingIntValue {
+    @Override
+    public long getLong() {
+      return getInt();
+    }
+    @Override
+    public float getFloat() {
+      return getInt();
+    }
+    @Override
+    public double getDouble() {
+      return getInt();
+    }
+    @Override
+    public String getString() {
+      int val = getInt();
+      return exists() ? Integer.toString(val) : null;
+    }
+    @Override
+    public Object getObject() {
+      int val = getInt();
+      return exists() ? val : null;
+    }
+    @Override
+    public void streamInts(IntConsumer cons) {
+      int val = getInt();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamLongs(LongConsumer cons) {
+      long val = getLong();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamFloats(FloatConsumer cons) {
+      float val = getFloat();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamDoubles(DoubleConsumer cons) {
+      double val = getDouble();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamStrings(Consumer<String> cons) {
+      String val = getString();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamObjects(Consumer<Object> cons) {
+      Object val = getObject();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public ExpressionComparator<Integer> getObjectComparator(String expression) {
+      return new ExpressionComparator<>(expression);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/IntValueStream.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/IntValueStream.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/IntValueStream.java
new file mode 100644
index 0000000..9cbe28c
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/IntValueStream.java
@@ -0,0 +1,71 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.util.function.FloatConsumer;
+
+/**
+ * A multi-valued analytics value that can be represented as a int.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #streamInts},
+ * resulting in different values on each call.
+ */
+public interface IntValueStream extends AnalyticsValueStream {
+  /**
+   * Stream the int representations of all current values, if any exist.
+   * 
+   * @param cons The consumer to accept the values
+   */
+  void streamInts(IntConsumer cons);
+
+  /**
+   * An interface that represents all of the types a {@link IntValueStream} should be able to cast to. 
+   */
+  public static interface CastingIntValueStream extends IntValueStream, LongValueStream, FloatValueStream, 
+                                                        DoubleValueStream, StringValueStream {}
+
+  /**
+   * An abstract base for {@link CastingIntValueStream} that automatically casts to all types if {@link #streamInts} is implemented.
+   */
+  public static abstract class AbstractIntValueStream implements CastingIntValueStream {
+    @Override
+    public void streamLongs(LongConsumer cons) {
+      streamInts((int val) -> cons.accept(val));
+    }
+    @Override
+    public void streamFloats(FloatConsumer cons) {
+      streamInts((int val) -> cons.accept(val));
+    }
+    @Override
+    public void streamDoubles(DoubleConsumer cons) {
+      streamInts((int val) -> cons.accept(val));
+    }
+    @Override
+    public void streamStrings(Consumer<String> cons) {
+      streamInts((int val) -> cons.accept(Integer.toString(val)));
+    }
+    @Override
+    public void streamObjects(Consumer<Object> cons) {
+      streamInts((int val) -> cons.accept(val));
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/LongValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/LongValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/LongValue.java
new file mode 100644
index 0000000..623cd13
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/LongValue.java
@@ -0,0 +1,97 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+
+/**
+ * A single-valued analytics value that can be represented as a long.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #getLong()} and {@link #exists()},
+ * resulting in different values on each call.
+ */
+public interface LongValue extends LongValueStream, AnalyticsValue {
+  /**
+   * Get the long representation of the current value.
+   * <p>
+   * NOTE: The value returned is not valid unless calling {@link #exists()} afterwards returns {@code TRUE}.
+   * 
+   * @return the current value
+   */
+  long getLong();
+
+  /**
+   * An interface that represents all of the types a {@link LongValue} should be able to cast to. 
+   */
+  public static interface CastingLongValue extends LongValue, DoubleValue, StringValue, ComparableValue {}
+
+  /**
+   * An abstract base for {@link CastingLongValue} that automatically casts to all types if {@link #getLong()} and {@link #exists()} are implemented.
+   */
+  public static abstract class AbstractLongValue implements CastingLongValue {
+    @Override
+    public double getDouble() {
+      return getLong();
+    }
+    @Override
+    public String getString() {
+      long val = getLong();
+      return exists() ? Long.toString(val) : null;
+    }
+    @Override
+    public Object getObject() {
+      long val = getLong();
+      return exists() ? val : null;
+    }
+    @Override
+    public void streamLongs(LongConsumer cons) {
+      long val = getLong();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamDoubles(DoubleConsumer cons) {
+      double val = getDouble();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamStrings(Consumer<String> cons) {
+      String val = getString();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public void streamObjects(Consumer<Object> cons) {
+      Object val = getObject();
+      if (exists()) {
+        cons.accept(val);
+      }
+    }
+    @Override
+    public ExpressionComparator<Long> getObjectComparator(String expression) {
+      return new ExpressionComparator<>(expression);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/LongValueStream.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/LongValueStream.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/LongValueStream.java
new file mode 100644
index 0000000..d18cb03
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/LongValueStream.java
@@ -0,0 +1,60 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.LongConsumer;
+
+/**
+ * A multi-valued analytics value that can be represented as a long.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #streamLongs},
+ * resulting in different values on each call.
+ */
+public interface LongValueStream extends AnalyticsValueStream {
+  /**
+   * Stream the long representations of all current values, if any exist.
+   * 
+   * @param cons The consumer to accept the values
+   */
+  void streamLongs(LongConsumer cons);
+
+  /**
+   * An interface that represents all of the types a {@link LongValueStream} should be able to cast to. 
+   */
+  public static interface CastingLongValueStream extends LongValueStream, DoubleValueStream, 
+                                                         StringValueStream { }
+
+  /**
+   * An abstract base for {@link CastingLongValueStream} that automatically casts to all types if {@link #streamLongs} is implemented.
+   */
+  public static abstract class AbstractLongValueStream implements CastingLongValueStream {
+    @Override
+    public void streamDoubles(DoubleConsumer cons) {
+      streamLongs((long val) -> cons.accept(val));
+    }
+    @Override
+    public void streamStrings(Consumer<String> cons) {
+      streamLongs((long val) -> cons.accept(Long.toString(val)));
+    }
+    @Override
+    public void streamObjects(Consumer<Object> cons) {
+      streamLongs((long val) -> cons.accept(val));
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/StringValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/StringValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/StringValue.java
new file mode 100644
index 0000000..7baf18e
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/StringValue.java
@@ -0,0 +1,71 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+
+/**
+ * A single-valued analytics value that can be represented as a string.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #getString()} and {@link #exists()},
+ * resulting in different values on each call.
+ */
+public interface StringValue extends StringValueStream, AnalyticsValue {
+  /**
+   * Get the String representation of the current value.
+   * <p>
+   * NOTE: The value returned is not valid unless calling {@link #exists()} afterwards returns {@code TRUE}.
+   * 
+   * @return the current value
+   */
+  String getString();
+
+  /**
+   * An interface that represents all of the types a {@link StringValue} should be able to cast to. 
+   */
+  public static interface CastingStringValue extends StringValue, ComparableValue {}
+
+  /**
+   * An abstract base for {@link CastingStringValue} that automatically casts to all types if {@link #getString()} and {@link #exists()} are implemented.
+   */
+  public static abstract class AbstractStringValue implements CastingStringValue {
+    @Override
+    public String getObject() {
+      return getString();
+    }
+    @Override
+    public void streamStrings(Consumer<String> func) {
+      String val = getString();
+      if (exists()) {
+        func.accept(val);
+      }
+    }
+    @Override
+    public void streamObjects(Consumer<Object> func) {
+      Object val = getObject();
+      if (exists()) {
+        func.accept(val);
+      }
+    }
+    @Override
+    public ExpressionComparator<String> getObjectComparator(String expression) {
+      return new ExpressionComparator<>(expression);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/StringValueStream.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/StringValueStream.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/StringValueStream.java
new file mode 100644
index 0000000..2269ac4
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/StringValueStream.java
@@ -0,0 +1,49 @@
+/*
+ * 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.solr.analytics.value;
+
+import java.util.function.Consumer;
+
+/**
+ * A multi-valued analytics value that can be represented as a String.
+ * <p>
+ * The back-end production of the value can change inbetween calls to {@link #streamStrings},
+ * resulting in different values on each call.
+ */
+public interface StringValueStream extends AnalyticsValueStream {
+  /**
+   * Stream the String representations of all current values, if any exist.
+   * 
+   * @param cons The consumer to accept the values
+   */
+  void streamStrings(Consumer<String> cons);
+
+  /**
+   * An interface that represents all of the types a {@link StringValueStream} should be able to cast to. 
+   */
+  public static interface CastingStringValueStream extends StringValueStream {}
+
+  /**
+   * An abstract base for {@link CastingStringValueStream} that automatically casts to all types if {@link #streamStrings} is implemented.
+   */
+  public static abstract class AbstractStringValueStream implements CastingStringValueStream {
+    @Override
+    public void streamObjects(Consumer<Object> cons) {
+      streamStrings((String val) -> cons.accept(val));
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantBooleanValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantBooleanValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantBooleanValue.java
new file mode 100644
index 0000000..aeccaaa
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantBooleanValue.java
@@ -0,0 +1,91 @@
+/*
+ * 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.solr.analytics.value.constant;
+
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.facet.compare.ConstantComparator;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.value.BooleanValue;
+import org.apache.solr.analytics.value.BooleanValue.CastingBooleanValue;
+
+/**
+ * A constant {@link BooleanValue}. Every call to {@link #getBoolean()} and other methods will return the same constant value.
+ */
+public class ConstantBooleanValue extends ConstantValue implements CastingBooleanValue {
+  private final boolean value;
+  private final String valueStr;
+  public static final String name = "const_bool";
+  private final String exprStr;
+
+  public ConstantBooleanValue(boolean value) {
+    this.value = value;
+    this.valueStr = Boolean.toString(value);
+    this.exprStr = ConstantValue.createExpressionString(this, valueStr);
+  }
+
+  @Override
+  public boolean getBoolean() {
+    return value;
+  }
+  @Override
+  public String getString() {
+    return valueStr;
+  }
+  @Override
+  public Object getObject() {
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return true;
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    cons.accept(value);
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    cons.accept(valueStr);
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    cons.accept(value);
+  }
+
+  @Override
+  public ConstantComparator getObjectComparator(String expression) {
+    return new ConstantComparator();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.CONST;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantDateValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantDateValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantDateValue.java
new file mode 100644
index 0000000..5614d6c
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantDateValue.java
@@ -0,0 +1,103 @@
+/*
+ * 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.solr.analytics.value.constant;
+
+import java.time.Instant;
+import java.util.Date;
+import java.util.function.Consumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.facet.compare.ConstantComparator;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValue.CastingDateValue;
+
+/**
+ * A constant {@link DateValue}. Every call to {@link #getDate()} and other methods will return the same constant value.
+ */
+public class ConstantDateValue extends ConstantValue implements CastingDateValue {
+  private final long value;
+  private final Date valueDate;
+  private final String valueStr;
+  public static final String name = "const_date";
+  private final String exprStr;
+
+  public ConstantDateValue(long value) {
+    this.value = value;
+    this.valueDate = new Date(value);
+    this.valueStr = Instant.ofEpochMilli(value).toString();
+    this.exprStr = ConstantValue.createExpressionString(this, valueStr);
+  }
+
+
+  @Override
+  public long getLong() {
+    return value;
+  }
+  @Override
+  public Date getDate() {
+    return valueDate;
+  }
+  @Override
+  public String getString() {
+    return valueStr;
+  }
+  @Override
+  public Object getObject() {
+    return valueDate;
+  }
+  @Override
+  public boolean exists() {
+    return true;
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    cons.accept(value);
+  }
+  @Override
+  public void streamDates(Consumer<Date> cons) {
+    cons.accept(valueDate);
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    cons.accept(valueStr);
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    cons.accept(valueDate);
+  }
+
+  @Override
+  public ConstantComparator getObjectComparator(String expression) {
+    return new ConstantComparator();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.CONST;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantDoubleValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantDoubleValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantDoubleValue.java
new file mode 100644
index 0000000..cf5682a
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantDoubleValue.java
@@ -0,0 +1,90 @@
+/*
+ * 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.solr.analytics.value.constant;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+
+import org.apache.solr.analytics.facet.compare.ConstantComparator;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValue.CastingDoubleValue;
+
+/**
+ * A constant {@link DoubleValue}. Every call to {@link #getDouble()} and other methods will return the same constant value.
+ */
+public class ConstantDoubleValue extends ConstantValue implements CastingDoubleValue {
+  private final double value;
+  private final String valueStr;
+  public static final String name = "const_double";
+  private final String exprStr;
+
+  public ConstantDoubleValue(double value) {
+    this.value = value;
+    this.valueStr = Double.toString(value);
+    this.exprStr = ConstantValue.createExpressionString(this, valueStr);
+  }
+
+  @Override
+  public double getDouble() {
+    return value;
+  }
+  @Override
+  public String getString() {
+    return valueStr;
+  }
+  @Override
+  public Object getObject() {
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return true;
+  }
+
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    cons.accept(value);
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    cons.accept(valueStr);
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    cons.accept(value);
+  }
+
+  @Override
+  public ConstantComparator getObjectComparator(String expression) {
+    return new ConstantComparator();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.CONST;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantFloatValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantFloatValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantFloatValue.java
new file mode 100644
index 0000000..ec495a4
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantFloatValue.java
@@ -0,0 +1,99 @@
+/*
+ * 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.solr.analytics.value.constant;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+
+import org.apache.solr.analytics.facet.compare.ConstantComparator;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.FloatValue;
+import org.apache.solr.analytics.value.FloatValue.CastingFloatValue;
+
+/**
+ * A constant {@link FloatValue}. Every call to {@link #getFloat()} and other methods will return the same constant value.
+ */
+public class ConstantFloatValue extends ConstantValue implements CastingFloatValue {
+  private final float value;
+  private final String valueStr;
+  public static final String name = "const_float";
+  private final String exprStr;
+
+  public ConstantFloatValue(float value) {
+    this.value = value;
+    this.valueStr = Float.toString(value);
+    this.exprStr = ConstantValue.createExpressionString(this, valueStr);
+  }
+
+  @Override
+  public float getFloat() {
+    return value;
+  }
+  @Override
+  public double getDouble() {
+    return value;
+  }
+  @Override
+  public String getString() {
+    return valueStr;
+  }
+  @Override
+  public Object getObject() {
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return true;
+  }
+
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    cons.accept(value);
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    cons.accept(value);
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    cons.accept(valueStr);
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    cons.accept(value);
+  }
+
+  @Override
+  public ConstantComparator getObjectComparator(String expression) {
+    return new ConstantComparator();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.CONST;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantIntValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantIntValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantIntValue.java
new file mode 100644
index 0000000..1e43359
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantIntValue.java
@@ -0,0 +1,118 @@
+/*
+ * 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.solr.analytics.value.constant;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.facet.compare.ConstantComparator;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.IntValue;
+import org.apache.solr.analytics.value.IntValue.CastingIntValue;
+
+/**
+ * A constant {@link IntValue}. Every call to {@link #getInt()} and other methods will return the same constant value.
+ */
+public class ConstantIntValue extends ConstantValue implements CastingIntValue {
+  private final int value;
+  private final String valueStr;
+  public static final String name = "const_int";
+  private final String exprStr;
+
+  public ConstantIntValue(int value) {
+    this.value = value;
+    this.valueStr = Integer.toString(value);
+    this.exprStr = ConstantValue.createExpressionString(this, valueStr);
+  }
+
+
+  @Override
+  public int getInt() {
+    return value;
+  }
+  @Override
+  public long getLong() {
+    return value;
+  }
+  @Override
+  public float getFloat() {
+    return value;
+  }
+  @Override
+  public double getDouble() {
+    return value;
+  }
+  @Override
+  public String getString() {
+    return valueStr;
+  }
+  @Override
+  public Object getObject() {
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return true;
+  }
+
+  @Override
+  public void streamInts(IntConsumer cons) {
+    cons.accept(value);
+  }
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    cons.accept(value);
+  }
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    cons.accept(value);
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    cons.accept(value);
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    cons.accept(valueStr);
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    cons.accept(value);
+  }
+
+  @Override
+  public ConstantComparator getObjectComparator(String expression) {
+    return new ConstantComparator();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.CONST;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantLongValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantLongValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantLongValue.java
new file mode 100644
index 0000000..d4c54d0
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantLongValue.java
@@ -0,0 +1,100 @@
+/*
+ * 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.solr.analytics.value.constant;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.facet.compare.ConstantComparator;
+import org.apache.solr.analytics.value.LongValue;
+import org.apache.solr.analytics.value.LongValue.CastingLongValue;
+
+/**
+ * A constant {@link LongValue}. Every call to {@link #getLong()} and other methods will return the same constant value.
+ */
+public class ConstantLongValue extends ConstantValue implements CastingLongValue {
+  private final long value;
+  private final String valueStr;
+  public static final String name = "const_long";
+  private final String exprStr;
+
+  public ConstantLongValue(long value) {
+    this.value = value;
+    this.valueStr = Long.toString(value);
+    this.exprStr = ConstantValue.createExpressionString(this, valueStr);
+  }
+
+
+  @Override
+  public long getLong() {
+    return value;
+  }
+  @Override
+  public double getDouble() {
+    return value;
+  }
+  @Override
+  public String getString() {
+    return valueStr;
+  }
+  @Override
+  public Object getObject() {
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return true;
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    cons.accept(value);
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    cons.accept(value);
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    cons.accept(valueStr);
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    cons.accept(value);
+  }
+
+  @Override
+  public ConstantComparator getObjectComparator(String expression) {
+    return new ConstantComparator();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.CONST;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantStringValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantStringValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantStringValue.java
new file mode 100644
index 0000000..c32a2a3
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantStringValue.java
@@ -0,0 +1,79 @@
+/*
+ * 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.solr.analytics.value.constant;
+
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.facet.compare.ConstantComparator;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValue.CastingStringValue;
+
+/**
+ * A constant {@link StringValue}. Every call to {@link #getString()} and other methods will return the same constant value.
+ */
+public class ConstantStringValue extends ConstantValue implements CastingStringValue {
+  String value;
+  public static final String name = "const_str";
+  private final String exprStr;
+
+  public ConstantStringValue(String value) {
+    this.value = value;
+    this.exprStr = ConstantValue.createExpressionString(this, value);
+  }
+
+  @Override
+  public String getString() {
+    return value;
+  }
+  @Override
+  public Object getObject() {
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return true;
+  }
+
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    cons.accept(value);
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    cons.accept(value);
+  }
+
+  @Override
+  public ConstantComparator getObjectComparator(String expression) {
+    return new ConstantComparator();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.CONST;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantValue.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantValue.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantValue.java
new file mode 100644
index 0000000..2e364b1
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/ConstantValue.java
@@ -0,0 +1,128 @@
+/*
+ * 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.solr.analytics.value.constant;
+
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.solr.analytics.ExpressionFactory.ConstantFunction;
+import org.apache.solr.analytics.value.AnalyticsValue;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * The parent class of all constant Analytics values.
+ * <p>
+ * Constant values can be specified in the following ways in analytics requests:
+ * <ul>
+ * <li> Constant booleans must match one of the following in any case: true, t, false, f
+ * <li> Constant strings must be surrounded with "s or 's
+ * <li> Constant numbers do not have to be surrounded with anything
+ * <li> Constant dates must match one of the following patterns
+ * <ul> 
+ * <li> yyyy-MM-dd
+ * <li> yyyy-MM-ddXXX
+ * <li> yyyy-MM-dd'T'HH:mm:ssZ
+ * <li> yyyy-MM-dd'T'HH:mm:ssXXX
+ * <li> yyyy-MM-dd'T'HH:mm:ss.SSSZ
+ * <li> yyyy-MM-dd'T'HH:mm:ss.SSSXXX
+ * </ul>
+ * </li> 
+ * </ul>
+ */
+public abstract class ConstantValue {
+  private static final Pattern truePattern = Pattern.compile("^true|t$", Pattern.CASE_INSENSITIVE);
+  private static final Pattern falsePattern = Pattern.compile("^false|f$", Pattern.CASE_INSENSITIVE);
+  private static final SimpleDateFormat dateParserBase = new SimpleDateFormat("yyyy-MM-dd", Locale.ROOT);
+  private static final SimpleDateFormat dateParser1 = new SimpleDateFormat("yyyy-MM-ddZ", Locale.ROOT);
+  private static final SimpleDateFormat dateParser2 = new SimpleDateFormat("yyyy-MM-ddXXX", Locale.ROOT);
+  private static final SimpleDateFormat dateParser3 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.ROOT);
+  private static final SimpleDateFormat dateParser4 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX", Locale.ROOT);
+  private static final SimpleDateFormat dateParser5 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ", Locale.ROOT);
+  private static final SimpleDateFormat dateParser6 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.ROOT);
+  
+  public static final ConstantFunction creatorFunction = (param -> {
+    param = param.trim();
+    
+    // Try to create a string
+    if ((param.charAt(0)=='"' && param.charAt(param.length()-1)=='"')
+        || (param.charAt(0)=='\'' && param.charAt(param.length()-1)=='\'')) {
+      return new ConstantStringValue(param.substring(1, param.length()-1));
+    }
+    
+    // Try to create a boolean
+    Matcher m = truePattern.matcher(param);
+    if (m.matches()) {
+      return new ConstantBooleanValue(true);
+    }
+    m = falsePattern.matcher(param);
+    if (m.matches()) {
+      return new ConstantBooleanValue(false);
+    }
+    
+    // Try to create a number
+    try {
+      AnalyticsValue value = new ConstantDoubleValue(Double.parseDouble(param));
+      try {
+        value = new ConstantLongValue(Long.parseLong(param));
+        value = new ConstantIntValue(Integer.parseInt(param));
+      } catch (NumberFormatException e) {
+        value = new ConstantFloatValue(Float.parseFloat(param));
+      }
+      return value;
+    } catch (NumberFormatException e) {
+      // The constant is not a number
+    }
+    
+    // Try to create a date
+    try {
+      AnalyticsValue value = new ConstantDateValue(dateParserBase.parse(param).getTime());
+      try {
+        return new ConstantDateValue(dateParser1.parse(param).getTime());
+      } catch (Exception e) {}
+      try {
+        return new ConstantDateValue(dateParser2.parse(param).getTime());
+      } catch (Exception e) {}
+      try {
+        return new ConstantDateValue(dateParser3.parse(param).getTime());
+      } catch (Exception e) {}
+      try {
+        return new ConstantDateValue(dateParser4.parse(param).getTime());
+      } catch (Exception e) {}
+      try {
+        return new ConstantDateValue(dateParser5.parse(param).getTime());
+      } catch (Exception e) {}
+      try {
+        return new ConstantDateValue(dateParser6.parse(param).getTime());
+      } catch (Exception e) {}
+      return value;
+    } catch (Exception e) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The parameter "+param+" could not be cast to any constant.");
+    }
+    
+  });
+  
+  static String createExpressionString(AnalyticsValueStream func, 
+                                       Object param) {
+    return String.format(Locale.ROOT,"%s(%s)",
+                         func.getName(),
+                         param.toString());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/package-info.java
new file mode 100644
index 0000000..eb6f29d
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/constant/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+ 
+/** 
+ * Constant values to be used in analytics expressions.
+ */
+package org.apache.solr.analytics.value.constant;
+
+


[04/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/package-info.java
new file mode 100644
index 0000000..247b592
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/value/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+ 
+/** 
+ * Value types for analytics expressions.
+ */
+package org.apache.solr.analytics.value;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/handler/AnalyticsHandler.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/handler/AnalyticsHandler.java b/solr/contrib/analytics/src/java/org/apache/solr/handler/AnalyticsHandler.java
new file mode 100644
index 0000000..64eb321
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/handler/AnalyticsHandler.java
@@ -0,0 +1,147 @@
+/*
+ * 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.solr.handler;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import org.apache.lucene.search.MatchNoDocsQuery;
+import org.apache.lucene.search.Query;
+import org.apache.solr.analytics.AnalyticsDriver;
+import org.apache.solr.analytics.AnalyticsRequestManager;
+import org.apache.solr.analytics.AnalyticsRequestParser;
+import org.apache.solr.analytics.ExpressionFactory;
+import org.apache.solr.analytics.stream.AnalyticsShardResponseParser;
+import org.apache.solr.client.solrj.io.ModelCache;
+import org.apache.solr.client.solrj.io.SolrClientCache;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.handler.component.AnalyticsComponent;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.AnalyticsShardResponseWriter;
+import org.apache.solr.response.AnalyticsShardResponseWriter.AnalyticsResponse;
+import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.search.DocSet;
+import org.apache.solr.search.Filter;
+import org.apache.solr.search.QParser;
+import org.apache.solr.search.QParserPlugin;
+import org.apache.solr.search.QueryParsing;
+import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.search.SyntaxError;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.security.PermissionNameProvider;
+import org.apache.solr.util.plugin.SolrCoreAware;
+
+/**
+ * Handler for Analytics shard requests. This handler should only be called by the {@link AnalyticsComponent}
+ * since the response is written in a bit-stream, formatted by the {@link AnalyticsShardResponseWriter}
+ * that can only be read by the {@link AnalyticsShardResponseParser}.
+ */
+public class AnalyticsHandler extends RequestHandlerBase implements SolrCoreAware, PermissionNameProvider {
+  public static final String NAME = "/analytics";
+  private IndexSchema indexSchema;
+
+  static SolrClientCache clientCache = new SolrClientCache();
+  static ModelCache modelCache = null;
+
+  @Override
+  public PermissionNameProvider.Name getPermissionName(AuthorizationContext request) {
+    return PermissionNameProvider.Name.READ_PERM;
+  }
+
+  @Override
+  public void inform(SolrCore core) {
+    core.registerResponseWriter(AnalyticsShardResponseWriter.NAME, new AnalyticsShardResponseWriter());
+    
+    indexSchema = core.getLatestSchema();
+    AnalyticsRequestParser.init();
+  }
+
+  public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
+    try {
+      DocSet docs;
+      try {
+        docs = getDocuments(req);
+      } catch (SyntaxError e) {
+        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, e);
+      }
+      // The olap-style requests are converted to the current format in the AnalyticsComponent
+      // so the AnalyticsHandler only needs to handle current format requests.
+      AnalyticsRequestManager manager = AnalyticsRequestParser.parse(req.getParams().get(AnalyticsRequestParser.analyticsParamName), 
+                                                                new ExpressionFactory(indexSchema),
+                                                                false);
+      // Collect the reduction data for the request
+      SolrIndexSearcher searcher = req.getSearcher();
+      Filter filter = docs.getTopFilter();
+      AnalyticsDriver.drive(manager, searcher, filter, req);
+      
+      // Do not calculate results, instead export the reduction data for this shard.
+      rsp.addResponse(new AnalyticsResponse(manager));
+    } catch (SolrException e) {
+      rsp.addResponse(new AnalyticsResponse(e));
+    }
+  }
+  
+  /**
+   * Get the documents returned by the query and filter queries included in the request.
+   * 
+   * @param req the request sent to the handler
+   * @return the set of documents matching the query
+   * @throws SyntaxError if there is a syntax error in the queries
+   * @throws IOException if an error occurs while searching the index
+   */
+  private DocSet getDocuments(SolrQueryRequest req) throws SyntaxError, IOException {
+    SolrParams params = req.getParams();
+    ArrayList<Query> queries = new ArrayList<>();
+
+    // Query Param
+    String queryString = params.get( CommonParams.Q );
+    
+    String defType = params.get(QueryParsing.DEFTYPE, QParserPlugin.DEFAULT_QTYPE);
+
+    QParser parser = QParser.getParser(queryString, defType, req);
+    Query query = parser.getQuery();
+    if (query == null) {
+      // normalize a null query to a query that matches nothing
+      query = new MatchNoDocsQuery();
+    }
+    queries.add(query);
+
+    // Filter Params
+    String[] fqs = req.getParams().getParams(CommonParams.FQ);
+    if (fqs!=null) {
+      for (String fq : fqs) {
+        if (fq != null && fq.trim().length()!=0) {
+          QParser fqp = QParser.getParser(fq, req);
+          queries.add(fqp.getQuery());
+        }
+      }
+    }
+    return req.getSearcher().getDocSet(queries);
+  }
+
+  @Override
+  public String getDescription() {
+    return NAME;
+  }
+
+  public String getSource() {
+    return null;
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/handler/component/AnalyticsComponent.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/handler/component/AnalyticsComponent.java b/solr/contrib/analytics/src/java/org/apache/solr/handler/component/AnalyticsComponent.java
index 505533b..71c6c92 100644
--- a/solr/contrib/analytics/src/java/org/apache/solr/handler/component/AnalyticsComponent.java
+++ b/solr/contrib/analytics/src/java/org/apache/solr/handler/component/AnalyticsComponent.java
@@ -18,58 +18,128 @@ package org.apache.solr.handler.component;
 
 import java.io.IOException;
 
-import org.apache.solr.analytics.plugin.AnalyticsStatisticsCollector;
-import org.apache.solr.analytics.request.AnalyticsStats;
-import org.apache.solr.analytics.util.AnalyticsParams;
-import org.apache.solr.common.params.SolrParams;
-import org.apache.solr.metrics.MetricsMap;
-import org.apache.solr.metrics.SolrMetricManager;
-import org.apache.solr.metrics.SolrMetricProducer;
+import org.apache.solr.analytics.AnalyticsDriver;
+import org.apache.solr.analytics.AnalyticsRequestManager;
+import org.apache.solr.analytics.AnalyticsRequestParser;
+import org.apache.solr.analytics.ExpressionFactory;
+import org.apache.solr.analytics.stream.AnalyticsShardRequestManager;
+import org.apache.solr.analytics.util.OldAnalyticsParams;
+import org.apache.solr.analytics.util.OldAnalyticsRequestConverter;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
 
-public class AnalyticsComponent extends SearchComponent implements SolrMetricProducer {
+/**
+ * Computes analytics requests.
+ */
+public class AnalyticsComponent extends SearchComponent {
   public static final String COMPONENT_NAME = "analytics";
-  private final AnalyticsStatisticsCollector analyticsCollector = new AnalyticsStatisticsCollector();;
+  
+  @Override
+  public void init(NamedList args) {
+    AnalyticsRequestParser.init();
+  }
 
   @Override
   public void prepare(ResponseBuilder rb) throws IOException {
-    if (rb.req.getParams().getBool(AnalyticsParams.ANALYTICS,false)) {
-      rb.setNeedDocSet( true );
+    // First check to see if there is an analytics request using the current format
+    String analyticsRequest = rb.req.getParams().get(AnalyticsRequestParser.analyticsParamName);
+    rb._isOlapAnalytics = false;
+    rb.doAnalytics = false;
+    if (analyticsRequest != null) {
+      rb.doAnalytics = true;
+      rb._analyticsRequestManager = AnalyticsRequestParser.parse(analyticsRequest, new ExpressionFactory(rb.req.getSchema()), rb.isDistrib);
+    }
+    // If there is no request in the current format, check for the old olap-style format
+    else if (rb.req.getParams().getBool(OldAnalyticsParams.OLD_ANALYTICS,false)) {
+      rb._analyticsRequestManager = AnalyticsRequestParser.parse(OldAnalyticsRequestConverter.convert(rb.req.getParams()), new ExpressionFactory(rb.req.getSchema()), rb.isDistrib);
+      rb._isOlapAnalytics = true;
+      rb.doAnalytics = true;
+    }
+    
+    if (rb.doAnalytics) {
+      AnalyticsRequestManager reqManager = (AnalyticsRequestManager)rb._analyticsRequestManager;
+      // Check to see if the request is distributed
+      if (rb.isDistrib) {
+        reqManager.sendShards = true;
+        reqManager.shardStream = new AnalyticsShardRequestManager(rb.req.getParams(), reqManager);
+      } else {
+        reqManager.sendShards = false;
+        rb.setNeedDocSet( true );
+      }
     }
   }
 
   @Override
   public void process(ResponseBuilder rb) throws IOException {
-    if (rb.req.getParams().getBool(AnalyticsParams.ANALYTICS,false)) {
-      SolrParams params = rb.req.getParams();
-      AnalyticsStats s = new AnalyticsStats(rb.req, rb.getResults().docSet, params, analyticsCollector);
-      rb.rsp.add( "stats", s.execute() );
+    if (!rb.doAnalytics) {
+      return;
     }
+    AnalyticsRequestManager reqManager = (AnalyticsRequestManager)rb._analyticsRequestManager;
+    // Collect the data and generate a response
+    AnalyticsDriver.drive(reqManager, rb.req.getSearcher(), rb.getResults().docSet.getTopFilter(), rb.req);
+    
+    if (rb._isOlapAnalytics) {
+      rb.rsp.add(AnalyticsResponseHeadings.COMPLETED_OLD_HEADER, reqManager.createOldResponse());
+    } else {
+      rb.rsp.add(AnalyticsResponseHeadings.COMPLETED_HEADER, reqManager.createResponse());
+    }
+
+    rb.doAnalytics = false;
   }
   
-  /*
+  
   @Override
   public int distributedProcess(ResponseBuilder rb) throws IOException {
+    if (!rb.doAnalytics || rb.stage != ResponseBuilder.STAGE_EXECUTE_QUERY) {
+      return ResponseBuilder.STAGE_DONE;
+    }
+    AnalyticsRequestManager reqManager = (AnalyticsRequestManager)rb._analyticsRequestManager;
+    if (!reqManager.sendShards){
+      return ResponseBuilder.STAGE_DONE;
+    }
+    
+    // Send out a request to each shard and merge the responses into our AnalyticsRequestManager
+    reqManager.shardStream.sendRequests(rb.req.getCore().getCoreDescriptor().getCollectionName(),
+        rb.req.getCore().getCoreContainer().getZkController().getZkServerAddress());
+    
+    reqManager.sendShards = false;
+    
     return ResponseBuilder.STAGE_DONE;
   }
   
   @Override
   public void modifyRequest(ResponseBuilder rb, SearchComponent who, ShardRequest sreq) {
-    // TODO Auto-generated method stub
+    // We don't want the shard requests to compute analytics, since we send
+    // separate requests for that in distributedProcess() to the AnalyticsHandler
+    sreq.params.remove(AnalyticsRequestParser.analyticsParamName);
+    sreq.params.remove(OldAnalyticsParams.OLD_ANALYTICS);
+    
     super.modifyRequest(rb, who, sreq);
   }
   
   @Override
   public void handleResponses(ResponseBuilder rb, ShardRequest sreq) {
-    // TODO Auto-generated method stub
+    
+    // NO-OP since analytics shard responses are handled through the AnalyticsResponseParser
+    
     super.handleResponses(rb, sreq);
   }
  
   @Override
   public void finishStage(ResponseBuilder rb) {
-    // TODO Auto-generated method stub
+    if (rb.doAnalytics && rb.stage == ResponseBuilder.STAGE_GET_FIELDS) {
+      AnalyticsRequestManager reqManager = (AnalyticsRequestManager)rb._analyticsRequestManager;
+      // Generate responses from the merged shard data
+      if (rb._isOlapAnalytics) {
+        rb.rsp.add(AnalyticsResponseHeadings.COMPLETED_OLD_HEADER, reqManager.createOldResponse());
+      } else {
+        rb.rsp.add(AnalyticsResponseHeadings.COMPLETED_HEADER, reqManager.createResponse());
+      }
+    }
+    
     super.finishStage(rb);
   }
-  */
+  
   
   @Override
   public String getName() {
@@ -81,9 +151,8 @@ public class AnalyticsComponent extends SearchComponent implements SolrMetricPro
     return "Perform analytics";
   }
 
-  @Override
-  public void initializeMetrics(SolrMetricManager manager, String registry, String scope) {
-    MetricsMap metrics = new MetricsMap((detailed, map) -> map.putAll(analyticsCollector.getStatistics()));
-    manager.registerGauge(this, registry, metrics, true, getClass().getSimpleName(), getCategory().toString(), scope);
-  }
+  /*@Override
+  public NamedList getStatistics() {
+    return analyticsCollector.getStatistics();
+  }*/
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/handler/package.html
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/handler/package.html b/solr/contrib/analytics/src/java/org/apache/solr/handler/package.html
new file mode 100644
index 0000000..6a5c2c3
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/handler/package.html
@@ -0,0 +1,28 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<!--
+ 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.
+-->
+<!-- not a package-info.java, because we already defined this package in core/ -->
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+<body>
+<p>
+Handler for distributed analytics requests to shards.
+</p>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/response/AnalyticsShardResponseWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/response/AnalyticsShardResponseWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/response/AnalyticsShardResponseWriter.java
new file mode 100644
index 0000000..52c71ee
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/response/AnalyticsShardResponseWriter.java
@@ -0,0 +1,91 @@
+/*
+ * 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.solr.response;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Writer;
+
+import org.apache.solr.analytics.AnalyticsRequestManager;
+import org.apache.solr.analytics.stream.AnalyticsShardResponseParser;
+import org.apache.solr.client.solrj.impl.BinaryResponseParser;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.request.SolrQueryRequest;
+
+/**
+ * Writes the reduction data of a analytics shard request to a bit-stream to send to the originating shard.
+ * The response must be parsed by the {@link AnalyticsShardResponseParser} initialized with the same analytics request
+ * as the shard request was sent.
+ */
+public class AnalyticsShardResponseWriter implements BinaryQueryResponseWriter {
+  public static final String NAME = "analytics_shard_stream";
+  public static final String ANALYTICS_MANGER = "analyticsManager";
+
+  @Override
+  public void write(OutputStream out, SolrQueryRequest req, SolrQueryResponse response) throws IOException {
+    ((AnalyticsResponse)response.getResponse()).write(new DataOutputStream(out));;
+  }
+
+  @Override
+  public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) throws IOException {
+    throw new RuntimeException("This is a binary writer , Cannot write to a characterstream");
+  }
+
+  @Override
+  public String getContentType(SolrQueryRequest request, SolrQueryResponse response) {
+    return BinaryResponseParser.BINARY_CONTENT_TYPE;
+  }
+
+  @Override
+  public void init(NamedList args) {}
+  
+  /**
+   * Manages the streaming of analytics reduction data if no exception occurred.
+   * Otherwise the exception is streamed over.
+   */
+  public static class AnalyticsResponse {
+    private final AnalyticsRequestManager manager;
+    private final SolrException exception;
+    
+    private final boolean requestSuccessful;
+    
+    public AnalyticsResponse(AnalyticsRequestManager manager) {
+      this.manager = manager;
+      this.exception = null;
+      this.requestSuccessful = true;
+    }
+    
+    public AnalyticsResponse(SolrException exception) {
+      this.manager = null;
+      this.exception = exception;
+      this.requestSuccessful = false;
+    }
+    
+    public void write(DataOutputStream output) throws IOException {
+      output.writeBoolean(requestSuccessful);
+      if (requestSuccessful) {
+        manager.exportShardData(output);
+      } else {
+        new ObjectOutputStream(output).writeObject(exception);
+      }
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/response/package.html
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/response/package.html b/solr/contrib/analytics/src/java/org/apache/solr/response/package.html
new file mode 100644
index 0000000..dd24f82
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/response/package.html
@@ -0,0 +1,28 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<!--
+ 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.
+-->
+<!-- not a package-info.java, because we already defined this package in core/ -->
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+<body>
+<p>
+Response Writer for distributed analytics response from a shard.
+</p>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/analytics/requestFiles/expressions.txt
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/analytics/requestFiles/expressions.txt b/solr/contrib/analytics/src/test-files/analytics/requestFiles/expressions.txt
deleted file mode 100644
index 329d32d..0000000
--- a/solr/contrib/analytics/src/test-files/analytics/requestFiles/expressions.txt
+++ /dev/null
@@ -1,70 +0,0 @@
-o.ar.s.sum=sum(int_id)
-o.ar.s.unique=unique(long_ld)
-o.ar.s.su=add(sum(int_id),unique(long_ld))
-o.ar.s.mean=mean(int_id)
-o.ar.s.count=count(long_ld)
-o.ar.s.median=median(int_id)
-o.ar.s.mcm=add(mean(int_id),count(long_ld),median(int_id))
-
-o.mr.s.sum=sum(int_id)
-o.mr.s.unique=unique(long_ld)
-o.mr.s.su=mult(sum(int_id),unique(long_ld))
-o.mr.s.mean=mean(int_id)
-o.mr.s.count=count(long_ld)
-o.mr.s.median=median(int_id)
-o.mr.s.mcm=mult(mean(int_id),count(long_ld),median(int_id))
-
-o.dr.s.sum=sum(int_id)
-o.dr.s.unique=unique(long_ld)
-o.dr.s.su=div(sum(int_id),unique(long_ld))
-o.dr.s.mean=mean(int_id)
-o.dr.s.count=count(long_ld)
-o.dr.s.mc=div(mean(int_id),count(long_ld))
-
-o.pr.s.sum=sum(int_id)
-o.pr.s.unique=unique(long_ld)
-o.pr.s.su=pow(sum(int_id),unique(long_ld))
-o.pr.s.mean=mean(int_id)
-o.pr.s.count=count(long_ld)
-o.pr.s.mc=pow(mean(int_id),count(long_ld))
-
-o.nr.s.sum=sum(int_id)
-o.nr.s.s=neg(sum(int_id))
-o.nr.s.count=count(long_ld)
-o.nr.s.c=neg(count(long_ld))
-
-o.avr.s.sum=sum(int_id)
-o.avr.s.s=abs(neg(sum(int_id)))
-o.avr.s.count=count(long_ld)
-o.avr.s.c=abs(neg(count(long_ld)))
-
-o.cnr.s.c8=const_num(8)
-o.cnr.s.c10=const_num(10)
-
-o.dmr.s.median=median(date_dtd)
-o.dmr.s.cme=const_str(+2YEARS)
-o.dmr.s.dmme=date_math(median(date_dtd),const_str(+2YEARS))
-o.dmr.s.max=max(date_dtd)
-o.dmr.s.cma=const_str(+2MONTHS)
-o.dmr.s.dmma=date_math(max(date_dtd),const_str(+2MONTHS))
-
-o.cdr.s.cd1=const_date(1800-12-31T23:59:59Z)
-o.cdr.s.cs1=const_str(1800-12-31T23:59:59Z)
-o.cdr.s.cd2=const_date(1804-06-30T23:59:59Z)
-o.cdr.s.cs2=const_str(1804-06-30T23:59:59Z)
-
-o.csr.s.cs1=const_str(this is the first)
-o.csr.s.cs2=const_str(this is the second)
-o.csr.s.cs3=const_str(this is the third)
-
-o.cr.s.csmin=const_str(this is the first)
-o.cr.s.min=min(string_sd)
-o.cr.s.ccmin=concat(const_str(this is the first),min(string_sd))
-o.cr.s.csmax=const_str(this is the second)
-o.cr.s.max=max(string_sd)
-o.cr.s.ccmax=concat(const_str(this is the second),max(string_sd))
-
-o.rr.s.min=min(string_sd)
-o.rr.s.rmin=rev(min(string_sd))
-o.rr.s.max=max(string_sd)
-o.rr.s.rmax=rev(max(string_sd))

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/analytics/requestFiles/fieldFacetExtras.txt
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/analytics/requestFiles/fieldFacetExtras.txt b/solr/contrib/analytics/src/test-files/analytics/requestFiles/fieldFacetExtras.txt
deleted file mode 100644
index 3979f57..0000000
--- a/solr/contrib/analytics/src/test-files/analytics/requestFiles/fieldFacetExtras.txt
+++ /dev/null
@@ -1,66 +0,0 @@
-o.sr.s.mean=mean(int_id)
-o.sr.s.median=median(int_id)
-o.sr.s.count=count(int_id)
-o.sr.s.percentile_20=percentile(20,int_id)
-o.sr.ff=long_ld
-o.sr.ff.long_ld.ss=mean
-o.sr.ff.long_ld.sd=asc
-o.sr.ff=float_fd
-o.sr.ff.float_fd.ss=median
-o.sr.ff.float_fd.sd=desc
-o.sr.ff=double_dd
-o.sr.ff.double_dd.ss=count
-o.sr.ff.double_dd.sd=asc
-o.sr.ff=string_sd
-o.sr.ff.string_sd.ss=percentile_20
-o.sr.ff.string_sd.sd=desc
-
-o.lr.s.mean=mean(int_id)
-o.lr.s.median=median(int_id)
-o.lr.s.count=count(int_id)
-o.lr.s.percentile_20=percentile(20,int_id)
-o.lr.ff=long_ld
-o.lr.ff.long_ld.ss=mean
-o.lr.ff.long_ld.sd=asc
-o.lr.ff.long_ld.limit=5
-o.lr.ff=float_fd
-o.lr.ff.float_fd.ss=median
-o.lr.ff.float_fd.sd=desc
-o.lr.ff.float_fd.limit=3
-o.lr.ff=double_dd
-o.lr.ff.double_dd.ss=count
-o.lr.ff.double_dd.sd=asc
-o.lr.ff.double_dd.limit=7
-o.lr.ff=string_sd
-o.lr.ff.string_sd.ss=percentile_20
-o.lr.ff.string_sd.sd=desc
-o.lr.ff.string_sd.limit=1
-
-
-
-o.offAll.s.mean=mean(int_id)
-o.offAll.ff=long_ld
-o.offAll.ff.long_ld.ss=mean
-o.offAll.ff.long_ld.sd=asc
-o.offAll.ff.long_ld.limit=7
-
-o.off0.s.mean=mean(int_id)
-o.off0.ff=long_ld
-o.off0.ff.long_ld.ss=mean
-o.off0.ff.long_ld.sd=asc
-o.off0.ff.long_ld.limit=2
-o.off0.ff.long_ld.offset=0
-
-o.off1.s.mean=mean(int_id)
-o.off1.ff=long_ld
-o.off1.ff.long_ld.ss=mean
-o.off1.ff.long_ld.sd=asc
-o.off1.ff.long_ld.limit=2
-o.off1.ff.long_ld.offset=2
-
-o.off2.s.mean=mean(int_id)
-o.off2.ff=long_ld
-o.off2.ff.long_ld.ss=mean
-o.off2.ff.long_ld.sd=asc
-o.off2.ff.long_ld.limit=3
-o.off2.ff.long_ld.offset=4

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/analytics/requestFiles/fieldFacets.txt
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/analytics/requestFiles/fieldFacets.txt b/solr/contrib/analytics/src/test-files/analytics/requestFiles/fieldFacets.txt
deleted file mode 100644
index 5ba5953..0000000
--- a/solr/contrib/analytics/src/test-files/analytics/requestFiles/fieldFacets.txt
+++ /dev/null
@@ -1,132 +0,0 @@
-o.sum.s.int=sum(int_id)
-o.sum.s.long=sum(long_ld)
-o.sum.s.float=sum(float_fd)
-o.sum.s.double=sum(double_dd)
-o.sum.ff=string_sd
-o.sum.ff=date_dtd
-
-o.mean.s.int=mean(int_id)
-o.mean.s.long=mean(long_ld)
-o.mean.s.float=mean(float_fd)
-o.mean.s.double=mean(double_dd)
-o.mean.ff=string_sd
-o.mean.ff=date_dtd
-
-o.sumOfSquares.s.int=sumofsquares(int_id)
-o.sumOfSquares.s.long=sumofsquares(long_ld)
-o.sumOfSquares.s.float=sumofsquares(float_fd)
-o.sumOfSquares.s.double=sumofsquares(double_dd)
-o.sumOfSquares.ff=string_sd
-o.sumOfSquares.ff=date_dtd
-
-o.stddev.s.int=stddev(int_id)
-o.stddev.s.long=stddev(long_ld)
-o.stddev.s.float=stddev(float_fd)
-o.stddev.s.double=stddev(double_dd)
-o.stddev.ff=string_sd
-o.stddev.ff=date_dtd
-
-o.median.s.int=median(int_id)
-o.median.s.long=median(long_ld)
-o.median.s.float=median(float_fd)
-o.median.s.double=median(double_dd)
-o.median.ff=string_sd
-o.median.ff=date_dtd
-
-o.percentile_20n.s.int=percentile(20,int_id)
-o.percentile_20n.s.long=percentile(20,long_ld)
-o.percentile_20n.s.float=percentile(20,float_fd)
-o.percentile_20n.s.double=percentile(20,double_dd)
-o.percentile_20n.ff=string_sd
-o.percentile_20n.ff=date_dtd
-
-o.percentile_20.s.str=percentile(20,string_sd)
-o.percentile_20.s.date=percentile(20,date_dtd)
-o.percentile_20.ff=int_id
-o.percentile_20.ff=long_ld
-
-o.percentile_60n.s.int=percentile(60,int_id)
-o.percentile_60n.s.long=percentile(60,long_ld)
-o.percentile_60n.s.float=percentile(60,float_fd)
-o.percentile_60n.s.double=percentile(60,double_dd)
-o.percentile_60n.ff=string_sd
-o.percentile_60n.ff=date_dtd
-
-o.percentile_60.s.str=percentile(60,string_sd)
-o.percentile_60.s.date=percentile(60,date_dtd)
-o.percentile_60.ff=int_id
-o.percentile_60.ff=long_ld
-
-o.minn.s.int=min(int_id)
-o.minn.s.long=min(long_ld)
-o.minn.s.float=min(float_fd)
-o.minn.s.double=min(double_dd)
-o.minn.ff=string_sd
-o.minn.ff=date_dtd
-
-o.min.s.str=min(string_sd)
-o.min.s.date=min(date_dtd)
-o.min.ff=int_id
-o.min.ff=long_ld
-
-o.maxn.s.int=max(int_id)
-o.maxn.s.long=max(long_ld)
-o.maxn.s.float=max(float_fd)
-o.maxn.s.double=max(double_dd)
-o.maxn.ff=string_sd
-o.maxn.ff=date_dtd
-
-o.max.s.str=max(string_sd)
-o.max.s.date=max(date_dtd)
-o.max.ff=int_id
-o.max.ff=long_ld
-
-o.countn.s.int=count(int_id)
-o.countn.s.long=count(long_ld)
-o.countn.s.float=count(float_fd)
-o.countn.s.double=count(double_dd)
-o.countn.ff=string_sd
-o.countn.ff=date_dtd
-
-o.count.s.str=count(string_sd)
-o.count.s.date=count(date_dtd)
-o.count.ff=int_id
-o.count.ff=long_ld
-
-o.uniquen.s.int=unique(int_id)
-o.uniquen.s.long=unique(long_ld)
-o.uniquen.s.float=unique(float_fd)
-o.uniquen.s.double=unique(double_dd)
-o.uniquen.ff=string_sd
-o.uniquen.ff=date_dtd
-
-o.unique.s.str=unique(string_sd)
-o.unique.s.date=unique(date_dtd)
-o.unique.ff=int_id
-o.unique.ff=long_ld
-
-o.missingn.s.int=missing(int_id)
-o.missingn.s.long=missing(long_ld)
-o.missingn.s.float=missing(float_fd)
-o.missingn.s.double=missing(double_dd)
-o.missingn.ff=string_sd
-o.missingn.ff=date_dtd
-
-o.missing.s.str=missing(string_sd)
-o.missing.s.date=missing(date_dtd)
-o.missing.ff=int_id
-o.missing.ff=long_ld
-
-o.multivalued.s.mean=mean(int_id)
-o.multivalued.ff=long_ldm
-o.multivalued.ff=string_sdm
-o.multivalued.ff=date_dtdm
-
-o.missingf.s.mean=mean(int_id)
-o.missingf.ff=date_dtd
-o.missingf.ff.date_dtd.dim=true
-o.missingf.ff=string_sd
-o.missingf.ff.string_sd.dim=true
-o.missingf.ff.string_sd.sm=true
-o.missingf.ff=date_dtdm
-o.missingf.ff.date_dtdm.sm=true

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/analytics/requestFiles/functions.txt
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/analytics/requestFiles/functions.txt b/solr/contrib/analytics/src/test-files/analytics/requestFiles/functions.txt
deleted file mode 100644
index e4930b6..0000000
--- a/solr/contrib/analytics/src/test-files/analytics/requestFiles/functions.txt
+++ /dev/null
@@ -1,62 +0,0 @@
-o.ar.s.sum=sum(add(int_id,float_fd))
-o.ar.s.sumc=sum(add_if_dd)
-o.ar.s.mean=mean(add(long_ld,double_dd,float_fd))
-o.ar.s.meanc=mean(add_ldf_dd)
-
-o.mr.s.sum=sum(mult(int_id,float_fd))
-o.mr.s.sumc=sum(mult_if_dd)
-o.mr.s.mean=mean(mult(long_ld,double_dd,float_fd))
-o.mr.s.meanc=mean(mult_ldf_dd)
-
-o.dr.s.sum=sum(div(int_id,float_fd))
-o.dr.s.sumc=sum(div_if_dd)
-o.dr.s.mean=mean(div(long_ld,double_dd))
-o.dr.s.meanc=mean(div_ld_dd)
-
-o.pr.s.sum=sum(pow(int_id,float_fd))
-o.pr.s.sumc=sum(pow_if_dd)
-o.pr.s.mean=mean(pow(long_ld,double_dd))
-o.pr.s.meanc=mean(pow_ld_dd)
-
-o.nr.s.sum=sum(neg(int_id))
-o.nr.s.sumc=sum(neg_i_dd)
-o.nr.s.mean=mean(neg(long_ld))
-o.nr.s.meanc=mean(neg_l_dd)
-
-o.avr.s.sum=sum(abs(neg(int_id)))
-o.avr.s.sumc=sum(int_id)
-o.avr.s.mean=mean(abs(neg(int_id)))
-o.avr.s.meanc=mean(int_id)
-
-o.cnr.s.sum=sum(const_num(8))
-o.cnr.s.sumc=sum(const_8_dd)
-o.cnr.s.mean=mean(const_num(10))
-o.cnr.s.meanc=mean(const_10_dd)
-
-o.dmr.s.median=median(date_math(date_dtd,const_str(+2YEARS)))
-o.dmr.s.medianc=median(dm_2y_dtd)
-o.dmr.s.max=max(date_math(date_dtd,const_str(+2MONTHS)))
-o.dmr.s.maxc=max(dm_2m_dtd)
-
-o.cdr.s.median=median(const_date(1800-06-30T23:59:59Z))
-o.cdr.s.medianc=median(const_00_dtd)
-o.cdr.s.max=max(const_date(1804-06-30T23:59:59Z))
-o.cdr.s.maxc=max(const_04_dtd)
-
-o.csr.s.min=min(const_str(this is the first))
-o.csr.s.minc=min(const_first_sd)
-o.csr.s.max=max(const_str(this is the second))
-o.csr.s.maxc=max(const_second_sd)
-
-o.cr.s.min=min(concat(const_str(this is the first),string_sd))
-o.cr.s.minc=min(concat_first_sd)
-o.cr.s.max=max(concat(const_str(this is the second),string_sd))
-o.cr.s.maxc=max(concat_second_sd)
-
-o.rr.s.min=min(rev(string_sd))
-o.rr.s.minc=min(rev_sd)
-o.rr.s.max=max(rev(string_sd))
-o.rr.s.maxc=max(rev_sd)
-
-o.ms.s.min=min(miss_dd)
-o.ms.s.max=max(miss_dd)

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/analytics/requestFiles/noFacets.txt
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/analytics/requestFiles/noFacets.txt b/solr/contrib/analytics/src/test-files/analytics/requestFiles/noFacets.txt
deleted file mode 100644
index b3d9163..0000000
--- a/solr/contrib/analytics/src/test-files/analytics/requestFiles/noFacets.txt
+++ /dev/null
@@ -1,74 +0,0 @@
-o.sr.s.int_id=sum(int_i)
-o.sr.s.long_ld=sum(long_l)
-o.sr.s.float_fd=sum(float_f)
-o.sr.s.double_dd=sum(double_d)
-
-o.sosr.s.int_id=sumofsquares(int_id)
-o.sosr.s.long_ld=sumofsquares(long_ld)
-o.sosr.s.float_fd=sumofsquares(float_fd)
-o.sosr.s.double_dd=sumofsquares(double_dd)
-
-o.mr.s.int_id=mean(int_id)
-o.mr.s.long_ld=mean(long_ld)
-o.mr.s.float_fd=mean(float_fd)
-o.mr.s.double_dd=mean(double_dd)
-
-o.str.s.int_id=stddev(int_id)
-o.str.s.long_ld=stddev(long_ld)
-o.str.s.float_fd=stddev(float_fd)
-o.str.s.double_dd=stddev(double_dd)
-
-o.medr.s.int_id=median(int_id)
-o.medr.s.long_ld=median(long_ld)
-o.medr.s.float_fd=median(float_fd)
-o.medr.s.double_dd=median(double_dd)
-o.medr.s.date_dtd=median(date_dtd)
-
-o.p2r.s.int_id=percentile(20,int_id)
-o.p2r.s.long_ld=percentile(20,long_ld)
-o.p2r.s.float_fd=percentile(20,float_fd)
-o.p2r.s.double_dd=percentile(20,double_dd)
-o.p2r.s.date_dtd=percentile(20,date_dtd)
-o.p2r.s.string_sd=percentile(20,string_sd)
-
-o.p6r.s.int_id=percentile(60,int_id)
-o.p6r.s.long_ld=percentile(60,long_ld)
-o.p6r.s.float_fd=percentile(60,float_fd)
-o.p6r.s.double_dd=percentile(60,double_dd)
-o.p6r.s.date_dtd=percentile(60,date_dtd)
-o.p6r.s.string_sd=percentile(60,string_sd)
-
-o.mir.s.int_id=min(int_id)
-o.mir.s.long_ld=min(long_ld)
-o.mir.s.float_fd=min(float_fd)
-o.mir.s.double_dd=min(double_dd)
-o.mir.s.date_dtd=min(date_dtd)
-o.mir.s.string_sd=min(string_sd)
-
-o.mar.s.int_id=max(int_id)
-o.mar.s.long_ld=max(long_ld)
-o.mar.s.float_fd=max(float_fd)
-o.mar.s.double_dd=max(double_dd)
-o.mar.s.date_dtd=max(date_dtd)
-o.mar.s.string_sd=max(string_sd)
-
-o.cr.s.int_id=count(int_id)
-o.cr.s.long_ld=count(long_ld)
-o.cr.s.float_fd=count(float_fd)
-o.cr.s.double_dd=count(double_dd)
-o.cr.s.date_dtd=count(date_dtd)
-o.cr.s.string_sd=count(string_sd)
-
-o.ur.s.int_id=unique(int_id)
-o.ur.s.long_ld=unique(long_ld)
-o.ur.s.float_fd=unique(float_fd)
-o.ur.s.double_dd=unique(double_dd)
-o.ur.s.date_dtd=unique(date_dtd)
-o.ur.s.string_sd=unique(string_sd)
-
-o.misr.s.int_id=missing(int_id)
-o.misr.s.long_ld=missing(long_ld)
-o.misr.s.float_fd=missing(float_fd)
-o.misr.s.double_dd=missing(double_dd)
-o.misr.s.date_dtd=missing(date_dtd)
-o.misr.s.string_sd=missing(string_sd)

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/analytics/requestFiles/queryFacets.txt
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/analytics/requestFiles/queryFacets.txt b/solr/contrib/analytics/src/test-files/analytics/requestFiles/queryFacets.txt
deleted file mode 100644
index 6be4a4e..0000000
--- a/solr/contrib/analytics/src/test-files/analytics/requestFiles/queryFacets.txt
+++ /dev/null
@@ -1,45 +0,0 @@
-o.ir.s.sum=sum(int_id)
-o.ir.s.mean=mean(int_id)
-o.ir.s.median=median(int_id)
-o.ir.s.percentile_8=percentile(8,int_id)
-o.ir.ff=string_sd
-o.ir.ff.string_sd.h=true
-o.ir.qf=float1
-o.ir.qf.float1.q=float_fd:[* TO 50]
-o.ir.qf=float2
-o.ir.qf.float2.q=float_fd:[* TO 30]
-
-o.pr.s.sum=sum(int_id)
-o.pr.s.mean=mean(int_id)
-o.pr.s.median=median(int_id)
-o.pr.s.q1=concat(const_str(float_fd:[), percentile(10,int_id), const_str( TO ), median(int_id), const_str(]))
-o.pr.hs.q2=concat(const_str(float_fd:[), percentile(30,int_id), const_str( TO ), median(int_id), const_str(]))
-o.pr.hs.q3=concat(const_str(float_fd:[), percentile(40,int_id), const_str( TO ), median(int_id), const_str(]))
-o.pr.s.percentile_8=percentile(8,int_id)
-o.pr.ff=string_sd
-o.pr.ff.string_sd.h=true
-o.pr.qf=float3
-o.pr.qf.float3.q=result(q1)
-o.pr.qf.float3.q=result(q2)
-o.pr.qf.float3.q=result(q3)
-o.pr.qf.float3.q=result(q1,string_sd,abc2)
-o.pr.qf=float4
-o.pr.qf.float4.d=float3
-o.pr.qf.float4.q=qresult(q1,float3,result(q1))
-
-o.lr.s.sum=sum(long_ld)
-o.lr.s.mean=mean(long_ld)
-o.lr.s.median=median(long_ld)
-o.lr.s.percentile_8=percentile(8,long_ld)
-o.lr.qf=string
-o.lr.qf.string.q=string_sd:abc1
-o.lr.qf.string.q=string_sd:abc2
-
-o.fr.s.sum=sum(float_fd)
-o.fr.s.mean=mean(float_fd)
-o.fr.s.median=median(float_fd)
-o.fr.s.percentile_8=percentile(8,float_fd)
-o.fr.qf=lad
-o.fr.qf.lad.q=long_ld:[20 TO *]
-o.fr.qf.lad.q=long_ld:[30 TO *]
-o.fr.qf.lad.q=double_dd:[* TO 50]

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/analytics/requestFiles/rangeFacets.txt
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/analytics/requestFiles/rangeFacets.txt b/solr/contrib/analytics/src/test-files/analytics/requestFiles/rangeFacets.txt
deleted file mode 100644
index cbfe052..0000000
--- a/solr/contrib/analytics/src/test-files/analytics/requestFiles/rangeFacets.txt
+++ /dev/null
@@ -1,170 +0,0 @@
-o.ri.s.sum=sum(int_id)
-o.ri.s.mean=mean(int_id)
-o.ri.s.median=median(int_id)
-o.ri.s.count=count(int_id)
-o.ri.s.sumOfSquares=sumofsquares(int_id)
-o.ri.rf=long_ld
-o.ri.rf.long_ld.st=5
-o.ri.rf.long_ld.e=30
-o.ri.rf.long_ld.g=5
-o.ri.rf.long_ld.ib=lower
-o.ri.rf.long_ld.or=all
-o.ri.rf=double_dd
-o.ri.rf.double_dd.st=3
-o.ri.rf.double_dd.e=39
-o.ri.rf.double_dd.g=7
-o.ri.rf.double_dd.ib=upper
-o.ri.rf.double_dd.ib=outer
-o.ri.rf.double_dd.or=all
-o.ri.rf=date_dtd
-o.ri.rf.date_dtd.st=1007-01-01T23:59:59Z
-o.ri.rf.date_dtd.e=1044-01-01T23:59:59Z
-o.ri.rf.date_dtd.g=+7YEARS
-o.ri.rf.date_dtd.ib=lower
-o.ri.rf.date_dtd.ib=edge
-o.ri.rf.date_dtd.ib=outer
-o.ri.rf.date_dtd.or=all
-
-o.rf.s.sum=sum(float_fd)
-o.rf.s.mean=mean(float_fd)
-o.rf.s.median=median(float_fd)
-o.rf.s.count=count(float_fd)
-o.rf.s.sumOfSquares=sumofsquares(float_fd)
-o.rf.rf=long_ld
-o.rf.rf.long_ld.st=0
-o.rf.rf.long_ld.e=29
-o.rf.rf.long_ld.g=4
-o.rf.rf.long_ld.ib=all
-o.rf.rf.long_ld.or=all
-o.rf.rf=double_dd
-o.rf.rf.double_dd.st=4
-o.rf.rf.double_dd.e=47
-o.rf.rf.double_dd.g=11
-o.rf.rf.double_dd.ib=edge
-o.rf.rf.double_dd.or=all
-o.rf.rf=date_dtd
-o.rf.rf.date_dtd.st=1004-01-01T23:59:59Z
-o.rf.rf.date_dtd.e=1046-01-01T23:59:59Z
-o.rf.rf.date_dtd.g=+5YEARS
-o.rf.rf.date_dtd.ib=upper
-o.rf.rf.date_dtd.ib=edge
-o.rf.rf.date_dtd.or=all
-
-o.hi.s.sum=sum(int_id)
-o.hi.s.mean=mean(int_id)
-o.hi.s.median=median(int_id)
-o.hi.s.count=count(int_id)
-o.hi.s.sumOfSquares=sumofsquares(int_id)
-o.hi.rf=long_ld
-o.hi.rf.long_ld.st=5
-o.hi.rf.long_ld.e=30
-o.hi.rf.long_ld.g=5
-o.hi.rf.long_ld.he=true
-o.hi.rf.long_ld.ib=lower
-o.hi.rf.long_ld.or=all
-o.hi.rf=double_dd
-o.hi.rf.double_dd.st=3
-o.hi.rf.double_dd.e=39
-o.hi.rf.double_dd.g=7
-o.hi.rf.double_dd.he=true
-o.hi.rf.double_dd.ib=upper
-o.hi.rf.double_dd.ib=outer
-o.hi.rf.double_dd.or=all
-o.hi.rf=date_dtd
-o.hi.rf.date_dtd.st=1007-01-01T23:59:59Z
-o.hi.rf.date_dtd.e=1044-01-01T23:59:59Z
-o.hi.rf.date_dtd.g=+7YEARS
-o.hi.rf.date_dtd.he=true
-o.hi.rf.date_dtd.ib=lower
-o.hi.rf.date_dtd.ib=edge
-o.hi.rf.date_dtd.ib=outer
-o.hi.rf.date_dtd.or=all
-
-o.hf.s.sum=sum(float_fd)
-o.hf.s.mean=mean(float_fd)
-o.hf.s.median=median(float_fd)
-o.hf.s.count=count(float_fd)
-o.hf.s.sumOfSquares=sumofsquares(float_fd)
-o.hf.rf=long_ld
-o.hf.rf.long_ld.st=0
-o.hf.rf.long_ld.e=29
-o.hf.rf.long_ld.g=4
-o.hf.rf.long_ld.he=true
-o.hf.rf.long_ld.ib=all
-o.hf.rf.long_ld.or=all
-o.hf.rf=double_dd
-o.hf.rf.double_dd.st=4
-o.hf.rf.double_dd.e=47
-o.hf.rf.double_dd.g=11
-o.hf.rf.double_dd.he=true
-o.hf.rf.double_dd.ib=edge
-o.hf.rf.double_dd.or=all
-o.hf.rf=date_dtd
-o.hf.rf.date_dtd.st=1004-01-01T23:59:59Z
-o.hf.rf.date_dtd.e=1046-01-01T23:59:59Z
-o.hf.rf.date_dtd.g=+5YEARS
-o.hf.rf.date_dtd.he=true
-o.hf.rf.date_dtd.ib=upper
-o.hf.rf.date_dtd.ib=edge
-o.hf.rf.date_dtd.or=all
-
-o.mi.s.sum=sum(int_id)
-o.mi.s.mean=mean(int_id)
-o.mi.s.median=median(int_id)
-o.mi.s.count=count(int_id)
-o.mi.s.sumOfSquares=sumofsquares(int_id)
-o.mi.rf=long_ld
-o.mi.rf.long_ld.st=5
-o.mi.rf.long_ld.e=30
-o.mi.rf.long_ld.g=4,2,6,3
-o.mi.rf.long_ld.ib=lower
-o.mi.rf.long_ld.or=all
-o.mi.rf=double_dd
-o.mi.rf.double_dd.st=3
-o.mi.rf.double_dd.e=39
-o.mi.rf.double_dd.g=3,1,7
-o.mi.rf.double_dd.ib=upper
-o.mi.rf.double_dd.ib=outer
-o.mi.rf.double_dd.or=all
-o.mi.rf=date_dtd
-o.mi.rf.date_dtd.st=1007-01-01T23:59:59Z
-o.mi.rf.date_dtd.e=1044-01-01T23:59:59Z
-o.mi.rf.date_dtd.g=+2YEARS,+7YEARS
-o.mi.rf.date_dtd.ib=lower
-o.mi.rf.date_dtd.ib=edge
-o.mi.rf.date_dtd.ib=outer
-o.mi.rf.date_dtd.or=all
-
-o.mf.s.sum=sum(float_fd)
-o.mf.s.mean=mean(float_fd)
-o.mf.s.median=median(float_fd)
-o.mf.s.count=count(float_fd)
-o.mf.s.sumOfSquares=sumofsquares(float_fd)
-o.mf.rf=long_ld
-o.mf.rf.long_ld.st=0
-o.mf.rf.long_ld.e=29
-o.mf.rf.long_ld.g=1,4
-o.mf.rf.long_ld.ib=all
-o.mf.rf.long_ld.or=all
-o.mf.rf=double_dd
-o.mf.rf.double_dd.st=4
-o.mf.rf.double_dd.e=47
-o.mf.rf.double_dd.g=2,3,11
-o.mf.rf.double_dd.ib=edge
-o.mf.rf.double_dd.or=all
-o.mf.rf=date_dtd
-o.mf.rf.date_dtd.st=1004-01-01T23:59:59Z
-o.mf.rf.date_dtd.e=1046-01-01T23:59:59Z
-o.mf.rf.date_dtd.g=+4YEARS,+5YEARS
-o.mf.rf.date_dtd.ib=upper
-o.mf.rf.date_dtd.ib=edge
-o.mf.rf.date_dtd.or=all
-
-o.pf.s.mean=mean(float_fd)
-o.pf.hs.min=min(date_dtd)
-o.pf.hs.max=max(date_dtd)
-o.pf.hs.gap=const_str(+5YEARS)
-o.pf.rf=date_dtd
-o.pf.rf.date_dtd.st=result(min)
-o.pf.rf.date_dtd.e=result(max)
-o.pf.rf.date_dtd.g=result(gap)

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/expressions.xml
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/expressions.xml b/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/expressions.xml
deleted file mode 100644
index 2ce8abf..0000000
--- a/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/expressions.xml
+++ /dev/null
@@ -1,285 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<analyticsRequestEnvelope stats="true" olap="true">
-   <analyticsRequest>
-     <name>Add Request</name>
-     
-     <statistic>
-       <expression>sum(int(int_id))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>unique(long(long_ld))</expression>
-       <name>unique</name>
-     </statistic>
-     <statistic>
-       <expression>add(sum(int(int_id)),unique(long(long_ld)))</expression>
-       <name>add sum and unique</name>
-     </statistic>
-     
-     <statistic>
-       <expression>mean(int(int_id))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>count(long(long_ld))</expression>
-       <name>count</name>
-     </statistic>
-     <statistic>
-       <expression>median(int(int_id))</expression>
-       <name>median</name>
-     </statistic>
-     <statistic>
-       <expression>add(mean(int(int_id)),count(long(long_ld)),median(int(int_id)))</expression>
-       <name>add mean and count and median</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Multiply Request</name>
-     
-     <statistic>
-       <expression>sum(int(int_id))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>unique(long(long_ld))</expression>
-       <name>unique</name>
-     </statistic>
-     <statistic>
-       <expression>mult(sum(int(int_id)),unique(long(long_ld)))</expression>
-       <name>multiply sum and unique</name>
-     </statistic>
-     
-     <statistic>
-       <expression>mean(int(int_id))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>count(long(long_ld))</expression>
-       <name>count</name>
-     </statistic>
-     <statistic>
-       <expression>median(int(int_id))</expression>
-       <name>median</name>
-     </statistic>
-     <statistic>
-       <expression>mult(mean(int(int_id)),count(long(long_ld)),median(int(int_id)))</expression>
-       <name>multiply mean and count and median</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Divide Request</name>
-     
-     <statistic>
-       <expression>sum(int(int_id))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>unique(long(long_ld))</expression>
-       <name>unique</name>
-     </statistic>
-     <statistic>
-       <expression>div(sum(int(int_id)),unique(long(long_ld)))</expression>
-       <name>divide sum by unique</name>
-     </statistic>
-     
-     <statistic>
-       <expression>mean(int(int_id))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>count(long(long_ld))</expression>
-       <name>count</name>
-     </statistic>
-     <statistic>
-       <expression>div(mean(int(int_id)),count(long(long_ld)))</expression>
-       <name>divide mean by count</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Power Request</name>
-     
-     <statistic>
-       <expression>sum(int(int_id))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>unique(long(long_ld))</expression>
-       <name>unique</name>
-     </statistic>
-     <statistic>
-       <expression>pow(sum(int(int_id)),unique(long(long_ld)))</expression>
-       <name>power sum by unique</name>
-     </statistic>
-     
-     <statistic>
-       <expression>mean(int(int_id))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>count(long(long_ld))</expression>
-       <name>count</name>
-     </statistic>
-     <statistic>
-       <expression>pow(mean(int(int_id)),count(long(long_ld)))</expression>
-       <name>power mean by count</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Negate Request</name>
-     
-     <statistic>
-       <expression>sum(int(int_id))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>neg(sum(int(int_id)))</expression>
-       <name>negate of sum</name>
-     </statistic>
-     
-     <statistic>
-       <expression>count(long(long_ld))</expression>
-       <name>count</name>
-     </statistic>
-     <statistic>
-       <expression>neg(count(long(long_ld)))</expression>
-       <name>negate of count</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Const Num Request</name>
-     
-     <statistic>
-       <expression>const_num(8)</expression>
-       <name>constant 8</name>
-     </statistic>
-     <statistic>
-       <expression>const_num(10)</expression>
-       <name>constant 10</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Date Math Request</name>
-     
-     <statistic>
-       <expression>median(date(date_dtd))</expression>
-       <name>median</name>
-     </statistic>
-     <statistic>
-       <expression>const_str(+2YEARS)</expression>
-       <name>constant str median</name>
-     </statistic>
-     <statistic>
-       <expression>date_math(median(date(date_dtd)),const_str(+2YEARS))</expression>
-       <name>date math median</name>
-     </statistic>
-     
-     <statistic>
-       <expression>max(date(date_dtd))</expression>
-       <name>max</name>
-     </statistic>
-     <statistic>
-       <expression>const_str(+2MONTHS)</expression>
-       <name>constant str max</name>
-     </statistic>
-     <statistic>
-       <expression>date_math(max(date(date_dtd)),const_str(+2MONTHS))</expression>
-       <name>date math max</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Constant Date Request</name>
-     
-     <statistic>
-       <expression>const_str(1800-12-31T23:59:59Z)</expression>
-       <name>const str 1</name>
-     </statistic>
-     <statistic>
-       <expression>const_date(1800-12-31T23:59:59Z)</expression>
-       <name>const date 1</name>
-     </statistic>
-     <statistic>
-       <expression>const_str(1804-06-30T23:59:59Z)</expression>
-       <name>const str 2</name>
-     </statistic>
-     <statistic>
-       <expression>const_date(1804-06-30T23:59:59Z)</expression>
-       <name>const date 2</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Constant String Request</name>
-     
-     <statistic>
-       <expression>const_str(this is the first)</expression>
-       <name>const str 1</name>
-     </statistic>
-     <statistic>
-       <expression>const_str(this is the second)</expression>
-       <name>const str 2</name>
-     </statistic>
-     <statistic>
-       <expression>const_str(this is the third)</expression>
-       <name>const str 3</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Concatenate Request</name>
-     
-     <statistic>
-       <expression>const_str(this is the first)</expression>
-       <name>const str min</name>
-     </statistic>
-     <statistic>
-       <expression>min(str(string_sd))</expression>
-       <name>min</name>
-     </statistic>
-     <statistic>
-       <expression>concat(const_str(this is the first),min(str(string_sd)))</expression>
-       <name>concat const and min</name>
-     </statistic>
-     
-     <statistic>
-       <expression>const_str(this is the second)</expression>
-       <name>const str max</name>
-     </statistic>
-     <statistic>
-       <expression>max(str(string_sd))</expression>
-       <name>max</name>
-     </statistic>
-     <statistic>
-       <expression>concat(const_str(this is the second),max(str(string_sd)))</expression>
-       <name>concat const and max</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Reverse Request</name>
-     
-     <statistic>
-       <expression>min(str(string_sd))</expression>
-       <name>min</name>
-     </statistic>
-     <statistic>
-       <expression>rev(min(str(string_sd)))</expression>
-       <name>reverse min</name>
-     </statistic>
-     
-     <statistic>
-       <expression>max(str(string_sd))</expression>
-       <name>max</name>
-     </statistic>
-     <statistic>
-       <expression>rev(max(str(string_sd)))</expression>
-       <name>reverse max</name>
-     </statistic>
-   </analyticsRequest>
-</analyticsRequestEnvelope> 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/fieldFacetExtras.xml
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/fieldFacetExtras.xml b/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/fieldFacetExtras.xml
deleted file mode 100644
index 68b5be9..0000000
--- a/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/fieldFacetExtras.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<analyticsRequestEnvelope stats="true" olap="true">
-   <analyticsRequest>
-     <name>sort request</name>
-     
-     <statistic>
-       <expression>mean(int(int_id))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>median(int(int_id))</expression>
-       <name>median</name>
-     </statistic>
-     <statistic>
-       <expression>count(int(int_id))</expression>
-       <name>count</name>
-     </statistic>
-     <statistic>
-       <expression>perc(20,int(int_id))</expression>
-       <name>perc_20</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>long_ld</field>
-       <sortSpecification>
-         <statName>mean</statName>
-         <direction>asc</direction>
-       </sortSpecification>
-     </fieldFacet>
-     <fieldFacet>
-       <field>float_fd</field>
-       <sortSpecification>
-         <statName>median</statName>
-         <direction>desc</direction>
-       </sortSpecification>
-     </fieldFacet>
-     <fieldFacet>
-       <field>double_dd</field>
-       <sortSpecification>
-         <statName>count</statName>
-         <direction>asc</direction>
-       </sortSpecification>
-     </fieldFacet>
-     <fieldFacet>
-       <field>string_sd</field>
-       <sortSpecification>
-         <statName>perc_20</statName>
-         <direction>desc</direction>
-       </sortSpecification>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>limit request</name>
-     
-     <statistic>
-       <expression>mean(int(int_id))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>median(int(int_id))</expression>
-       <name>median</name>
-     </statistic>
-     <statistic>
-       <expression>count(int(int_id))</expression>
-       <name>count</name>
-     </statistic>
-     <statistic>
-       <expression>perc(20,int(int_id))</expression>
-       <name>perc_20</name>
-     </statistic>
-     
-     <fieldFacet limit="5">
-       <field>long_ld</field>
-       <sortSpecification>
-         <statName>mean</statName>
-         <direction>asc</direction>
-       </sortSpecification>
-     </fieldFacet>
-     <fieldFacet limit="3">
-       <field>float_fd</field>
-       <sortSpecification>
-         <statName>median</statName>
-         <direction>desc</direction>
-       </sortSpecification>
-     </fieldFacet>
-     <fieldFacet limit="7">
-       <field>double_dd</field>
-       <sortSpecification>
-         <statName>count</statName>
-         <direction>asc</direction>
-       </sortSpecification>
-     </fieldFacet>
-     <fieldFacet limit="1">
-       <field>string_sd</field>
-       <sortSpecification>
-         <statName>perc_20</statName>
-         <direction>desc</direction>
-       </sortSpecification>
-     </fieldFacet>
-   </analyticsRequest>
-</analyticsRequestEnvelope>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/fieldFacets.xml
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/fieldFacets.xml b/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/fieldFacets.xml
deleted file mode 100644
index 8e84cf1..0000000
--- a/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/fieldFacets.xml
+++ /dev/null
@@ -1,496 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<analyticsRequestEnvelope stats="true" olap="true">
-   <analyticsRequest>
-     <name>sum</name>
-     
-     <statistic>
-       <expression>sum(int(int_id))</expression>
-       <name>int</name>
-     </statistic>
-     <statistic>
-       <expression>sum(long(long_ld))</expression>
-       <name>long</name>
-     </statistic>
-     <statistic>
-       <expression>sum(float(float_fd))</expression>
-       <name>float</name>
-     </statistic>
-     <statistic>
-       <expression>sum(double(double_dd))</expression>
-       <name>double</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>string_sd</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>date_dtd</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>mean</name>
-     
-     <statistic>
-       <expression>mean(int(int_id))</expression>
-       <name>int</name>
-     </statistic>
-     <statistic>
-       <expression>mean(long(long_ld))</expression>
-       <name>long</name>
-     </statistic>
-     <statistic>
-       <expression>mean(float(float_fd))</expression>
-       <name>float</name>
-     </statistic>
-     <statistic>
-       <expression>mean(double(double_dd))</expression>
-       <name>double</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>string_sd</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>date_dtd</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>sumOfSquares</name>
-     
-     <statistic>
-       <expression>sumofsquares(int(int_id))</expression>
-       <name>int</name>
-     </statistic>
-     <statistic>
-       <expression>sumofsquares(long(long_ld))</expression>
-       <name>long</name>
-     </statistic>
-     <statistic>
-       <expression>sumofsquares(float(float_fd))</expression>
-       <name>float</name>
-     </statistic>
-     <statistic>
-       <expression>sumofsquares(double(double_dd))</expression>
-       <name>double</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>string_sd</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>date_dtd</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>stddev</name>
-     
-     <statistic>
-       <expression>stddev(int(int_id))</expression>
-       <name>int</name>
-     </statistic>
-     <statistic>
-       <expression>stddev(long(long_ld))</expression>
-       <name>long</name>
-     </statistic>
-     <statistic>
-       <expression>stddev(float(float_fd))</expression>
-       <name>float</name>
-     </statistic>
-     <statistic>
-       <expression>stddev(double(double_dd))</expression>
-       <name>double</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>string_sd</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>date_dtd</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>median</name>
-     
-     <statistic>
-       <expression>median(int(int_id))</expression>
-       <name>int</name>
-     </statistic>
-     <statistic>
-       <expression>median(long(long_ld))</expression>
-       <name>long</name>
-     </statistic>
-     <statistic>
-       <expression>median(float(float_fd))</expression>
-       <name>float</name>
-     </statistic>
-     <statistic>
-       <expression>median(double(double_dd))</expression>
-       <name>double</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>string_sd</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>date_dtd</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>perc_20 numeric</name>
-     
-     <statistic>
-       <expression>perc(20,int(int_id))</expression>
-       <name>int</name>
-     </statistic>
-     <statistic>
-       <expression>perc(20,long(long_ld))</expression>
-       <name>long</name>
-     </statistic>
-     <statistic>
-       <expression>perc(20,float(float_fd))</expression>
-       <name>float</name>
-     </statistic>
-     <statistic>
-       <expression>perc(20,double(double_dd))</expression>
-       <name>double</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>string_sd</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>date_dtd</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>perc_20</name>
-     
-     <statistic>
-       <expression>perc(20,str(string_sd))</expression>
-       <name>str</name>
-     </statistic>
-     <statistic>
-       <expression>perc(20,date(date_dtd))</expression>
-       <name>date</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>int_id</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>long_ld</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>perc_60 numeric</name>
-     
-     <statistic>
-       <expression>perc(60,int(int_id))</expression>
-       <name>int</name>
-     </statistic>
-     <statistic>
-       <expression>perc(60,long(long_ld))</expression>
-       <name>long</name>
-     </statistic>
-     <statistic>
-       <expression>perc(60,float(float_fd))</expression>
-       <name>float</name>
-     </statistic>
-     <statistic>
-       <expression>perc(60,double(double_dd))</expression>
-       <name>double</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>string_sd</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>date_dtd</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>perc_60</name>
-     
-     <statistic>
-       <expression>perc(60,str(string_sd))</expression>
-       <name>str</name>
-     </statistic>
-     <statistic>
-       <expression>perc(60,date(date_dtd))</expression>
-       <name>date</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>int_id</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>long_ld</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>min numeric</name>
-     
-     <statistic>
-       <expression>min(int(int_id))</expression>
-       <name>int</name>
-     </statistic>
-     <statistic>
-       <expression>min(long(long_ld))</expression>
-       <name>long</name>
-     </statistic>
-     <statistic>
-       <expression>min(float(float_fd))</expression>
-       <name>float</name>
-     </statistic>
-     <statistic>
-       <expression>min(double(double_dd))</expression>
-       <name>double</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>string_sd</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>date_dtd</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>min</name>
-     
-     <statistic>
-       <expression>min(str(string_sd))</expression>
-       <name>str</name>
-     </statistic>
-     <statistic>
-       <expression>min(date(date_dtd))</expression>
-       <name>date</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>int_id</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>long_ld</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>max numeric</name>
-     
-     <statistic>
-       <expression>max(int(int_id))</expression>
-       <name>int</name>
-     </statistic>
-     <statistic>
-       <expression>max(long(long_ld))</expression>
-       <name>long</name>
-     </statistic>
-     <statistic>
-       <expression>max(float(float_fd))</expression>
-       <name>float</name>
-     </statistic>
-     <statistic>
-       <expression>max(double(double_dd))</expression>
-       <name>double</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>string_sd</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>date_dtd</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>max</name>
-     
-     <statistic>
-       <expression>max(str(string_sd))</expression>
-       <name>str</name>
-     </statistic>
-     <statistic>
-       <expression>max(date(date_dtd))</expression>
-       <name>date</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>int_id</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>long_ld</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>count numeric</name>
-     
-     <statistic>
-       <expression>count(int(int_id))</expression>
-       <name>int</name>
-     </statistic>
-     <statistic>
-       <expression>count(long(long_ld))</expression>
-       <name>long</name>
-     </statistic>
-     <statistic>
-       <expression>count(float(float_fd))</expression>
-       <name>float</name>
-     </statistic>
-     <statistic>
-       <expression>count(double(double_dd))</expression>
-       <name>double</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>string_sd</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>date_dtd</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>count</name>
-     
-     <statistic>
-       <expression>count(str(string_sd))</expression>
-       <name>str</name>
-     </statistic>
-     <statistic>
-       <expression>count(date(date_dtd))</expression>
-       <name>date</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>int_id</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>long_ld</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>unique numeric</name>
-     
-     <statistic>
-       <expression>unique(int(int_id))</expression>
-       <name>int</name>
-     </statistic>
-     <statistic>
-       <expression>unique(long(long_ld))</expression>
-       <name>long</name>
-     </statistic>
-     <statistic>
-       <expression>unique(float(float_fd))</expression>
-       <name>float</name>
-     </statistic>
-     <statistic>
-       <expression>unique(double(double_dd))</expression>
-       <name>double</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>string_sd</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>date_dtd</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>unique</name>
-     
-     <statistic>
-       <expression>unique(str(string_sd))</expression>
-       <name>str</name>
-     </statistic>
-     <statistic>
-       <expression>unique(date(date_dtd))</expression>
-       <name>date</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>int_id</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>long_ld</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>missing numeric</name>
-     
-     <statistic>
-       <expression>missing(int{int_id})</expression>
-       <name>int</name>
-     </statistic>
-     <statistic>
-       <expression>missing(long{long_ld})</expression>
-       <name>long</name>
-     </statistic>
-     <statistic>
-       <expression>missing(float{float_fd})</expression>
-       <name>float</name>
-     </statistic>
-     <statistic>
-       <expression>missing(double{double_dd})</expression>
-       <name>double</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>string_sd</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>date_dtd</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>missing</name>
-     
-     <statistic>
-       <expression>missing(str{string_sd})</expression>
-       <name>str</name>
-     </statistic>
-     <statistic>
-       <expression>missing(date{date_dtd})</expression>
-       <name>date</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>int_id</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>long_ld</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>multivalued</name>
-     
-     <statistic>
-       <expression>mean(int(int_id))</expression>
-       <name>mean</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>long_ldm</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>string_sdm</field>
-     </fieldFacet>
-     <fieldFacet>
-       <field>date_dtdm</field>
-     </fieldFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>missing facet</name>
-
-     <statistic>
-       <expression>mean(int(int_id))</expression>
-       <name>mean</name>
-     </statistic>
-     
-     <fieldFacet>
-       <field>date_dtd</field>
-     </fieldFacet>
-     <fieldFacet showMissing="true">
-       <field>string_sd</field>
-     </fieldFacet>
-     <fieldFacet showMissing="true">
-       <field>date_dtdm</field>
-     </fieldFacet>
-   </analyticsRequest>
-</analyticsRequestEnvelope>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/functions.xml
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/functions.xml b/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/functions.xml
deleted file mode 100644
index 8fa92b6..0000000
--- a/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/functions.xml
+++ /dev/null
@@ -1,246 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<analyticsRequestEnvelope stats="true" olap="true">
-   <analyticsRequest>
-     <name>Add Request</name>
-     
-     <statistic>
-       <expression>sum(add(int(int_id),float(float_fd)))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>sum(double(add_if_dd))</expression>
-       <name>sum calced</name>
-     </statistic>
-     
-     <statistic>
-       <expression>mean(add(long(long_ld),double(double_dd),float(float_fd)))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>mean(double(add_ldf_dd))</expression>
-       <name>mean calced</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Multiply Request</name>
-     
-     <statistic>
-       <expression>sum(mult(int(int_id),float(float_fd)))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>sum(double(mult_if_dd))</expression>
-       <name>sum calced</name>
-     </statistic>
-     
-     <statistic>
-       <expression>mean(mult(long(long_ld),double(double_dd),float(float_fd)))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>mean(double(mult_ldf_dd))</expression>
-       <name>mean calced</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Divide Request</name>
-     
-     <statistic>
-       <expression>sum(div(int(int_id),float(float_fd)))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>sum(double(div_if_dd))</expression>
-       <name>sum calced</name>
-     </statistic>
-     
-     <statistic>
-       <expression>mean(div(long(long_ld),double(double_dd)))</expression>
-       <name>mean</name>
-     </statistic>
-     
-     <statistic>
-       <expression>mean(double(div_ld_dd))</expression>
-       <name>mean calced</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Power Request</name>
-     
-     <statistic>
-       <expression>sum(pow(int(int_id),float(float_fd))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>sum(double(pow_if_dd))</expression>
-       <name>sum calced</name>
-     </statistic>
-     
-     <statistic>
-       <expression>mean(pow(long(long_ld),double(double_dd)))</expression>
-       <name>mean</name>
-     </statistic>
-     
-     <statistic>
-       <expression>mean(double(pow_ld_dd))</expression>
-       <name>mean calced</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Negate Request</name>
-     
-     <statistic>
-       <expression>sum(neg(int(int_id)))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>sum(double(neg_i_dd))</expression>
-       <name>sum calced</name>
-     </statistic>
-     
-     <statistic>
-       <expression>mean(neg(long(long_ld)))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>mean(double(neg_l_dd))</expression>
-       <name>mean calced</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Const Num Request</name>
-     
-     <statistic>
-       <expression>sum(const_num(8))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>sum(double(const_8_dd))</expression>
-       <name>sum calced</name>
-     </statistic>
-     
-     <statistic>
-       <expression>mean(const_num(10))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>mean(double(const_10_dd))</expression>
-       <name>mean calced</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Date Math Request</name>
-     
-     <statistic>
-       <expression>median(date_math(date(date_dtd),const_str(+2YEARS)))</expression>
-       <name>median</name>
-     </statistic>
-     <statistic>
-       <expression>median(date(dm_2y_dtd))</expression>
-       <name>median calced</name>
-     </statistic>
-     
-     <statistic>
-       <expression>max(date_math(date(date_dtd),const_str(+2MONTHS)))</expression>
-       <name>max</name>
-     </statistic>
-     <statistic>
-       <expression>max(date(dm_2m_dtd))</expression>
-       <name>max calced</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Constant Date Request</name>
-     
-     <statistic>
-       <expression>median(const_date(1800-06-30T23:59:59Z))</expression>
-       <name>median</name>
-     </statistic>
-     <statistic>
-       <expression>median(date(const_00_dtd))</expression>
-       <name>median calced</name>
-     </statistic>
-     
-     <statistic>
-       <expression>max(const_date(1804-06-30T23:59:59Z))</expression>
-       <name>max</name>
-     </statistic>
-     <statistic>
-       <expression>max(date(const_04_dtd))</expression>
-       <name>max calced</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Constant String Request</name>
-     
-     <statistic>
-       <expression>min(const_str(this is the first))</expression>
-       <name>min</name>
-     </statistic>
-     <statistic>
-       <expression>min(str(const_first_sd))</expression>
-       <name>min calced</name>
-     </statistic>
-     
-     <statistic>
-       <expression>max(const_str(this is the second))</expression>
-       <name>max</name>
-     </statistic>
-     <statistic>
-       <expression>max(str(const_second_sd))</expression>
-       <name>max calced</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Concatenate Request</name>
-     
-     <statistic>
-       <expression>min(concat(const_str(this is the first),str(string_sd)))</expression>
-       <name>min</name>
-     </statistic>
-     <statistic>
-       <expression>min(str(concat_first_sd))</expression>
-       <name>min calced</name>
-     </statistic>
-     
-     <statistic>
-       <expression>max(concat(const_str(this is the second),str(string_sd)))</expression>
-       <name>max</name>
-     </statistic>
-     <statistic>
-       <expression>max(str(concat_second_sd))</expression>
-       <name>max calced</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Reverse Request</name>
-     
-     <statistic>
-       <expression>min(rev(str(string_sd)))</expression>
-       <name>min</name>
-     </statistic>
-     <statistic>
-       <expression>min(str(rev_sd))</expression>
-       <name>min calced</name>
-     </statistic>
-     
-     <statistic>
-       <expression>max(rev(str(string_sd)))</expression>
-       <name>max</name>
-     </statistic>
-     <statistic>
-       <expression>max(str(rev_sd))</expression>
-       <name>max calced</name>
-     </statistic>
-   </analyticsRequest>
-</analyticsRequestEnvelope> 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/noFacets.xml
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/noFacets.xml b/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/noFacets.xml
deleted file mode 100644
index 2813d18..0000000
--- a/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/noFacets.xml
+++ /dev/null
@@ -1,310 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<analyticsRequestEnvelope stats="true" olap="true">
-   <analyticsRequest>
-     <name>Sum Request</name>
-     
-     <statistic>
-       <expression>sum(int(int_id))</expression>
-       <name>int_id</name>
-     </statistic>
-     <statistic>
-       <expression>sum(long(long_ld))</expression>
-       <name>long_ld</name>
-     </statistic>
-     <statistic>
-       <expression>sum(float(float_fd))</expression>
-       <name>float_fd</name>
-     </statistic>
-     <statistic>
-       <expression>sum(double(double_dd))</expression>
-       <name>double_dd</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>SumOfSquares Request</name>
-     
-     <statistic>
-       <expression>sumofsquares(int(int_id))</expression>
-       <name>int_id</name>
-     </statistic>
-     <statistic>
-       <expression>sumofsquares(long(long_ld))</expression>
-       <name>long_ld</name>
-     </statistic>
-     <statistic>
-       <expression>sumofsquares(float(float_fd))</expression>
-       <name>float_fd</name>
-     </statistic>
-     <statistic>
-       <expression>sumofsquares(double(double_dd))</expression>
-       <name>double_dd</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Mean Request</name>
-     
-     <statistic>
-       <expression>mean(int(int_id))</expression>
-       <name>int_id</name>
-     </statistic>
-     <statistic>
-       <expression>mean(long(long_ld))</expression>
-       <name>long_ld</name>
-     </statistic>
-     <statistic>
-       <expression>mean(float(float_fd))</expression>
-       <name>float_fd</name>
-     </statistic>
-     <statistic>
-       <expression>mean(double(double_dd))</expression>
-       <name>double_dd</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Stddev Request</name>
-     
-     <statistic>
-       <expression>stddev(int(int_id))</expression>
-       <name>int_id</name>
-     </statistic>
-     <statistic>
-       <expression>stddev(long(long_ld))</expression>
-       <name>long_ld</name>
-     </statistic>
-     <statistic>
-       <expression>stddev(float(float_fd))</expression>
-       <name>float_fd</name>
-     </statistic>
-     <statistic>
-       <expression>stddev(double(double_dd))</expression>
-       <name>double_dd</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Median Request</name>
-     
-     <statistic>
-       <expression>median(int(int_id))</expression>
-       <name>int_id</name>
-     </statistic>
-     <statistic>
-       <expression>median(long(long_ld))</expression>
-       <name>long_ld</name>
-     </statistic>
-     <statistic>
-       <expression>median(float(float_fd))</expression>
-       <name>float_fd</name>
-     </statistic>
-     <statistic>
-       <expression>median(double(double_dd))</expression>
-       <name>double_dd</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Perc 20 Request</name>
-     
-     <statistic>
-       <expression>perc(20,int(int_id))</expression>
-       <name>int_id</name>
-     </statistic>
-     <statistic>
-       <expression>perc(20,long(long_ld))</expression>
-       <name>long_ld</name>
-     </statistic>
-     <statistic>
-       <expression>perc(20,float(float_fd))</expression>
-       <name>float_fd</name>
-     </statistic>
-     <statistic>
-       <expression>perc(20,double(double_dd))</expression>
-       <name>double_dd</name>
-     </statistic>
-     <statistic>
-       <expression>perc(20,date(date_dtd))</expression>
-       <name>date_dtd</name>
-     </statistic>
-     <statistic>
-       <expression>perc(20,str(string_sd))</expression>
-       <name>string_sd</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Perc 60 Request</name>
-     
-     <statistic>
-       <expression>perc(60,int(int_id))</expression>
-       <name>int_id</name>
-     </statistic>
-     <statistic>
-       <expression>perc(60,long(long_ld))</expression>
-       <name>long_ld</name>
-     </statistic>
-     <statistic>
-       <expression>perc(60,float(float_fd))</expression>
-       <name>float_fd</name>
-     </statistic>
-     <statistic>
-       <expression>perc(60,double(double_dd))</expression>
-       <name>double_dd</name>
-     </statistic>
-     <statistic>
-       <expression>perc(60,date(date_dtd))</expression>
-       <name>date_dtd</name>
-     </statistic>
-     <statistic>
-       <expression>perc(60,str(string_sd))</expression>
-       <name>string_sd</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Min Request</name>
-     
-     <statistic>
-       <expression>min(int(int_id))</expression>
-       <name>int_id</name>
-     </statistic>
-     <statistic>
-       <expression>min(long(long_ld))</expression>
-       <name>long_ld</name>
-     </statistic>
-     <statistic>
-       <expression>min(float(float_fd))</expression>
-       <name>float_fd</name>
-     </statistic>
-     <statistic>
-       <expression>min(double(double_dd))</expression>
-       <name>double_dd</name>
-     </statistic>
-     <statistic>
-       <expression>min(date(date_dtd))</expression>
-       <name>date_dtd</name>
-     </statistic>
-     <statistic>
-       <expression>min(str(string_sd))</expression>
-       <name>string_sd</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Max Request</name>
-     
-     <statistic>
-       <expression>max(int(int_id))</expression>
-       <name>int_id</name>
-     </statistic>
-     <statistic>
-       <expression>max(long(long_ld))</expression>
-       <name>long_ld</name>
-     </statistic>
-     <statistic>
-       <expression>max(float(float_fd))</expression>
-       <name>float_fd</name>
-     </statistic>
-     <statistic>
-       <expression>max(double(double_dd))</expression>
-       <name>double_dd</name>
-     </statistic>
-     <statistic>
-       <expression>max(date(date_dtd))</expression>
-       <name>date_dtd</name>
-     </statistic>
-     <statistic>
-       <expression>max(str(string_sd))</expression>
-       <name>string_sd</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Unique Request</name>
-     
-     <statistic>
-       <expression>unique(int(int_id))</expression>
-       <name>int_id</name>
-     </statistic>
-     <statistic>
-       <expression>unique(long(long_ld))</expression>
-       <name>long_ld</name>
-     </statistic>
-     <statistic>
-       <expression>unique(float(float_fd))</expression>
-       <name>float_fd</name>
-     </statistic>
-     <statistic>
-       <expression>unique(double(double_dd))</expression>
-       <name>double_dd</name>
-     </statistic>
-     <statistic>
-       <expression>unique(date(date_dtd))</expression>
-       <name>date_dtd</name>
-     </statistic>
-     <statistic>
-       <expression>unique(str(string_sd))</expression>
-       <name>string_sd</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Count Request</name>
-     
-     <statistic>
-       <expression>count(int(int_id))</expression>
-       <name>int_id</name>
-     </statistic>
-     <statistic>
-       <expression>count(long(long_ld))</expression>
-       <name>long_ld</name>
-     </statistic>
-     <statistic>
-       <expression>count(float(float_fd))</expression>
-       <name>float_fd</name>
-     </statistic>
-     <statistic>
-       <expression>count(double(double_dd))</expression>
-       <name>double_dd</name>
-     </statistic>
-     <statistic>
-       <expression>count(date(date_dtd))</expression>
-       <name>date_dtd</name>
-     </statistic>
-     <statistic>
-       <expression>count(str(string_sd))</expression>
-       <name>string_sd</name>
-     </statistic>
-   </analyticsRequest>
-   
-   <analyticsRequest>
-     <name>Missing Request</name>
-          
-     <statistic>
-       <expression>missing(int{int_id})</expression>
-       <name>int_id</name>
-     </statistic>
-     <statistic>
-       <expression>missing(long{long_ld})</expression>
-       <name>long_ld</name>
-     </statistic>
-     <statistic>
-       <expression>missing(float{float_fd})</expression>
-       <name>float_fd</name>
-     </statistic>
-     <statistic>
-       <expression>missing(double{double_dd})</expression>
-       <name>double_dd</name>
-     </statistic>
-     <statistic>
-       <expression>missing(date{date_dtd})</expression>
-       <name>date_dtd</name>
-     </statistic>
-     <statistic>
-       <expression>missing(str{string_sd})</expression>
-       <name>string_sd</name>
-     </statistic>
-   </analyticsRequest>
-</analyticsRequestEnvelope> 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/queryFacets.xml
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/queryFacets.xml b/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/queryFacets.xml
deleted file mode 100644
index f5c7191..0000000
--- a/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/queryFacets.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<analyticsRequestEnvelope stats="true" olap="true">
-   <analyticsRequest>
-     <name>int request</name>
-     
-     <statistic>
-       <expression>sum(int(int_id))</expression>
-       <name>sum</name>
-     </statistic>
-     
-     <statistic>
-       <expression>mean(int(int_id))</expression>
-       <name>mean</name>
-     </statistic>
-     
-     <statistic>
-       <expression>median(int(int_id))</expression>
-       <name>median</name>
-     </statistic>
-     
-     <statistic>
-       <expression>perc(8,int(int_id))</expression>
-       <name>perc_8</name>
-     </statistic>
-     
-     <queryFacet>
-       <name>float1</name>
-       <query>float_fd:[* TO 50]</query>
-     </queryFacet>
-     <queryFacet>
-       <name>float2</name>
-       <query>float_fd:[* TO 30]</query>
-     </queryFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>long request</name>
-     
-     <statistic>
-       <expression>sum(long(long_ld))</expression>
-       <name>sum</name>
-     </statistic>
-     
-     <statistic>
-       <expression>mean(long(long_ld))</expression>
-       <name>mean</name>
-     </statistic>
-     
-     <statistic>
-       <expression>median(long(long_ld))</expression>
-       <name>median</name>
-     </statistic>
-     
-     <statistic>
-       <expression>perc(8,long(long_ld))</expression>
-       <name>perc_8</name>
-     </statistic>
-     
-     <queryFacet>
-       <name>string</name>
-       <query>string_sd:abc1</query>
-       <query>string_sd:abc2</query>
-     </queryFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>float request</name>
-     
-     <statistic>
-       <expression>sum(float(float_fd))</expression>
-       <name>sum</name>
-     </statistic>
-     
-     <statistic>
-       <expression>mean(float(float_fd))</expression>
-       <name>mean</name>
-     </statistic>
-     
-     <statistic>
-       <expression>median(float(float_fd))</expression>
-       <name>median</name>
-     </statistic>
-     
-     <statistic>
-       <expression>perc(8,float(float_fd))</expression>
-       <name>perc_8</name>
-     </statistic>
-     
-     <queryFacet>
-       <name>long and double</name>
-       <query>long_ld:[20 TO *]</query>
-       <query>long_ld:[30 TO *]</query>
-       <query>double_dd:[* TO 50]</query>
-     </queryFacet>
-   </analyticsRequest>
-</analyticsRequestEnvelope>


[19/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/ExpressionFactory.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/ExpressionFactory.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/ExpressionFactory.java
new file mode 100644
index 0000000..b59469f
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/ExpressionFactory.java
@@ -0,0 +1,821 @@
+/*
+ * 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.solr.analytics;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.solr.analytics.function.MergingReductionCollectionManager;
+import org.apache.solr.analytics.function.ReductionCollectionManager;
+import org.apache.solr.analytics.function.mapping.*;
+import org.apache.solr.analytics.function.mapping.CompareFunction.EqualFunction;
+import org.apache.solr.analytics.function.mapping.CompareFunction.GTEFunction;
+import org.apache.solr.analytics.function.mapping.CompareFunction.GTFunction;
+import org.apache.solr.analytics.function.mapping.CompareFunction.LTEFunction;
+import org.apache.solr.analytics.function.mapping.CompareFunction.LTFunction;
+import org.apache.solr.analytics.function.mapping.ConcatFunction.ConcatSeparatedFunction;
+import org.apache.solr.analytics.function.mapping.NumericConvertFunction.CeilingFunction;
+import org.apache.solr.analytics.function.mapping.NumericConvertFunction.FloorFunction;
+import org.apache.solr.analytics.function.mapping.NumericConvertFunction.RoundFunction;
+import org.apache.solr.analytics.function.mapping.LogicFunction.AndFunction;
+import org.apache.solr.analytics.function.mapping.LogicFunction.OrFunction;
+import org.apache.solr.analytics.function.reduction.*;
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.analytics.value.*;
+import org.apache.solr.analytics.value.AnalyticsValueStream.ExpressionType;
+import org.apache.solr.analytics.value.constant.ConstantValue;
+import org.apache.solr.analytics.function.field.*;
+import org.apache.solr.analytics.function.ReductionFunction;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.schema.BoolField;
+import org.apache.solr.schema.DatePointField;
+import org.apache.solr.schema.DoublePointField;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.FloatPointField;
+import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.schema.IntPointField;
+import org.apache.solr.schema.LongPointField;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.schema.StrField;
+import org.apache.solr.schema.TrieDateField;
+import org.apache.solr.schema.TrieDoubleField;
+import org.apache.solr.schema.TrieFloatField;
+import org.apache.solr.schema.TrieIntField;
+import org.apache.solr.schema.TrieLongField;
+
+/**
+ * A factory to parse and create expressions, and capture information about those expressions along the way.
+ * 
+ * <p>
+ * In order to use, first call {@link #startRequest()} and create all ungrouped expressions,
+ * then call {@link #createReductionManager} to get the ungrouped reduction manager.
+ * <br>
+ * Then for each grouping call {@link #startGrouping()} first then create all expressions within that grouping,
+ * finally calling {@link #createGroupingReductionManager}  to get the reduction manager for that grouping.
+ */
+public class ExpressionFactory {
+  private static final Pattern functionNamePattern = Pattern.compile("^\\s*([^().\\s]+)\\s*(?:\\(.*\\)\\s*)?$", Pattern.CASE_INSENSITIVE);
+  private static final Pattern functionParamsPattern = Pattern.compile("^\\s*(?:[^(.)]+)\\s*\\(\\s*(.+)\\s*\\)\\s*$", Pattern.CASE_INSENSITIVE);
+  private static final String funtionVarParamUniqueName = ".%s_(%d)";
+
+  /**
+   * Used to denote a variable length parameter.
+   */
+  public final static String variableLengthParamSuffix = "..";
+  /**
+   * The character used to denote the start of a for each lambda expression
+   */
+  public final static char variableForEachSep = ':';
+  /**
+   * The character used to denote the looped parameter in the for each lambda expression
+   */
+  public final static char variableForEachParam = '_';
+
+  private HashMap<String, VariableFunctionInfo> systemVariableFunctions;
+  private HashMap<String, VariableFunctionInfo> variableFunctions;
+  private HashSet<String> variableFunctionNameHistory;
+  
+  private HashMap<String, CreatorFunction> expressionCreators;
+  private final ConstantFunction constantCreator;
+  
+  private LinkedHashMap<String, ReductionFunction> reductionFunctions;
+  private LinkedHashMap<String, ReductionDataCollector<?>> collectors;
+  private LinkedHashMap<String, AnalyticsField> fields;
+  private HashMap<String, AnalyticsValueStream> expressions;
+  
+  private IndexSchema schema;
+  
+  private Map<String, ReductionDataCollector<?>> groupedCollectors;
+  private Map<String, AnalyticsField> groupedFields;
+  private boolean isGrouped;
+
+  public ExpressionFactory(IndexSchema schema) {
+    this.schema = schema;
+    
+    expressionCreators = new HashMap<>();
+    systemVariableFunctions = new HashMap<>();
+    
+    constantCreator = ConstantValue.creatorFunction;
+    addSystemFunctions();
+  }
+  
+  /**
+   * Get the index schema used by this factory.
+   * 
+   * @return the index schema
+   */
+  public IndexSchema getSchema() {
+    return schema;
+  }
+  
+  /**
+   * Prepare the factory to start building the request.
+   */
+  public void startRequest() {
+    reductionFunctions = new LinkedHashMap<>();
+    collectors = new LinkedHashMap<>();
+    fields = new LinkedHashMap<>();
+    expressions = new HashMap<>();
+
+    variableFunctions = new HashMap<>();
+    variableFunctions.putAll(systemVariableFunctions);
+    variableFunctionNameHistory = new HashSet<>();
+    
+    isGrouped = false;
+  }
+  
+  /**
+   * Prepare the factory to start building the next grouping.
+   * <br>
+   * NOTE: MUST be called before each new grouping.
+   */
+  public void startGrouping() {
+    groupedCollectors = new HashMap<>();
+    groupedFields = new HashMap<>();
+    
+    isGrouped = true;
+  }
+
+  /**
+   * Add a system function to the expression factory.
+   * This will be treated as a native function and not a variable function.
+   * 
+   * @param functionName the unique name for the function
+   * @param functionCreator the creator function to generate an expression
+   * @return this factory, to easily chain function adds
+   * @throws SolrException if the functionName is not unique
+   */
+  public ExpressionFactory addSystemFunction(final String functionName, final CreatorFunction functionCreator) throws SolrException {
+    if (expressionCreators.containsKey(functionName)) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"System function " + functionName + " defined twice.");
+    }
+    expressionCreators.put(CountFunction.name, CountFunction.creatorFunction);
+    return this;
+  }
+
+  /**
+   * Add a variable function that will be treated like a system function.
+   * 
+   * @param functionSignature the function signature of the variable function (e.g. {@code func(a,b)} )
+   * @param returnSignature the return signature of the variable function (e.g. {@code div(sum(a,b),count(b))} )
+   * @return this factory, to easily chain function adds
+   * @throws SolrException if the name of the function is not unique or the syntax of either signature is incorrect
+   */
+  public ExpressionFactory addSystemVariableFunction(final String functionSignature, final String returnSignature) throws SolrException {
+    return addVariableFunction(functionSignature, returnSignature, systemVariableFunctions);
+  }
+
+  /**
+   * Add a variable function that was defined in an analytics request.
+   * 
+   * @param functionSignature the function signature of the variable function (e.g. {@code func(a,b)} )
+   * @param returnSignature the return signature of the variable function (e.g. {@code div(sum(a,b),count(b))} )
+   * @return this factory, to easily chain function adds
+   * @throws SolrException if the name of the function is not unique or the syntax of either signature is incorrect
+   */
+  public ExpressionFactory addUserDefinedVariableFunction(final String functionSignature, final String returnSignature) throws SolrException {
+    return addVariableFunction(functionSignature, returnSignature, variableFunctions);
+  }
+  
+  /**
+   * Add a variable function to the given map of variable functions. 
+   * 
+   * @param functionSignature the function signature of the variable function (e.g. {@code func(a,b)} )
+   * @param returnSignature the return signature of the variable function (e.g. {@code div(sum(a,b),count(b))} )
+   * @param variableFunctions the map of variable functions to add the new function to
+   * @return this factory, to easily chain function adds
+   * @throws SolrException if the name of the function is not unique or the syntax of either signature is incorrect
+   */
+  private ExpressionFactory addVariableFunction(final String functionSignature,
+                                                final String returnSignature,
+                                                Map<String,VariableFunctionInfo> variableFunctions) throws SolrException {
+    String functionName = getFunctionName(functionSignature);
+    if (expressionCreators.containsKey(functionName)) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"Users cannot define a variable function with the same name as an existing function: " + functionName);
+    }
+    VariableFunctionInfo varFuncInfo = new VariableFunctionInfo();
+    varFuncInfo.params = getParams(functionSignature, null, null);
+    varFuncInfo.returnSignature = returnSignature;
+    variableFunctions.put(functionName, varFuncInfo);
+    return this;
+  }
+
+  /**
+   * Create a reduction manager to manage the collection of all expressions that have been created since
+   * {@link #startRequest()} was called.
+   * 
+   * @param isCloudCollection whether the request is a distributed request
+   * @return a reduction manager
+   */
+  public ReductionCollectionManager createReductionManager(boolean isCloudCollection) {
+    ReductionDataCollector<?>[] collectorsArr = new ReductionDataCollector<?>[collectors.size()];
+    collectors.values().toArray(collectorsArr);
+    if (isCloudCollection) {
+      return new MergingReductionCollectionManager(collectorsArr, fields.values());
+    } else {
+      return new ReductionCollectionManager(collectorsArr, fields.values());
+    }
+  }
+
+  /**
+   * Create a reduction manager to manage the collection of all expressions that have been created since
+   * {@link #startGrouping()} was called.
+   * 
+   * @param isCloudCollection whether the request is a distributed request
+   * @return a reduction manager
+   */
+  public ReductionCollectionManager createGroupingReductionManager(boolean isCloudCollection) {
+    ReductionDataCollector<?>[] collectorsArr = new ReductionDataCollector<?>[groupedCollectors.size()];
+    groupedCollectors.values().toArray(collectorsArr);
+    if (isCloudCollection) {
+      return new MergingReductionCollectionManager(collectorsArr, groupedFields.values());
+    } else {
+      return new ReductionCollectionManager(collectorsArr, groupedFields.values());
+    }
+  }
+  
+  /**
+   * Parse and build an expression from the given expression string.
+   * 
+   * @param expressionStr string that represents the desired expression
+   * @return the object representation of the expression
+   * @throws SolrException if an error occurs while constructing the expression
+   */
+  public AnalyticsValueStream createExpression(String expressionStr) throws SolrException {
+    return createExpression(expressionStr, new HashMap<>(), null, null);
+  }
+
+  /**
+   * Create an expression from the given expression string, with the given variable function information.
+   * 
+   * @param expressionStr string that represents the desired expression
+   * @param varFuncParams the current set of variable function parameters and their values. If this expression is not a variable function
+   * return signature, the map should be empty.
+   * @param varFuncVarParamName if the current expression is a variable function return signature, this must be the name of the variable length
+   * parameter if it is included in the function signature.
+   * @param varFuncVarParamValues if the current expression is a variable function return signature, this must be the array values of the variable length
+   * parameter if they are included when calling the function.
+   * @return the object representation of the expression
+   * @throws SolrException if an error occurs while constructing the expression
+   */
+  private AnalyticsValueStream createExpression(String expressionStr, Map<String,AnalyticsValueStream> varFuncParams,
+                                                String varFuncVarParamName, String[] varFuncVarParamValues) throws SolrException {
+    AnalyticsValueStream expression;
+    expressionStr = expressionStr.trim();
+    
+    boolean isField = false;
+    try {
+      // Try to make a constant value
+      expression = constantCreator.apply(expressionStr);
+    } catch (SolrException e1) {
+      // Not a constant
+      // If the expression has parens, it is an expression otherwise it is a field
+      if (!expressionStr.contains("(")) {
+        try {
+          // Try to make a field out of it
+          expression = createField(schema.getField(expressionStr));
+          isField = true;
+        } catch (SolrException e2) {
+          throw new SolrException(ErrorCode.BAD_REQUEST,"The following is not a field, constant or function : " + expressionStr); 
+        }  
+      } else {
+        // Must be a function
+        expression = createFunction(expressionStr, varFuncParams, varFuncVarParamName, varFuncVarParamValues);
+      }
+    }
+
+    // Try to use an already made expression instead of the new one.
+    // This will decrease the amount of collection needed to be done.
+    if (expressions.containsKey(expression.getExpressionStr())) {
+      expression = expressions.get(expression.getExpressionStr());
+      // If this is a grouped expression, make sure that the reduction info for the expression is included in the grouped reduction manager.
+      if (expression.getExpressionType() == ExpressionType.REDUCTION && isGrouped) {
+        ((ReductionFunction)expression).synchronizeDataCollectors( collector -> {
+          groupedCollectors.put(collector.getExpressionStr(), collector);
+          return collector;
+        });
+      }
+    }
+    else {
+      expressions.put(expression.getExpressionStr(), expression);
+      // Make sure that the reduction info for the expression is included in the reduction manager and grouped reduction manager if necessary.
+      if (expression.getExpressionType() == ExpressionType.REDUCTION) {
+        reductionFunctions.put(expression.getExpressionStr(), (ReductionFunction)expression);
+        ((ReductionFunction)expression).synchronizeDataCollectors( collector -> {
+          String collectorStr = collector.getExpressionStr();
+          ReductionDataCollector<?> usedCollector = collectors.get(collectorStr);
+          if (usedCollector == null) {
+            usedCollector = collector;
+            collectors.put(collectorStr, collector);
+          }
+          if (isGrouped) {
+            groupedCollectors.put(collectorStr, usedCollector);
+          }
+          return usedCollector;
+        });
+      }
+      // Add the field info to the reduction manager
+      if (isField) {
+        fields.put(expression.getExpressionStr(), (AnalyticsField)expression);
+      }
+    }
+    // If this is a grouped expression, make sure that the field info is included in the grouped reduction manager.
+    if (isField && isGrouped) {
+      groupedFields.put(expression.getExpressionStr(), (AnalyticsField)expression);
+    }
+    return expression;
+  }
+  
+  /**
+   * Create a function expression from the given expression string, with the given variable function information.
+   * 
+   * @param expressionStr string that represents the desired expression
+   * @param varFuncParams the current set of variable function parameters and their values. If this expression is not a variable function
+   * return signature, the map should be empty.
+   * @param varFuncVarParamName if the current expression is a variable function return signature, this must be the name of the variable length
+   * parameter if it is included in the function signature.
+   * @param varFuncVarParamValues if the current expression is a variable function return signature, this must be the array values of the variable length
+   * parameter if they are included when calling the function.
+   * @return the object representation of the expression
+   * @throws SolrException if an error occurs while constructing the expression
+   */
+  private AnalyticsValueStream createFunction(String expressionStr, Map<String,AnalyticsValueStream> varFuncParams,
+                                              String varFuncVarParamName, String[] varFuncVarParamValues) throws SolrException {
+    AnalyticsValueStream expression = null;
+    String name = getFunctionName(expressionStr);
+
+    final String[] params = getParams(expressionStr, varFuncVarParamName, varFuncVarParamValues);
+    AnalyticsValueStream[] paramStreams = new AnalyticsValueStream[params.length];
+    for (int i = 0; i < params.length; i++) {
+      // First check if the parameter is a variable function variable otherwise create the expression
+      if (varFuncParams.containsKey(params[i])) {
+        paramStreams[i] = varFuncParams.get(params[i]);
+      } else {
+        paramStreams[i] = createExpression(params[i], varFuncParams, varFuncVarParamName, varFuncVarParamValues);
+      }
+    }
+    // Check to see if the function name is a variable function name, if so apply the variables to the return signature
+    if (variableFunctions.containsKey(name)) {
+      if (variableFunctionNameHistory.contains(name)) {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The following variable function is self referencing : " + name); 
+      }
+      variableFunctionNameHistory.add(name);
+      VariableFunctionInfo newVarFunc = variableFunctions.get(name);
+      Map<String, AnalyticsValueStream> newVarFuncParams = new HashMap<>();
+      
+      boolean varLenEnd = false;
+      
+      for (int i = 0; i < newVarFunc.params.length; ++i) {
+        String variable = newVarFunc.params[i];
+        if (variable.endsWith(variableLengthParamSuffix)) {
+          if (i != newVarFunc.params.length - 1) {
+            throw new SolrException(ErrorCode.BAD_REQUEST,"The following invocation of a variable function has the incorrect number of arguments : " + expressionStr);
+          }
+          variable = variable.substring(0, variable.length() - variableLengthParamSuffix.length()).trim();
+          int numVars = paramStreams.length - i;
+          String[] newVarFuncVarParamValues = new String[numVars];
+          for (int j = 0; j < numVars; ++j) {
+            // Create a new name for each variable length parameter value
+            String paramName = String.format(Locale.ROOT, funtionVarParamUniqueName, variable, j);
+            newVarFuncVarParamValues[j] = paramName;
+            newVarFuncParams.put(paramName, paramStreams[i + j]);
+          }
+          expression = createFunction(newVarFunc.returnSignature, newVarFuncParams, variable, newVarFuncVarParamValues);
+          varLenEnd = true;
+        } else {
+          newVarFuncParams.put(variable, paramStreams[i]);
+        }
+      }
+      if (!varLenEnd) {
+        expression = createExpression(newVarFunc.returnSignature, newVarFuncParams, null, null);
+      }
+      variableFunctionNameHistory.remove(name);
+      return expression;
+    } else if (expressionCreators.containsKey(name)) {
+      // It is a regular system function
+      expression = expressionCreators.get(name).apply(paramStreams);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The following function does not exist: " + name);
+    }
+    return expression;
+  }
+
+  /**
+   * Create an {@link AnalyticsField} out of the given {@link SchemaField}.
+   * <p>
+   * Currently only fields with doc-values enabled are supported.
+   * 
+   * @param field the field to convert for analytics
+   * @return an analytics representation of the field
+   * @throws SolrException if the field is not supported by the analytics framework
+   */
+  private AnalyticsField createField(SchemaField field) throws SolrException {
+    String fieldName = field.getName();
+    if (fields.containsKey(fieldName)) {
+      return fields.get(fieldName);
+    }
+    if (!field.hasDocValues()) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The field "+fieldName+" does not have docValues enabled.");
+    }
+    boolean multivalued = field.multiValued();
+    FieldType fieldType = field.getType();
+    AnalyticsField aField;
+    if (fieldType instanceof BoolField) {
+      if (multivalued) {
+        aField = new BooleanMultiField(fieldName);
+      } else {
+        aField = new BooleanField(fieldName);
+      }
+    } else if (fieldType instanceof TrieIntField) {
+      if (multivalued) {
+        aField = new IntMultiField(fieldName);
+      } else {
+        aField = new IntField(fieldName);
+      }
+    } else if (fieldType instanceof IntPointField) {
+      if (multivalued) {
+        aField = new IntMultiPointField(fieldName);
+      } else {
+        aField = new IntField(fieldName);
+      }
+    } else if (fieldType instanceof TrieLongField) {
+      if (multivalued) {
+        aField = new LongMultiField(fieldName);
+      } else {
+        aField = new LongField(fieldName);
+      }
+    } else if (fieldType instanceof LongPointField) {
+      if (multivalued) {
+        aField = new LongMultiPointField(fieldName);
+      } else {
+        aField = new LongField(fieldName);
+      }
+    } else if (fieldType instanceof TrieFloatField) {
+      if (multivalued) {
+        aField = new FloatMultiField(fieldName);
+      } else {
+        aField = new FloatField(fieldName);
+      }
+    } else if (fieldType instanceof FloatPointField) {
+      if (multivalued) {
+        aField = new FloatMultiPointField(fieldName);
+      } else {
+        aField = new FloatField(fieldName);
+      }
+    } else if (fieldType instanceof TrieDoubleField) {
+      if (multivalued) {
+        aField = new DoubleMultiField(fieldName);
+      } else {
+        aField = new DoubleField(fieldName);
+      }
+    } else if (fieldType instanceof DoublePointField) {
+      if (multivalued) {
+        aField = new DoubleMultiPointField(fieldName);
+      } else {
+        aField = new DoubleField(fieldName);
+      }
+    } else if (fieldType instanceof TrieDateField) {
+      if (multivalued) {
+        aField = new DateMultiField(fieldName);
+      } else {
+        aField = new DateField(fieldName);
+      }
+    } else if (fieldType instanceof DatePointField) {
+      if (multivalued) {
+        aField = new DateMultiPointField(fieldName);
+      } else {
+        aField = new DateField(fieldName);
+      }
+    } else if (fieldType instanceof StrField) {
+      if (multivalued) {
+        aField = new StringMultiField(fieldName);
+      } else {
+        aField = new StringField(fieldName);
+      }
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"FieldType of the following field not supported by analytics: "+fieldName);
+    }
+    return aField;
+  }
+
+  /**
+   * Get the name of the top function used in the given expression.
+   * 
+   * @param expression the expression to find the function name of
+   * @return the name of the function 
+   * @throws SolrException if the expression has incorrect syntax
+   */
+  private static String getFunctionName(String expression) throws SolrException {
+    Matcher m = functionNamePattern.matcher(expression);
+    if (!m.matches()) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The following function has no name: " + expression);
+    }
+    String name = m.group(1);
+    return name;
+  }
+  
+  /**
+   * Get the params of a function.
+   * 
+   * @param function the function to parse
+   * @return an array of param strings
+   * @throws SolrException if the function has incorrect syntax
+   */
+  private static String[] getFunctionParams(String function) throws SolrException {
+    return getParams(function, null, null);
+  }
+
+  /**
+   * Parse a function expression string and break up the parameters of the function into separate strings.
+   * <p>
+   * The parsing replaces the variable length parameter, and lambda for-each's using the variable length parameter,
+   * with the parameter values in the returned parameter string.
+   * <p>
+   * Parsing breaks up parameters by commas (',') and ignores ',' inside of extra parens and quotes (both ' and "), since these commas are either
+   * splitting up the parameters of nested functions or are apart of strings.
+   * <br>
+   * The only escaping that needs to be done is " within a double quote string and ' within a single quote string and \ within any string.
+   * For example\:
+   * <ul>
+   * <li> {@code func("This is \" the \\ escaping ' example")} will be treated as {@code func(This is " the \ escaping ' example)}
+   * <li> {@code func('This is " the \\ escaping \' example')} will be treated as {@code func(This is " the \ escaping ' example)}
+   * </ul>
+   * In string constants the \ character is used to escape quotes, so it can never be used alone. in order to write a \ you must write \\
+   * 
+   * @param expression the function expression to parse
+   * @param varLengthParamName the name of the variable length parameter that is used in the expression, pass null if none is used.
+   * @param varLengthParamValues the values of the variable length parameter that are used in the expression, pass null if none are used.
+   * @return the parsed and split arguments to the function
+   * @throws SolrException if the expression has incorrect syntax.
+   */
+  private static String[] getParams(String expression, String varLengthParamName, String[] varLengthParamValues) throws SolrException {
+    Matcher m = functionParamsPattern.matcher(expression);
+    if (!m.matches()) {
+      return new String[0];
+    }
+    String paramsStr = m.group(1);
+    
+    ArrayList<String> paramsList = new ArrayList<String>();
+    StringBuilder param = new StringBuilder();
+    
+    // Variables to help while filling out the values of for-each lambda functions.
+    boolean inForEach = false;
+    int forEachStart = -1;
+    int forEachIter = -1;
+    int forEachLevel = -1;
+    
+    // The current level of nested parenthesis, 0 means the iteration is in no nested parentheses
+    int parenCount = 0;
+    // If the iteration is currently in a single-quote string constant
+    boolean singleQuoteOn = false;
+    // If the iteration is currently in a double-quote string constant
+    boolean doubleQuoteOn = false;
+    // If the iteration is currently in any kind of string constant
+    boolean quoteOn = false;
+    // Is the next character escaped.
+    boolean escaped = false;
+    
+    char[] chars = paramsStr.toCharArray();
+    
+    // Iterate through every character, building the params one at a time
+    for (int i = 0; i < chars.length; ++i) {
+      char c = chars[i];
+      
+      if (c == ' ' && !quoteOn) {
+        // Ignore white space that is not in string constants
+        continue;
+      } else if (c == ',' && parenCount == 0 && !quoteOn) {
+        // This signifies the end of one parameter and the start of another, since we are not in a nested parenthesis or a string constant
+        String paramStr = param.toString();
+        if (paramStr.length() == 0) {
+          throw new SolrException(ErrorCode.BAD_REQUEST,"Empty parameter in expression: " + expression);
+        }
+        // check to see if the parameter is a variable length parameter
+        if (paramStr.equals(varLengthParamName)) {
+          // Add every variable length parameter value, since there are a variable amount
+          for (String paramName : varLengthParamValues) {
+            paramsList.add(paramName);
+          }
+        } else {
+          paramsList.add(paramStr);
+        }
+        
+        param.setLength(0);
+        continue;
+      } else if (c == ',' && !quoteOn && inForEach) {
+        // separate the for each parameters, so they can be replaced with the result of the for each
+        if (param.charAt(param.length()-1) == variableForEachParam && 
+            (param.charAt(param.length()-2) == '(' || param.charAt(param.length()-2) == ',')) {
+          param.setLength(param.length()-1);
+          param.append(varLengthParamValues[forEachIter++]);
+        }
+      } else if (c == '"' && !singleQuoteOn) {
+        // Deal with escaping, or ending string constants
+        if (doubleQuoteOn && !escaped) {
+          doubleQuoteOn = false;
+          quoteOn = false;
+        } else if (!quoteOn) {
+          doubleQuoteOn = true;
+          quoteOn = true;
+        } else {
+          // only happens if escaped is true
+          escaped = false;
+        }
+      }  else if (c== '\'' && !doubleQuoteOn) {
+        // Deal with escaping, or ending string constants
+        if (singleQuoteOn && !escaped) {
+          singleQuoteOn = false;
+          quoteOn = false;
+        } else if (!singleQuoteOn) {
+          singleQuoteOn = true;
+          quoteOn = true;
+        } else {
+          // only happens if escaped is true
+          escaped = false;
+        }
+      } else if (c == '(' && !quoteOn) {
+        // Reached a further level of nested parentheses
+        parenCount++;
+      } else if (c == ')' && !quoteOn) {
+        // Returned from a level of nested parentheses
+        parenCount--;
+        if (parenCount < 0) {
+          throw new SolrException(ErrorCode.BAD_REQUEST,"The following expression has extra end parens: " + param.toString());
+        }
+        if (inForEach) {
+          if (param.charAt(param.length()-1) == variableForEachParam && 
+              (param.charAt(param.length()-2) == '(' || param.charAt(param.length()-2) == ',')) {
+            param.setLength(param.length()-1);
+            param.append(varLengthParamValues[forEachIter++]);
+          }
+          if (forEachLevel == parenCount) {
+            // at the end of the for-each start the parsing of the for-each again, with the next value of the variable length parameter
+            if (forEachIter == 0) {
+              throw new SolrException(ErrorCode.BAD_REQUEST,"For each statement for variable '" + varLengthParamName + "' has no use of lambda variable " + variableForEachParam);
+            } else if (forEachIter < varLengthParamValues.length) {
+              if (parenCount == 0) {
+                param.append(')');
+                paramsList.add(param.toString());
+                param.setLength(0);
+              } else {
+                param.append(')');
+                param.append(',');
+              }
+              i = forEachStart;
+              continue;
+            } else {
+              inForEach = false;
+            }
+          }
+        }
+      }
+      if (c == '\\') {
+        // Escaping or escaped backslash
+        if (!quoteOn) {
+          throw new SolrException(ErrorCode.BAD_REQUEST,"The following expression has escaped characters outside of quotation marks: " + expression.toString());
+        }
+        if (escaped) {
+          escaped = false;
+        } else {
+          escaped = true;
+          if (parenCount == 0) {
+            continue;
+          }
+        }
+      } else if (escaped) {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"Invalid escape character '" + c + "' used in the following expression: " + expression.toString());
+      }
+      if (c == variableForEachSep && !quoteOn && varLengthParamName != null) {
+        int varStart = param.length()-varLengthParamName.length();
+        if (param.subSequence(varStart, param.length()).equals(varLengthParamName)) {
+          inForEach = true;
+          forEachStart = i;
+          forEachIter = 0;
+          forEachLevel = parenCount;
+          param.setLength(varStart);
+          continue;
+        }
+        throw new SolrException(ErrorCode.BAD_REQUEST,"For-each called on invalid parameter '" + param.toString().trim());
+      }
+      param.append(c);
+    }
+    String paramStr = param.toString().trim();
+    if (paramStr.length() == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"Empty parameter in expression: " + expression);
+    }
+    if (parenCount > 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The following expression needs more end parens: " + param.toString());
+    }
+    if (quoteOn) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"Misplaced quotation marks in expression: " + expression);
+    }
+    if (paramStr.equals(varLengthParamName)) {
+      for (String paramName : varLengthParamValues) {
+        paramsList.add(paramName);
+      }
+    } else {
+      paramsList.add(paramStr);
+    }
+    return paramsList.toArray(new String[paramsList.size()]);
+  }
+  
+  /**
+   * Add the natively supported functionality.
+   */
+  public void addSystemFunctions() {
+    // Mapping Functions
+    expressionCreators.put(AbsoluteValueFunction.name, AbsoluteValueFunction.creatorFunction);
+    expressionCreators.put(AndFunction.name, AndFunction.creatorFunction);
+    expressionCreators.put(AddFunction.name, AddFunction.creatorFunction);
+    expressionCreators.put(BottomFunction.name, BottomFunction.creatorFunction);
+    expressionCreators.put(CeilingFunction.name, CeilingFunction.creatorFunction);
+    expressionCreators.put(ConcatFunction.name, ConcatFunction.creatorFunction);
+    expressionCreators.put(ConcatSeparatedFunction.name, ConcatSeparatedFunction.creatorFunction);
+    expressionCreators.put(DateMathFunction.name, DateMathFunction.creatorFunction);
+    expressionCreators.put(DateParseFunction.name, DateParseFunction.creatorFunction);
+    expressionCreators.put(DivideFunction.name, DivideFunction.creatorFunction);
+    expressionCreators.put(DocCountFunction.name, DocCountFunction.creatorFunction);
+    expressionCreators.put(EqualFunction.name,EqualFunction.creatorFunction);
+    expressionCreators.put(FillMissingFunction.name, FillMissingFunction.creatorFunction);
+    expressionCreators.put(FilterFunction.name, FilterFunction.creatorFunction);
+    expressionCreators.put(FloorFunction.name, FloorFunction.creatorFunction);
+    expressionCreators.put(GTFunction.name,GTFunction.creatorFunction);
+    expressionCreators.put(GTEFunction.name,GTEFunction.creatorFunction);
+    expressionCreators.put(IfFunction.name, IfFunction.creatorFunction);
+    expressionCreators.put(JoinFunction.name, JoinFunction.creatorFunction);
+    expressionCreators.put(LogFunction.name,LogFunction.creatorFunction);
+    expressionCreators.put(LTFunction.name,LTFunction.creatorFunction);
+    expressionCreators.put(LTEFunction.name,LTEFunction.creatorFunction);
+    expressionCreators.put(MultFunction.name, MultFunction.creatorFunction);
+    expressionCreators.put(NegateFunction.name, NegateFunction.creatorFunction);
+    expressionCreators.put(OrFunction.name, OrFunction.creatorFunction);
+    expressionCreators.put(PowerFunction.name, PowerFunction.creatorFunction);
+    expressionCreators.put(ReplaceFunction.name, ReplaceFunction.creatorFunction);
+    expressionCreators.put(RemoveFunction.name, RemoveFunction.creatorFunction);
+    expressionCreators.put(RoundFunction.name, RoundFunction.creatorFunction);
+    expressionCreators.put(StringCastFunction.name, StringCastFunction.creatorFunction);
+    expressionCreators.put(SubtractFunction.name, SubtractFunction.creatorFunction);
+    expressionCreators.put(TopFunction.name, TopFunction.creatorFunction);
+    
+    // Reduction Functions
+    expressionCreators.put(CountFunction.name, CountFunction.creatorFunction);
+    expressionCreators.put(MaxFunction.name, MaxFunction.creatorFunction);
+    expressionCreators.put(MedianFunction.name, MedianFunction.creatorFunction);
+    expressionCreators.put(MinFunction.name, MinFunction.creatorFunction);
+    expressionCreators.put(MissingFunction.name, MissingFunction.creatorFunction);
+    expressionCreators.put(OrdinalFunction.name, OrdinalFunction.creatorFunction);
+    expressionCreators.put(PercentileFunction.name, PercentileFunction.creatorFunction);
+    expressionCreators.put(SumFunction.name, SumFunction.creatorFunction);
+    expressionCreators.put(UniqueFunction.name, UniqueFunction.creatorFunction);
+    
+    // Variables
+    addSystemVariableFunction("wmean(a,b)","div(mean(prod(a,b)),sum(b))");
+    addSystemVariableFunction("mean(a)","div(sum(a),count(a))");
+    addSystemVariableFunction("sumofsquares(a)","sum(pow(a,2))");
+    addSystemVariableFunction("sqrt(a)","pow(a,0.5)");
+    addSystemVariableFunction("variance(a)","sub(mean(pow(a,2)),pow(mean(a),2))");
+    addSystemVariableFunction("stddev(a)","sqrt(variance(a))");
+    addSystemVariableFunction("csv(a..)","concatsep(',',a)");
+    addSystemVariableFunction("csv_output(a..)","concatsep(',',a:fillmissing(join(_,';'),''))");
+  }
+
+  /**
+   * Used for system analytics functions for initialization. Should take in a list of expression parameters and return an expression.
+   */
+  @FunctionalInterface
+  public static interface CreatorFunction {
+    AnalyticsValueStream apply(AnalyticsValueStream[] t) throws SolrException;
+  }
+  /**
+   * Used to initialize analytics constants.
+   */
+  @FunctionalInterface
+  public static interface ConstantFunction {
+    AnalyticsValueStream apply(String t) throws SolrException;
+  }
+}
+class VariableFunctionInfo {
+  public String[] params;
+  public String returnSignature;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/BasicAccumulator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/BasicAccumulator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/BasicAccumulator.java
deleted file mode 100644
index cbd8078..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/BasicAccumulator.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * 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.solr.analytics.accumulator;
-
-import java.io.IOException;
-import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Supplier;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.solr.analytics.expression.Expression;
-import org.apache.solr.analytics.expression.ExpressionFactory;
-import org.apache.solr.analytics.request.AnalyticsRequest;
-import org.apache.solr.analytics.request.ExpressionRequest;
-import org.apache.solr.analytics.statistics.StatsCollector;
-import org.apache.solr.analytics.statistics.StatsCollectorSupplierFactory;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.SolrException.ErrorCode;
-import org.apache.solr.common.util.NamedList;
-import org.apache.solr.search.DocSet;
-import org.apache.solr.search.SolrIndexSearcher;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * A <code>BasicAccumulator</code> manages the ValueCounters and Expressions without regard to Facets.
- */
-public class BasicAccumulator extends ValueAccumulator {
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-  protected final SolrIndexSearcher searcher;
-  protected final AnalyticsRequest request;
-  protected final DocSet docs;
-  protected final Supplier<StatsCollector[]> statsCollectorArraySupplier;
-  protected final StatsCollector[] statsCollectors;
-  protected final Expression[] expressions;
-  protected final String[] expressionNames;
-  protected final String[] expressionStrings;
-  protected final Set<String> hiddenExpressions;
-  protected LeafReaderContext context = null;
-  
-  public BasicAccumulator(SolrIndexSearcher searcher, DocSet docs, AnalyticsRequest request) throws IOException {
-    this.searcher = searcher;
-    this.docs = docs;
-    this.request = request;
-    final List<ExpressionRequest> exRequests = new ArrayList<ExpressionRequest>(request.getExpressions()); // make a copy here
-    Collections.sort(exRequests);
-    log.info("Processing request '"+request.getName()+"'");
-    statsCollectorArraySupplier = StatsCollectorSupplierFactory.create(searcher.getSchema(), exRequests);
-    statsCollectors = statsCollectorArraySupplier.get();
-    int size = exRequests.size();
-    expressionNames = new String[size];
-    expressionStrings = new String[size];
-    int count = 0;
-    for (ExpressionRequest expRequest : exRequests) {
-      expressionNames[count] = expRequest.getName();
-      expressionStrings[count++] = expRequest.getExpressionString();
-    }
-    expressions = makeExpressions(statsCollectors);
-    hiddenExpressions = request.getHiddenExpressions();
-  }
-  
-  @Override
-  protected void doSetNextReader(LeafReaderContext context) throws IOException {
-    this.context = context;
-    for (StatsCollector counter : statsCollectors) {
-      counter.setNextReader(context);
-    }
-  }
- 
-  public static BasicAccumulator create(SolrIndexSearcher searcher, DocSet docs, AnalyticsRequest request) throws IOException {
-    return new BasicAccumulator(searcher,docs,request);
-  }
-  
-  /**
-   * Passes the documents on to the {@link StatsCollector}s to be collected.
-   * @param doc Document to collect from
-   */
-  @Override
-  public void collect(int doc) throws IOException {
-    for (StatsCollector statsCollector : statsCollectors) {
-      statsCollector.collect(doc);
-    }
-  }
-  
-  @Override
-  public void compute() {
-    for (StatsCollector statsCollector : statsCollectors) {
-      statsCollector.compute();
-    }
-  }
-  
-  public NamedList<?> export(){
-    NamedList<Object> base = new NamedList<>();
-    for (int count = 0; count < expressions.length; count++) {
-      if (!hiddenExpressions.contains(expressionNames[count])) {
-        base.add(expressionNames[count], expressions[count].getValue());
-      }
-    }
-    return base;
-  }
-  
-  /**
-   * Builds an array of Expressions with the given list of counters
-   * @param statsCollectors the stats collectors
-   * @return The array of Expressions
-   */
-  public Expression[] makeExpressions(StatsCollector[] statsCollectors) {
-   Expression[] expressions = new Expression[expressionStrings.length];
-    for (int count = 0; count < expressionStrings.length; count++) {
-      expressions[count] = ExpressionFactory.create(expressionStrings[count], statsCollectors);
-    }
-    return expressions;
-  }
-  
-  /**
-   * Returns the value of an expression to use in a field or query facet.
-   * @param expressionName the name of the expression
-   * @return String String representation of pivot value
-   */
-  @SuppressWarnings({ "deprecation", "rawtypes" })
-  public String getResult(String expressionName) {
-    for (int count = 0; count < expressionNames.length; count++) {
-      if (expressionName.equals(expressionNames[count])) {
-        Comparable value = expressions[count].getValue();
-        if (value.getClass().equals(Date.class)) {
-          return ((Date)value).toInstant().toString();
-        } else {
-          return value.toString();
-        }
-      }
-    }
-    throw new SolrException(ErrorCode.BAD_REQUEST, "Pivot expression "+expressionName+" not found.");
-  }
-
-  /**
-   * Used for JMX stats collecting. Counts the number of stats requests
-   * @return number of unique stats collectors
-   */
-  public long getNumStatsCollectors() {
-    return statsCollectors.length;
-  }
-
-  /**
-   * Used for JMX stats collecting. Counts the number of queries in all query facets
-   * @return number of queries requested in all query facets.
-   */
-  public long getNumQueries() {
-    return 0l;
-  }
-  
-  @Override
-  public boolean needsScores() {
-    return true; // TODO: is this true?
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/FacetingAccumulator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/FacetingAccumulator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/FacetingAccumulator.java
deleted file mode 100644
index d8828a6..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/FacetingAccumulator.java
+++ /dev/null
@@ -1,730 +0,0 @@
-/*
- * 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.solr.analytics.accumulator;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.TreeMap;
-
-import com.google.common.collect.Iterables;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.search.BooleanClause.Occur;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.Query;
-import org.apache.solr.analytics.accumulator.facet.FacetValueAccumulator;
-import org.apache.solr.analytics.accumulator.facet.FieldFacetAccumulator;
-import org.apache.solr.analytics.accumulator.facet.QueryFacetAccumulator;
-import org.apache.solr.analytics.accumulator.facet.RangeFacetAccumulator;
-import org.apache.solr.analytics.expression.Expression;
-import org.apache.solr.analytics.expression.ExpressionFactory;
-import org.apache.solr.analytics.request.AnalyticsContentHandler;
-import org.apache.solr.analytics.request.AnalyticsRequest;
-import org.apache.solr.analytics.request.FieldFacetRequest;
-import org.apache.solr.analytics.request.FieldFacetRequest.FacetSortSpecification;
-import org.apache.solr.analytics.request.QueryFacetRequest;
-import org.apache.solr.analytics.request.RangeFacetRequest;
-import org.apache.solr.analytics.statistics.StatsCollector;
-import org.apache.solr.analytics.util.AnalyticsParams;
-import org.apache.solr.analytics.util.RangeEndpointCalculator;
-import org.apache.solr.analytics.util.RangeEndpointCalculator.FacetRange;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.SolrException.ErrorCode;
-import org.apache.solr.common.util.NamedList;
-import org.apache.solr.request.SolrQueryRequest;
-import org.apache.solr.schema.SchemaField;
-import org.apache.solr.search.DocSet;
-import org.apache.solr.search.Filter;
-import org.apache.solr.search.QParser;
-import org.apache.solr.search.SolrIndexSearcher;
-import org.apache.solr.search.SyntaxError;
-
-/**
- * A <code>FacetingAccumulator</code> manages the StatsCollectors and Expressions for facets.
- */
-public class FacetingAccumulator extends BasicAccumulator implements FacetValueAccumulator {
-  public static final String MISSING_VALUE = "(MISSING)";
-  protected boolean basicsAndFieldFacetsComputed;
-  protected int leafNum;
-  protected LeafReaderContext leaf;
-  protected final AnalyticsRequest analyticsRequest;
-  protected final Map<String,Map<String,Expression[]>> fieldFacetExpressions;
-  protected final Map<String,Map<String,Expression[]>> rangeFacetExpressions;
-  protected final Map<String,Map<String,Expression[]>> queryFacetExpressions;
-  protected final Map<String,Map<String,StatsCollector[]>> fieldFacetCollectors;
-  protected final Map<String,Map<String,StatsCollector[]>> rangeFacetCollectors;
-  protected final Map<String,Map<String,StatsCollector[]>> queryFacetCollectors;
-  protected final List<FieldFacetAccumulator> facetAccumulators;
-  protected final Set<String> hiddenFieldFacets;
-  /** the current value of this stat field */
-  protected final SolrQueryRequest queryRequest;
-  
-  protected List<RangeFacetRequest> rangeFacets = null;
-  protected List<QueryFacetRequest> queryFacets = null;
-  
-  protected long queryCount;
-  
-  public FacetingAccumulator(SolrIndexSearcher searcher, DocSet docs, AnalyticsRequest request, SolrQueryRequest queryRequest) throws IOException {
-    // The parent Basic Accumulator keeps track of overall stats while
-    // the Faceting Accumulator only manages the facet stats
-    super(searcher, docs, request);
-    this.analyticsRequest = request;
-    this.queryRequest = queryRequest;
-    basicsAndFieldFacetsComputed = false;
-    List<FieldFacetRequest> fieldFreqs = request.getFieldFacets();
-    List<RangeFacetRequest> rangeFreqs = request.getRangeFacets();
-    List<QueryFacetRequest> queryFreqs = request.getQueryFacets();
-
-    this.fieldFacetExpressions = new TreeMap<>();
-    this.rangeFacetExpressions = new LinkedHashMap<>(rangeFreqs.size());
-    this.queryFacetExpressions = new LinkedHashMap<>(queryFreqs.size());
-    this.fieldFacetCollectors = new LinkedHashMap<>(fieldFreqs.size());
-    this.rangeFacetCollectors = new LinkedHashMap<>(rangeFreqs.size());
-    this.queryFacetCollectors = new LinkedHashMap<>(queryFreqs.size());
-    this.facetAccumulators = new ArrayList<>();
-    this.hiddenFieldFacets = new HashSet<>();
-    
-    /**
-     * For each field facet request add a bucket to the {@link Expression} map and {@link StatsCollector} map.
-     * Field facets are computed during the initial collection of documents, therefore
-     * the FieldFacetAccumulators are created initially.
-     */
-    for( FieldFacetRequest freq : fieldFreqs ){
-      final FieldFacetRequest fr = (FieldFacetRequest) freq;
-      if (fr.isHidden()) {
-        hiddenFieldFacets.add(fr.getName());
-      }
-      final SchemaField ff = fr.getField();
-      final FieldFacetAccumulator facc = FieldFacetAccumulator.create(searcher, this, ff);
-      facetAccumulators.add(facc);
-      fieldFacetExpressions.put(freq.getName(), new TreeMap<String, Expression[]>() );
-      fieldFacetCollectors.put(freq.getName(), new TreeMap<String,StatsCollector[]>());
-    }
-    /**
-     * For each range and query facet request add a bucket to the corresponding
-     * {@link Expression} map and {@link StatsCollector} map.
-     * Range and Query Facets are computed in the post processing, so the accumulators
-     * are not created initially.
-     */
-    for( RangeFacetRequest freq : rangeFreqs ){
-      if( rangeFacets == null ) rangeFacets = new ArrayList<>();
-      rangeFacets.add(freq);
-      rangeFacetExpressions.put(freq.getName(), new LinkedHashMap<String,Expression[]>() );
-      rangeFacetCollectors.put(freq.getName(), new LinkedHashMap<String,StatsCollector[]>());
-    }
-    for( QueryFacetRequest freq : queryFreqs ){
-      if( queryFacets == null ) queryFacets = new ArrayList<>();
-      queryFacets.add(freq);
-      queryFacetExpressions.put(freq.getName(), new LinkedHashMap<String,Expression[]>() );
-      queryFacetCollectors.put(freq.getName(), new LinkedHashMap<String,StatsCollector[]>());
-    }
-    this.queryCount = 0l;
-  }
-  
-  public static FacetingAccumulator create(SolrIndexSearcher searcher, DocSet docs, AnalyticsRequest request, SolrQueryRequest queryRequest) throws IOException {
-    return new FacetingAccumulator(searcher,docs,request,queryRequest);
-  }
-
-  /**
-   * Update the readers for the {@link BasicAccumulator}, field facets and field facet {@link StatsCollector}s.
-   * @param context The context to read documents from.
-   * @throws IOException if there is an error setting the next reader
-   */
-  @Override
-  protected void doSetNextReader(LeafReaderContext context) throws IOException {
-    super.doSetNextReader(context);
-    for( Map<String,StatsCollector[]> valueList : fieldFacetCollectors.values() ){
-      for (StatsCollector[] statsCollectorList : valueList.values()) {
-        for (StatsCollector statsCollector : statsCollectorList) {
-          statsCollector.setNextReader(context);
-        }
-      }
-    }
-    for (FieldFacetAccumulator fa : facetAccumulators) {
-      fa.getLeafCollector(context);
-    }
-  }
-  
-  /**
-   * Updates the reader for all of the range facet {@link StatsCollector}s.
-   * @param context The context to read documents from.
-   * @throws IOException if there is an error setting the next reader
-   */
-  public void setRangeStatsCollectorReaders(LeafReaderContext context) throws IOException {
-    super.getLeafCollector(context);
-    for( Map<String,StatsCollector[]> rangeList : rangeFacetCollectors.values() ){
-      for (StatsCollector[] statsCollectorList : rangeList.values()) {
-        for (StatsCollector statsCollector : statsCollectorList) {
-          statsCollector.setNextReader(context);
-        }
-      }
-    }
-  }
-
-  
-  /**
-   * Updates the reader for all of the query facet {@link StatsCollector}s.
-   * @param context The context to read documents from.
-   * @throws IOException if there is an error setting the next reader
-   */
-  public void setQueryStatsCollectorReaders(LeafReaderContext context) throws IOException {
-    super.getLeafCollector(context);
-    for( Map<String,StatsCollector[]> queryList : queryFacetCollectors.values() ){
-      for (StatsCollector[] statsCollectorList : queryList.values()) {
-        for (StatsCollector statsCollector : statsCollectorList) {
-          statsCollector.setNextReader(context);
-        }
-      }
-    }
-  }
-
-  /**
-   * Called from Analytics stats, adds documents to the field 
-   * facets and the super {@link BasicAccumulator}.
-   */
-  @Override
-  public void collect(int doc) throws IOException {
-    for( FieldFacetAccumulator fa : facetAccumulators ){
-      fa.collect(doc);
-    }
-    super.collect(doc);
-  }
-  
-  /**
-   * Given a document, fieldFacet field and facetValue, adds the document to the
-   * {@link StatsCollector}s held in the bucket corresponding to the fieldFacet field and facetValue.
-   * Called during initial document collection.
-   */
-  @Override
-  public void collectField(int doc, String facetField, String facetValue) throws IOException {
-    Map<String,StatsCollector[]> map = fieldFacetCollectors.get(facetField);
-    StatsCollector[] statsCollectors = map.get(facetValue);
-    // If the facetValue has not been seen yet, a StatsCollector array is
-    // created and associated with that bucket.
-    if( statsCollectors == null ){
-      statsCollectors = statsCollectorArraySupplier.get();
-      map.put(facetValue,statsCollectors);
-      fieldFacetExpressions.get(facetField).put(facetValue,makeExpressions(statsCollectors));
-      for (StatsCollector statsCollector : statsCollectors) {
-        statsCollector.setNextReader(context);
-      }
-    }
-    for (StatsCollector statsCollector : statsCollectors) {
-      statsCollector.collect(doc);
-    }
-  }
-  
-  /**
-   * Given a document, rangeFacet field and range, adds the document to the
-   * {@link StatsCollector}s held in the bucket corresponding to the rangeFacet field and range.
-   * Called during post processing.
-   */
-  @Override
-  public void collectRange(int doc, String facetField, String range) throws IOException {
-    Map<String,StatsCollector[]> map = rangeFacetCollectors.get(facetField);
-    StatsCollector[] statsCollectors = map.get(range);
-    // If the range has not been seen yet, a StatsCollector array is
-    // created and associated with that bucket.
-    if( statsCollectors == null ){
-      statsCollectors = statsCollectorArraySupplier.get();
-      map.put(range,statsCollectors);
-      rangeFacetExpressions.get(facetField).put(range,makeExpressions(statsCollectors));
-      for (StatsCollector statsCollector : statsCollectors) {
-        statsCollector.setNextReader(context);
-      }
-    }
-    for (StatsCollector statsCollector : statsCollectors) {
-      statsCollector.collect(doc);
-    }
-  }
-  
-  /**
-   * Given a document, queryFacet name and query, adds the document to the
-   * {@link StatsCollector}s held in the bucket corresponding to the queryFacet name and query.
-   * Called during post processing.
-   */
-  @Override
-  public void collectQuery(int doc, String facetName, String query) throws IOException {
-    Map<String,StatsCollector[]> map = queryFacetCollectors.get(facetName);
-    StatsCollector[] statsCollectors = map.get(query);
-    // If the query has not been seen yet, a StatsCollector array is
-    // created and associated with that bucket.
-    if( statsCollectors == null ){
-      statsCollectors = statsCollectorArraySupplier.get();
-      map.put(query,statsCollectors);
-      queryFacetExpressions.get(facetName).put(query,makeExpressions(statsCollectors));
-      for (StatsCollector statsCollector : statsCollectors) {
-        statsCollector.setNextReader(context);
-      }
-    }
-    for (StatsCollector statsCollector : statsCollectors) {
-      statsCollector.collect(doc);
-    }
-  }
-
-  /**
-   * A comparator to compare expression values for field facet sorting.
-   */
-  public static class EntryComparator implements Comparator<Entry<String,Expression[]>> {
-    private final Comparator<Expression> comp;
-    private final int comparatorExpressionPlace;
-   
-    public EntryComparator(Comparator<Expression> comp, int comparatorExpressionPlace) {
-      this.comp = comp;
-      this.comparatorExpressionPlace = comparatorExpressionPlace;
-    }
-
-    @Override
-    public int compare(Entry<String,Expression[]> o1, Entry<String,Expression[]> o2) {
-      return comp.compare(o1.getValue()[comparatorExpressionPlace], o2.getValue()[comparatorExpressionPlace]);
-    }
-  }
-  
-  /**
-   * Finalizes the statistics within the each facet bucket before exporting;
-   */
-  @Override
-  public void compute() {
-    if (!basicsAndFieldFacetsComputed) {
-      super.compute();
-      for( Map<String, StatsCollector[]> f : fieldFacetCollectors.values() ){
-        for( StatsCollector[] arr : f.values() ){
-          for( StatsCollector b : arr ){
-            b.compute();
-          }
-        }
-      }
-      basicsAndFieldFacetsComputed = true;
-    }
-  }
-  
-  /**
-   * Finalizes the statistics within the a specific query facet before exporting;
-   */
-  public void computeQueryFacet(String facet) {
-    Map<String, StatsCollector[]> f = queryFacetCollectors.get(facet);
-    for( StatsCollector[] arr : f.values() ){
-      for( StatsCollector b : arr ){
-        b.compute();
-      }
-    }
-  }
-  
-  /**
-   * Finalizes the statistics within the a specific range facet before exporting;
-   */
-  public void computeRangeFacet(String facet) {
-    Map<String, StatsCollector[]> f = rangeFacetCollectors.get(facet);
-    for( StatsCollector[] arr : f.values() ){
-      for( StatsCollector b : arr ){
-        b.compute();
-      }
-    }
-  }
-  
-  /**
-   * Returns the value of an expression to use in a range or query facet.
-   * @param expressionName the name of the expression
-   * @param fieldFacet the facet field
-   * @param facetValue the facet value
-   * @return String String representation of pivot value
-   */
-  @SuppressWarnings({ "deprecation", "rawtypes" })
-  public String getResult(String expressionName, String fieldFacet, String facetValue) {
-    if (facetValue.contains(AnalyticsParams.RESULT) && !facetValue.contains(AnalyticsParams.QUERY_RESULT)) {
-      try {
-        String[] pivotStr = ExpressionFactory.getArguments(facetValue.substring(facetValue.indexOf('(')+1,facetValue.lastIndexOf(')')).trim());
-        if (pivotStr.length==1) {
-          facetValue = getResult(pivotStr[0]);
-        } else if (pivotStr.length==3) {
-          facetValue = getResult(pivotStr[0],pivotStr[1],pivotStr[2]);
-        } else {
-          throw new SolrException(ErrorCode.BAD_REQUEST, "Result request "+facetValue+" has an invalid amount of arguments.");
-        }
-      } catch (IndexOutOfBoundsException e) {
-        throw new SolrException(ErrorCode.BAD_REQUEST, "Result request "+facetValue+" is invalid. Lacks parentheses.",e);
-      }
-    } 
-    if (fieldFacetExpressions.get(fieldFacet)!=null) {
-      Expression[] facetExpressions = fieldFacetExpressions.get(fieldFacet).get(facetValue);
-      for (int count = 0; count < expressionNames.length; count++) {
-        if (expressionName.equals(expressionNames[count])) {
-          Comparable value = facetExpressions[count].getValue();
-          if (value.getClass().equals(Date.class)) {
-            return ((Date)value).toInstant().toString();
-          } else {
-            return value.toString();
-          }
-        }
-      }
-    }
-    throw new SolrException(ErrorCode.BAD_REQUEST,"Field Facet Pivot expression "+expressionName+" not found.");
-  }
-  
-  /**
-   * Returns the value of an expression to use in a range or query facet.
-   * @param currentFacet the name of the current facet
-   * @param expressionName the name of the expression
-   * @param queryFacet the facet query
-   * @param facetValue the field value
-   * @return String String representation of pivot value
-   */
-  @SuppressWarnings({ "deprecation", "rawtypes" })
-  public String getQueryResult(String currentFacet, String expressionName, String queryFacet, String facetValue) {
-    if (facetValue.contains(AnalyticsParams.RESULT) && !facetValue.contains(AnalyticsParams.QUERY_RESULT)) {
-      try {
-        String[] pivotStr = ExpressionFactory.getArguments(facetValue.substring(facetValue.indexOf('(')+1,facetValue.lastIndexOf(')')).trim());
-        if (pivotStr.length==1) {
-          facetValue = getResult(pivotStr[0]);
-        } else if (pivotStr.length==3) {
-          facetValue = getResult(pivotStr[0],pivotStr[1],pivotStr[2]);
-        } else {
-          throw new SolrException(ErrorCode.BAD_REQUEST, "Result request "+facetValue+" has an invalid amount of arguments.");
-        }
-      } catch (IndexOutOfBoundsException e) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"Result request "+facetValue+" is invalid. Lacks parentheses.",e);
-      }
-    } 
-    if (facetValue.contains(AnalyticsParams.QUERY_RESULT)) {
-      try {
-        String[] pivotStr = ExpressionFactory.getArguments(facetValue.substring(facetValue.indexOf('(')+1,facetValue.lastIndexOf(')')).trim());
-        if (pivotStr.length==1) {
-          facetValue = getResult(pivotStr[0]);
-        } else if (pivotStr.length==3) {
-          facetValue = getQueryResult(currentFacet,pivotStr[0],pivotStr[1],pivotStr[2]);
-        } else {
-          throw new SolrException(ErrorCode.BAD_REQUEST,"Result request "+facetValue+" has an invalid amount of arguments.");
-        }
-      } catch (IndexOutOfBoundsException e) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"Result request "+facetValue+" is invalid. Lacks parentheses.",e);
-      }
-    } 
-    if (queryFacetExpressions.get(queryFacet)!=null) {
-      Expression[] facetExpressions = queryFacetExpressions.get(queryFacet).get(facetValue);
-      for (int count = 0; count < expressionNames.length; count++) {
-        if (expressionName.equals(expressionNames[count])) {
-          Comparable value = facetExpressions[count].getValue();
-          if (value.getClass().equals(Date.class)) {
-            return ((Date)value).toInstant().toString();
-          } else {
-            return value.toString();
-          }
-        }
-      }
-    }
-    throw new SolrException(ErrorCode.BAD_REQUEST,"Field Facet Pivot expression "+expressionName+" not found.");
-  }
-  
-  @Override
-  @SuppressWarnings("unchecked")
-  public NamedList<?> export() {
-    final NamedList<Object> base = (NamedList<Object>)super.export();
-    NamedList<NamedList<?>> facetList = new NamedList<>();
-    
-    // Add the field facet buckets to the output
-    base.add("fieldFacets",facetList);
-    for( FieldFacetRequest freq : request.getFieldFacets() ){
-      final String name = freq.getName();
-      if (hiddenFieldFacets.contains(name)) {
-        continue;
-      }
-      final Map<String,Expression[]> buckets = fieldFacetExpressions.get(name);
-      final NamedList<Object> bucketBase = new NamedList<>();
-
-      Iterable<Entry<String,Expression[]>> iter = buckets.entrySet();
-      
-      final FieldFacetRequest fr = (FieldFacetRequest) freq;
-     
-      final FacetSortSpecification sort = fr.getSort();
-      final int limit = fr.getLimit();
-      final int offset = fr.getOffset();
-      final boolean showMissing = fr.showsMissing();
-      if (!showMissing) {
-        buckets.remove(MISSING_VALUE);
-      }
-      // Sorting the buckets if a sort specification is provided
-      if( sort != null && buckets.values().iterator().hasNext()){
-        int sortPlace = Arrays.binarySearch(expressionNames, sort.getStatistic());
-        final Expression first = buckets.values().iterator().next()[sortPlace];
-        final Comparator<Expression> comp = (Comparator<Expression>) first.comparator(sort.getDirection());
-        
-        final List<Entry<String,Expression[]>> sorted = new ArrayList<>(buckets.size());
-        Iterables.addAll(sorted, iter);
-        Collections.sort(sorted, new EntryComparator(comp,sortPlace));
-        iter = sorted;
-      }
-      // apply the limit
-      if( limit > AnalyticsContentHandler.DEFAULT_FACET_LIMIT ){
-        if( offset > 0 ){
-          iter = Iterables.skip(iter, offset);
-        }
-        iter = Iterables.limit(iter, limit);
-      }
-      
-      // Export each expression in the bucket.
-      for( Entry<String,Expression[]> bucket : iter ){
-        bucketBase.add(bucket.getKey(),export(bucket.getValue()));
-      }
-      
-      facetList.add(name, bucketBase);
-    }
-
-    // Add the range facet buckets to the output
-    facetList = new NamedList<>();
-    base.add("rangeFacets",facetList);
-    for( RangeFacetRequest freq : request.getRangeFacets() ){
-      final String name = freq.getName();
-      final Map<String,Expression[]> buckets = rangeFacetExpressions.get(name);
-      final NamedList<Object> bucketBase = new NamedList<>();
-
-      Iterable<Entry<String,Expression[]>> iter = buckets.entrySet();
-      
-      for( Entry<String,Expression[]> bucket : iter ){
-        bucketBase.add(bucket.getKey(),export(bucket.getValue()));
-      }
-      
-      facetList.add(name, bucketBase);
-    }
-    
-    // Add the query facet buckets to the output
-    facetList = new NamedList<>();
-    base.add("queryFacets",facetList);
-    for( QueryFacetRequest freq : request.getQueryFacets() ){
-      final String name = freq.getName();
-      final Map<String,Expression[]> buckets = queryFacetExpressions.get(name);
-      final NamedList<Object> bucketBase = new NamedList<>();
-
-      Iterable<Entry<String,Expression[]>> iter = buckets.entrySet();
-      
-      for( Entry<String,Expression[]> bucket : iter ){
-        bucketBase.add(bucket.getKey(),export(bucket.getValue()));
-      }
-      
-      facetList.add(name, bucketBase);
-    }
-
-    return base;
-  }
-  
-  /**
-   * Exports a list of expressions as a NamedList
-   * @param expressionArr an array of expressions
-   * @return named list of expressions
-   */
-  public NamedList<?> export(Expression[] expressionArr) {
-    NamedList<Object> base = new NamedList<>();
-    for (int count = 0; count < expressionArr.length; count++) {
-      if (!hiddenExpressions.contains(expressionNames[count])) {
-        base.add(expressionNames[count], expressionArr[count].getValue());
-      }
-    }
-    return base;
-  }
-
-  /**
-   * Processes the query and range facets.
-   * Must be called if range and/or query facets are supported.
-   */
-  @Override
-  public void postProcess() throws IOException {
-    super.compute();
-    for( Map<String, StatsCollector[]> f : fieldFacetCollectors.values() ){
-      for( StatsCollector[] arr : f.values() ){
-        for( StatsCollector b : arr ){
-          b.compute();
-        }
-      }
-    }
-    basicsAndFieldFacetsComputed = true;
-    final Filter filter = docs.getTopFilter();
-    if( rangeFacets != null ){
-      processRangeFacets(filter); 
-    }
-    if( queryFacets != null ){
-      processQueryFacets(filter); 
-    }
-  }
-  
-  /**
-   * Initiates the collecting of query facets
-   * @param filter the base filter to work against
-   * @throws IOException if searching failed
-   */
-  public void processQueryFacets(final Filter filter) throws IOException {
-    for( QueryFacetRequest qfr : queryFacets ){
-      for( String query : qfr.getQueries() ){
-        if (query.contains(AnalyticsParams.RESULT) && !query.contains(AnalyticsParams.QUERY_RESULT)) {
-          try {
-            String[] pivotStr = ExpressionFactory.getArguments(query.substring(query.indexOf('(')+1,query.lastIndexOf(')')).trim());
-            if (pivotStr.length==1) {
-              query = getResult(pivotStr[0]);
-            } else if (pivotStr.length==3) {
-              query = getResult(pivotStr[0],pivotStr[1],pivotStr[2]);
-            } else {
-              throw new SolrException(ErrorCode.BAD_REQUEST,"Result request "+query+" has an invalid amount of arguments.");
-            }
-          } catch (IndexOutOfBoundsException e) {
-            throw new SolrException(ErrorCode.BAD_REQUEST,"Result request "+query+" is invalid. Lacks parentheses.",e);
-          }
-        } else if (query.contains(AnalyticsParams.QUERY_RESULT)) {
-          try {
-            String[] pivotStr = ExpressionFactory.getArguments(query.substring(query.indexOf('(')+1,query.lastIndexOf(')')).trim());
-            if (pivotStr.length==3) {
-              query = getQueryResult(qfr.getName(),pivotStr[0],pivotStr[1],pivotStr[2]);
-            } else {
-              throw new SolrException(ErrorCode.BAD_REQUEST,"Result request "+query+" has an invalid amount of arguments.");
-            }
-          } catch (IndexOutOfBoundsException e) {
-            throw new SolrException(ErrorCode.BAD_REQUEST,"Result request "+query+" is invalid. Lacks parentheses.",e);
-          }
-        }
-        QueryFacetAccumulator qAcc = new QueryFacetAccumulator(this,qfr.getName(),query);
-        final Query q;
-        try {
-          q = QParser.getParser(query, queryRequest).getQuery();
-        } catch( SyntaxError e ){
-          throw new SolrException(ErrorCode.BAD_REQUEST,"Invalid query '"+query+"'",e);
-        }
-        // The searcher sends docIds to the QueryFacetAccumulator which forwards
-        // them to <code>collectQuery()</code> in this class for collection.
-        Query filtered = new BooleanQuery.Builder()
-            .add(q, Occur.MUST)
-            .add(filter, Occur.FILTER)
-            .build();
-        searcher.search(filtered, qAcc);
-        computeQueryFacet(qfr.getName());
-        queryCount++;
-      }
-    }
-  }
-  
-  @Override
-  public long getNumQueries() {
-    return queryCount;
-  }
-
-  /**
-   * Initiates the collecting of range facets
-   * @param filter the base filter to use
-   * @throws IOException if searching fails
-   */
-  public void processRangeFacets(final Filter filter) throws IOException {
-    for( RangeFacetRequest rfr : rangeFacets ){
-      String[] pivotStr;
-      String start = rfr.getStart();
-      if (start.contains(AnalyticsParams.QUERY_RESULT)) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"Query result requests can not be used in Range Facets");
-      } else if (start.contains(AnalyticsParams.RESULT)) {
-        try {
-          pivotStr = ExpressionFactory.getArguments(start.substring(start.indexOf('(')+1,start.indexOf(')')).trim());
-          if (pivotStr.length==1) {
-            rfr.setStart(getResult(pivotStr[0]));
-          } else if (pivotStr.length==3) {
-            rfr.setStart(getResult(pivotStr[0],pivotStr[1],pivotStr[2]));
-          } else {
-            throw new SolrException(ErrorCode.BAD_REQUEST, "Result request "+start+" has an invalid amount of arguments.");
-          }
-        } catch (IndexOutOfBoundsException e) {
-          throw new SolrException(ErrorCode.BAD_REQUEST, "Result request "+start+" is invalid. Lacks parentheses.",e);
-        }
-      }
-      String end = rfr.getEnd();
-      if (end.contains(AnalyticsParams.QUERY_RESULT)) {
-        throw new SolrException(ErrorCode.BAD_REQUEST, "Query result requests can not be used in Range Facets");
-      } else if (end.contains(AnalyticsParams.RESULT)) {
-        try {
-          pivotStr = ExpressionFactory.getArguments(end.substring(end.indexOf('(')+1,end.indexOf(')')).trim());
-          if (pivotStr.length==1) {
-            rfr.setEnd(getResult(pivotStr[0]));
-          } else if (pivotStr.length==3) {
-            rfr.setEnd(getResult(pivotStr[0],pivotStr[1],pivotStr[2]));
-          } else {
-            throw new SolrException(ErrorCode.BAD_REQUEST, "Result request "+end+" has an invalid amount of arguments.");
-          }
-        } catch (IndexOutOfBoundsException e) {
-          throw new SolrException(ErrorCode.BAD_REQUEST, "Result request "+end+" is invalid. Lacks parentheses.",e);
-        }
-      }
-      String[] gaps = rfr.getGaps();
-      for (int count = 0; count<gaps.length; count++){
-        String gap = gaps[count];
-        if (gap.contains(AnalyticsParams.QUERY_RESULT)) {
-          throw new SolrException(ErrorCode.BAD_REQUEST, "Query result requests can not be used in Range Facets");
-        } else if (gap.contains(AnalyticsParams.RESULT)) {
-          try {
-            pivotStr = ExpressionFactory.getArguments(gap.substring(gap.indexOf('(')+1,gap.indexOf(')')).trim());
-            if (pivotStr.length==1) {
-              gaps[count]=getResult(pivotStr[0]);
-            } else if (pivotStr.length==3) {
-              gaps[count]=getResult(pivotStr[0],pivotStr[1],pivotStr[2]);
-            } else {
-              throw new SolrException(ErrorCode.BAD_REQUEST, "Result request "+gap+" has an invalid amount of arguments.");
-            }
-          } catch (IndexOutOfBoundsException e) {
-            throw new SolrException(ErrorCode.BAD_REQUEST, "Result request "+gap+" is invalid. Lacks parentheses.",e);
-          }
-        }
-      }
-      // Computes the end points of the ranges in the rangeFacet
-      final RangeEndpointCalculator<? extends Comparable<?>> rec = RangeEndpointCalculator.create(rfr);
-      final SchemaField sf = rfr.getField();
-      
-      // Create a rangeFacetAccumulator for each range and 
-      // collect the documents for that range.
-      for( FacetRange range : rec.getRanges() ){
-        final String upper;
-        final String lower;
-        String facetValue = "";
-        if( range.lower == null ){
-          facetValue = "(*";
-          lower = null;
-        } else {
-          lower = range.lower;
-          facetValue = ((range.includeLower)?"[":"(") + range.lower;
-        }
-        facetValue+=" TO ";
-        if( range.upper == null ){
-          upper = null;
-          facetValue += "*)";
-        } else {
-          upper = range.upper;
-          facetValue += range.upper + ((range.includeUpper)?"]":")");
-        }
-        
-        Query q = sf.getType().getRangeQuery(null, sf, lower, upper, range.includeLower,range.includeUpper);
-        RangeFacetAccumulator rAcc = new RangeFacetAccumulator(this,rfr.getName(),facetValue);
-        // The searcher sends docIds to the RangeFacetAccumulator which forwards
-        // them to <code>collectRange()</code> in this class for collection.
-        Query filtered = new BooleanQuery.Builder()
-            .add(q, Occur.MUST)
-            .add(filter, Occur.FILTER)
-            .build();
-        searcher.search(filtered, rAcc);
-        computeRangeFacet(sf.getName());
-      }
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/ValueAccumulator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/ValueAccumulator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/ValueAccumulator.java
deleted file mode 100644
index 489d3de..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/ValueAccumulator.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.solr.analytics.accumulator;
-
-import java.io.IOException;
-
-import org.apache.lucene.search.SimpleCollector;
-import org.apache.solr.common.util.NamedList;
-
-/**
- * Abstract Collector that manages all StatsCollectors, Expressions and Facets.
- */
-public abstract class ValueAccumulator extends SimpleCollector {
-  
-  /**
-   * Finalizes the statistics within each StatsCollector.
-   * Must be called before <code>export()</code>.
-   */
-  public abstract void compute();
-  public abstract NamedList<?> export();
-  
-  public void postProcess() throws IOException {
-    // NOP
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/FacetValueAccumulator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/FacetValueAccumulator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/FacetValueAccumulator.java
deleted file mode 100644
index 1b6fbb7..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/FacetValueAccumulator.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.solr.analytics.accumulator.facet;
-
-import java.io.IOException;
-
-import org.apache.lucene.index.LeafReaderContext;
-
-/**
- * Interface that describes the methods needed for an Accumulator to be able to handle 
- * fieldFacets, rangeFacets and queryFacets.
- */
-public interface FacetValueAccumulator {
-
-  void collectField(int doc, String facetName, String facetValue) throws IOException;
-  void collectQuery(int doc, String facetName, String facetValue) throws IOException;
-  void collectRange(int doc, String facetName, String facetValue) throws IOException;
-  void setQueryStatsCollectorReaders(LeafReaderContext context) throws IOException;
-  void setRangeStatsCollectorReaders(LeafReaderContext context) throws IOException;
-
-}


[15/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/FillMissingFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/FillMissingFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/FillMissingFunction.java
new file mode 100644
index 0000000..188d698
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/FillMissingFunction.java
@@ -0,0 +1,842 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.AnalyticsValue;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.BooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValue;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValue;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.AnalyticsValue.AbstractAnalyticsValue;
+import org.apache.solr.analytics.value.BooleanValue.AbstractBooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream.AbstractBooleanValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DateValueStream.AbstractDateValueStream;
+import org.apache.solr.analytics.value.DoubleValue.AbstractDoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream.AbstractDoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue.AbstractFloatValue;
+import org.apache.solr.analytics.value.FloatValueStream.AbstractFloatValueStream;
+import org.apache.solr.analytics.value.IntValue.AbstractIntValue;
+import org.apache.solr.analytics.value.IntValueStream.AbstractIntValueStream;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.value.LongValueStream.AbstractLongValueStream;
+import org.apache.solr.analytics.value.StringValue.AbstractStringValue;
+import org.apache.solr.analytics.value.StringValueStream.AbstractStringValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A mapping function to fill all non-existing values with a given value. 
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If two Values are passed in, a Value mimicking the first parameter with the second parameter used as filler will be returned.
+ * <li>If two ValueStreams are passed in, a ValueStream mimicking the first parameter with the second parameter used as filler will be returned.
+ * </ul>
+ * <p>
+ * The resulting Value or ValueStream will be typed with the closest super-type of the two parameters.
+ * (e.g. {@value #name}(double,int) will return a double)
+ */
+public class FillMissingFunction {
+  public static final String name = "fillmissing";
+
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 2) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 2 paramaters, " + params.length + " found.");
+    }
+
+    AnalyticsValueStream baseExpr = params[0];
+    AnalyticsValueStream fillExpr = params[1];
+    if (baseExpr instanceof DateValue && fillExpr instanceof DateValue) {
+      return new DateFillMissingFunction((DateValue)baseExpr,(DateValue)fillExpr);
+    }
+    if (baseExpr instanceof DateValueStream && fillExpr instanceof DateValueStream) {
+      return new DateStreamFillMissingFunction((DateValueStream)baseExpr,(DateValueStream)fillExpr);
+    }
+    if (baseExpr instanceof BooleanValue && fillExpr instanceof BooleanValue) {
+      return new BooleanFillMissingFunction((BooleanValue)baseExpr,(BooleanValue)fillExpr);
+    }
+    if (baseExpr instanceof BooleanValueStream && fillExpr instanceof BooleanValueStream) {
+      return new BooleanStreamFillMissingFunction((BooleanValueStream)baseExpr,(BooleanValueStream)fillExpr);
+    }
+    if (baseExpr instanceof IntValue && fillExpr instanceof IntValue) {
+      return new IntFillMissingFunction((IntValue)baseExpr,(IntValue)fillExpr);
+    }
+    if (baseExpr instanceof IntValueStream && fillExpr instanceof IntValueStream) {
+      return new IntStreamFillMissingFunction((IntValueStream)baseExpr,(IntValueStream)fillExpr);
+    }
+    if (baseExpr instanceof LongValue && fillExpr instanceof LongValue) {
+      return new LongFillMissingFunction((LongValue)baseExpr,(LongValue)fillExpr);
+    }
+    if (baseExpr instanceof LongValueStream && fillExpr instanceof LongValueStream) {
+      return new LongStreamFillMissingFunction((LongValueStream)baseExpr,(LongValueStream)fillExpr);
+    }
+    if (baseExpr instanceof FloatValue && fillExpr instanceof FloatValue) {
+      return new FloatFillMissingFunction((FloatValue)baseExpr,(FloatValue)fillExpr);
+    }
+    if (baseExpr instanceof FloatValueStream && fillExpr instanceof FloatValueStream) {
+      return new FloatStreamFillMissingFunction((FloatValueStream)baseExpr,(FloatValueStream)fillExpr);
+    }
+    if (baseExpr instanceof DoubleValue && fillExpr instanceof DoubleValue) {
+      return new DoubleFillMissingFunction((DoubleValue)baseExpr,(DoubleValue)fillExpr);
+    }
+    if (baseExpr instanceof DoubleValueStream && fillExpr instanceof DoubleValueStream) {
+      return new DoubleStreamFillMissingFunction((DoubleValueStream)baseExpr,(DoubleValueStream)fillExpr);
+    }
+    if (baseExpr instanceof StringValue && fillExpr instanceof StringValue) {
+      return new StringFillMissingFunction((StringValue)baseExpr,(StringValue)fillExpr);
+    }
+    if (baseExpr instanceof StringValueStream && fillExpr instanceof StringValueStream) {
+      return new StringStreamFillMissingFunction((StringValueStream)baseExpr,(StringValueStream)fillExpr);
+    }
+    if (baseExpr instanceof AnalyticsValue && fillExpr instanceof AnalyticsValue) {
+      return new ValueFillMissingFunction((AnalyticsValue)baseExpr,(AnalyticsValue)fillExpr);
+    }
+    return new StreamFillMissingFunction(baseExpr,fillExpr);
+  });
+}
+class StreamFillMissingFunction implements AnalyticsValueStream, Consumer<Object> {
+  private final AnalyticsValueStream baseExpr;
+  private final AnalyticsValueStream fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StreamFillMissingFunction(AnalyticsValueStream baseExpr, AnalyticsValueStream fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+
+  boolean exists = false;
+  Consumer<Object> cons;
+
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    exists = false;
+    this.cons = cons;
+    baseExpr.streamObjects(this);
+    if (!exists) {
+      fillExpr.streamObjects(cons);
+    }
+  }
+  @Override
+  public void accept(Object value) {
+    exists = true;
+    cons.accept(value);
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class ValueFillMissingFunction extends AbstractAnalyticsValue {
+  private final AnalyticsValue baseExpr;
+  private final AnalyticsValue fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public ValueFillMissingFunction(AnalyticsValue baseExpr, AnalyticsValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public Object getObject() {
+    Object value = baseExpr.getObject();
+    exists = true;
+    if (!baseExpr.exists()) {
+      value = fillExpr.getObject();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanStreamFillMissingFunction extends AbstractBooleanValueStream implements BooleanConsumer {
+  private final BooleanValueStream baseExpr;
+  private final BooleanValueStream fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanStreamFillMissingFunction(BooleanValueStream baseExpr, BooleanValueStream fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+
+  boolean exists = false;
+  BooleanConsumer cons;
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    exists = false;
+    this.cons = cons;
+    baseExpr.streamBooleans(this);
+    if (!exists) {
+      fillExpr.streamBooleans(cons);
+    }
+  }
+  @Override
+  public void accept(boolean value) {
+    exists = true;
+    cons.accept(value);
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanFillMissingFunction extends AbstractBooleanValue {
+  private final BooleanValue baseExpr;
+  private final BooleanValue fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanFillMissingFunction(BooleanValue baseExpr, BooleanValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public boolean getBoolean() {
+    boolean value = baseExpr.getBoolean();
+    exists = true;
+    if (!baseExpr.exists()) {
+      value = fillExpr.getBoolean();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntStreamFillMissingFunction extends AbstractIntValueStream implements IntConsumer {
+  private final IntValueStream baseExpr;
+  private final IntValueStream fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntStreamFillMissingFunction(IntValueStream baseExpr, IntValueStream fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+
+  boolean exists = false;
+  IntConsumer cons;
+
+  @Override
+  public void streamInts(IntConsumer cons) {
+    exists = false;
+    this.cons = cons;
+    baseExpr.streamInts(this);
+    if (!exists) {
+      fillExpr.streamInts(cons);
+    }
+  }
+  @Override
+  public void accept(int value) {
+    exists = true;
+    cons.accept(value);
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntFillMissingFunction extends AbstractIntValue {
+  private final IntValue baseExpr;
+  private final IntValue fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntFillMissingFunction(IntValue baseExpr, IntValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public int getInt() {
+    int value = baseExpr.getInt();
+    exists = true;
+    if (!baseExpr.exists()) {
+      value = fillExpr.getInt();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongStreamFillMissingFunction extends AbstractLongValueStream implements LongConsumer {
+  private final LongValueStream baseExpr;
+  private final LongValueStream fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongStreamFillMissingFunction(LongValueStream baseExpr, LongValueStream fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+
+  boolean exists = false;
+  LongConsumer cons;
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    exists = false;
+    this.cons = cons;
+    baseExpr.streamLongs(this);
+    if (!exists) {
+      fillExpr.streamLongs(cons);
+    }
+  }
+  @Override
+  public void accept(long value) {
+    exists = true;
+    cons.accept(value);
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongFillMissingFunction extends AbstractLongValue {
+  private final LongValue baseExpr;
+  private final LongValue fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongFillMissingFunction(LongValue baseExpr, LongValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public long getLong() {
+    long value = baseExpr.getLong();
+    exists = true;
+    if (!baseExpr.exists()) {
+      value = fillExpr.getLong();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatStreamFillMissingFunction extends AbstractFloatValueStream implements FloatConsumer {
+  private final FloatValueStream baseExpr;
+  private final FloatValueStream fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatStreamFillMissingFunction(FloatValueStream baseExpr, FloatValueStream fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+
+  boolean exists = false;
+  FloatConsumer cons;
+
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    exists = false;
+    this.cons = cons;
+    baseExpr.streamFloats(this);
+    if (!exists) {
+      fillExpr.streamFloats(cons);
+    }
+  }
+  @Override
+  public void accept(float value) {
+    exists = true;
+    cons.accept(value);
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatFillMissingFunction extends AbstractFloatValue {
+  private final FloatValue baseExpr;
+  private final FloatValue fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatFillMissingFunction(FloatValue baseExpr, FloatValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public float getFloat() {
+    float value = baseExpr.getFloat();
+    exists = true;
+    if (!baseExpr.exists()) {
+      value = fillExpr.getFloat();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DoubleStreamFillMissingFunction extends AbstractDoubleValueStream implements DoubleConsumer {
+  private final DoubleValueStream baseExpr;
+  private final DoubleValueStream fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DoubleStreamFillMissingFunction(DoubleValueStream baseExpr, DoubleValueStream fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+
+  boolean exists = false;
+  DoubleConsumer cons;
+
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    exists = false;
+    this.cons = cons;
+    baseExpr.streamDoubles(this);
+    if (!exists) {
+      fillExpr.streamDoubles(cons);
+    }
+  }
+  @Override
+  public void accept(double value) {
+    exists = true;
+    cons.accept(value);
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DoubleFillMissingFunction extends AbstractDoubleValue {
+  private final DoubleValue baseExpr;
+  private final DoubleValue fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DoubleFillMissingFunction(DoubleValue baseExpr, DoubleValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public double getDouble() {
+    double value = baseExpr.getDouble();
+    exists = true;
+    if (!baseExpr.exists()) {
+      value = fillExpr.getDouble();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DateStreamFillMissingFunction extends AbstractDateValueStream implements LongConsumer {
+  private final DateValueStream baseExpr;
+  private final DateValueStream fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateStreamFillMissingFunction(DateValueStream baseExpr, DateValueStream fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+
+  boolean exists = false;
+  LongConsumer cons;
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    exists = false;
+    this.cons = cons;
+    baseExpr.streamLongs(this);
+    if (!exists) {
+      fillExpr.streamLongs(cons);
+    }
+  }
+  @Override
+  public void accept(long value) {
+    exists = true;
+    cons.accept(value);
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DateFillMissingFunction extends AbstractDateValue {
+  private final DateValue baseExpr;
+  private final DateValue fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateFillMissingFunction(DateValue baseExpr, DateValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public long getLong() {
+    long value = baseExpr.getLong();
+    exists = true;
+    if (!baseExpr.exists()) {
+      value = fillExpr.getLong();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringStreamFillMissingFunction extends AbstractStringValueStream implements Consumer<String> {
+  private final StringValueStream baseExpr;
+  private final StringValueStream fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StringStreamFillMissingFunction(StringValueStream baseExpr, StringValueStream fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+
+  boolean exists = false;
+  Consumer<String> cons;
+
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    exists = false;
+    this.cons = cons;
+    baseExpr.streamStrings(this);
+    if (!exists) {
+      fillExpr.streamStrings(cons);
+    }
+  }
+  @Override
+  public void accept(String value) {
+    exists = true;
+    cons.accept(value);
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringFillMissingFunction extends AbstractStringValue {
+  private final StringValue baseExpr;
+  private final StringValue fillExpr;
+  public static final String name = FillMissingFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StringFillMissingFunction(StringValue baseExpr, StringValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public String getString() {
+    String value = baseExpr.getString();
+    exists = true;
+    if (!baseExpr.exists()) {
+      value = fillExpr.getString();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/FilterFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/FilterFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/FilterFunction.java
new file mode 100644
index 0000000..84a3e30
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/FilterFunction.java
@@ -0,0 +1,722 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.AnalyticsValue;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.BooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValue;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValue;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.AnalyticsValue.AbstractAnalyticsValue;
+import org.apache.solr.analytics.value.BooleanValue.AbstractBooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream.AbstractBooleanValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DateValueStream.AbstractDateValueStream;
+import org.apache.solr.analytics.value.DoubleValue.AbstractDoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream.AbstractDoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue.AbstractFloatValue;
+import org.apache.solr.analytics.value.FloatValueStream.AbstractFloatValueStream;
+import org.apache.solr.analytics.value.IntValue.AbstractIntValue;
+import org.apache.solr.analytics.value.IntValueStream.AbstractIntValueStream;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.value.LongValueStream.AbstractLongValueStream;
+import org.apache.solr.analytics.value.StringValue.AbstractStringValue;
+import org.apache.solr.analytics.value.StringValueStream.AbstractStringValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A mapping function to filter a Value or ValueStream. For each document, the value exists if the second parameter 
+ * is true and it doesn't exist otherwise.
+ * <p>
+ * The first parameter can be any type of analytics expression. (Required)
+ * <br>
+ * The second parameter must be a {@link BooleanValue}. (Required)
+ */
+public class FilterFunction {
+  public static final String name = "filter";
+
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 2) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 2 paramaters, " + params.length + " found.");
+    }
+    if (!(params[1] instanceof BooleanValue)) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires the second paramater to be single-valued and boolean.");
+    }
+
+    AnalyticsValueStream baseExpr = params[0];
+    BooleanValue filterExpr = (BooleanValue)params[1];
+
+    if (baseExpr instanceof DateValue) {
+      return new DateFilterFunction((DateValue)baseExpr,filterExpr);
+    }
+    if (baseExpr instanceof DateValueStream) {
+      return new DateStreamFilterFunction((DateValueStream)baseExpr,filterExpr);
+    }
+    if (baseExpr instanceof BooleanValue) {
+      return new BooleanFilterFunction((BooleanValue)baseExpr,filterExpr);
+    }
+    if (baseExpr instanceof BooleanValueStream) {
+      return new BooleanStreamFilterFunction((BooleanValueStream)baseExpr,filterExpr);
+    }
+    if (baseExpr instanceof IntValue) {
+      return new IntFilterFunction((IntValue)baseExpr,filterExpr);
+    }
+    if (baseExpr instanceof IntValueStream) {
+      return new IntStreamFilterFunction((IntValueStream)baseExpr,filterExpr);
+    }
+    if (baseExpr instanceof LongValue) {
+      return new LongFilterFunction((LongValue)baseExpr,filterExpr);
+    }
+    if (baseExpr instanceof LongValueStream) {
+      return new LongStreamFilterFunction((LongValueStream)baseExpr,filterExpr);
+    }
+    if (baseExpr instanceof FloatValue) {
+      return new FloatFilterFunction((FloatValue)baseExpr,filterExpr);
+    }
+    if (baseExpr instanceof FloatValueStream) {
+      return new FloatStreamFilterFunction((FloatValueStream)baseExpr,filterExpr);
+    }
+    if (baseExpr instanceof DoubleValue) {
+      return new DoubleFilterFunction((DoubleValue)baseExpr,filterExpr);
+    }
+    if (baseExpr instanceof DoubleValueStream) {
+      return new DoubleStreamFilterFunction((DoubleValueStream)baseExpr,filterExpr);
+    }
+    if (baseExpr instanceof StringValue) {
+      return new StringFilterFunction((StringValue)baseExpr,filterExpr);
+    }
+    if (baseExpr instanceof StringValueStream) {
+      return new StringStreamFilterFunction((StringValueStream)baseExpr,filterExpr);
+    }
+    if (baseExpr instanceof AnalyticsValue) {
+      return new ValueFilterFunction((AnalyticsValue)baseExpr,filterExpr);
+    }
+    return new StreamFilterFunction(baseExpr,filterExpr);
+  });
+}
+class StreamFilterFunction implements AnalyticsValueStream {
+  private final AnalyticsValueStream baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StreamFilterFunction(AnalyticsValueStream baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    if (filterExpr.getBoolean() && filterExpr.exists()) {
+      baseExpr.streamObjects(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class ValueFilterFunction extends AbstractAnalyticsValue {
+  private final AnalyticsValue baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public ValueFilterFunction(AnalyticsValue baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public Object getObject() {
+    Object value = baseExpr.getObject();
+    exists = baseExpr.exists() && filterExpr.getBoolean() && filterExpr.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanStreamFilterFunction extends AbstractBooleanValueStream {
+  private final BooleanValueStream baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanStreamFilterFunction(BooleanValueStream baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    if (filterExpr.getBoolean() && filterExpr.exists()) {
+      baseExpr.streamBooleans(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanFilterFunction extends AbstractBooleanValue {
+  private final BooleanValue baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanFilterFunction(BooleanValue baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public boolean getBoolean() {
+    boolean value = baseExpr.getBoolean();
+    exists = baseExpr.exists() && filterExpr.getBoolean() && filterExpr.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntStreamFilterFunction extends AbstractIntValueStream {
+  private final IntValueStream baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntStreamFilterFunction(IntValueStream baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+
+  @Override
+  public void streamInts(IntConsumer cons) {
+    if (filterExpr.getBoolean() && filterExpr.exists()) {
+      baseExpr.streamInts(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntFilterFunction extends AbstractIntValue {
+  private final IntValue baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntFilterFunction(IntValue baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public int getInt() {
+    int value = baseExpr.getInt();
+    exists = baseExpr.exists() && filterExpr.getBoolean() && filterExpr.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongStreamFilterFunction extends AbstractLongValueStream {
+  private final LongValueStream baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongStreamFilterFunction(LongValueStream baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    if (filterExpr.getBoolean() && filterExpr.exists()) {
+      baseExpr.streamLongs(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongFilterFunction extends AbstractLongValue {
+  private final LongValue baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongFilterFunction(LongValue baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public long getLong() {
+    long value = baseExpr.getLong();
+    exists = baseExpr.exists() && filterExpr.getBoolean() && filterExpr.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatStreamFilterFunction extends AbstractFloatValueStream {
+  private final FloatValueStream baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatStreamFilterFunction(FloatValueStream baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    if (filterExpr.getBoolean() && filterExpr.exists()) {
+      baseExpr.streamFloats(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatFilterFunction extends AbstractFloatValue {
+  private final FloatValue baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatFilterFunction(FloatValue baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public float getFloat() {
+    float value = baseExpr.getFloat();
+    exists = baseExpr.exists() && filterExpr.getBoolean() && filterExpr.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DoubleStreamFilterFunction extends AbstractDoubleValueStream {
+  private final DoubleValueStream baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DoubleStreamFilterFunction(DoubleValueStream baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    if (filterExpr.getBoolean() && filterExpr.exists()) {
+      baseExpr.streamDoubles(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DoubleFilterFunction extends AbstractDoubleValue {
+  private final DoubleValue baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DoubleFilterFunction(DoubleValue baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public double getDouble() {
+    double value = baseExpr.getDouble();
+    exists = baseExpr.exists() && filterExpr.getBoolean() && filterExpr.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DateStreamFilterFunction extends AbstractDateValueStream {
+  private final DateValueStream baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateStreamFilterFunction(DateValueStream baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    if (filterExpr.getBoolean() && filterExpr.exists()) {
+      baseExpr.streamLongs(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DateFilterFunction extends AbstractDateValue {
+  private final DateValue baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateFilterFunction(DateValue baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public long getLong() {
+    long value = baseExpr.getLong();
+    exists = baseExpr.exists() && filterExpr.getBoolean() && filterExpr.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringStreamFilterFunction extends AbstractStringValueStream {
+  private final StringValueStream baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StringStreamFilterFunction(StringValueStream baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    if (filterExpr.getBoolean() && filterExpr.exists()) {
+      baseExpr.streamStrings(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringFilterFunction extends AbstractStringValue {
+  private final StringValue baseExpr;
+  private final BooleanValue filterExpr;
+  public static final String name = FilterFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StringFilterFunction(StringValue baseExpr, BooleanValue filterExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.filterExpr = filterExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,filterExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,filterExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public String getString() {
+    String value = baseExpr.getString();
+    exists = baseExpr.exists() && filterExpr.getBoolean() && filterExpr.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/IfFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/IfFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/IfFunction.java
new file mode 100644
index 0000000..e2fd163
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/IfFunction.java
@@ -0,0 +1,892 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.AnalyticsValue;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.BooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValue;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValue;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.AnalyticsValue.AbstractAnalyticsValue;
+import org.apache.solr.analytics.value.BooleanValue.AbstractBooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream.AbstractBooleanValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DateValueStream.AbstractDateValueStream;
+import org.apache.solr.analytics.value.DoubleValue.AbstractDoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream.AbstractDoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue.AbstractFloatValue;
+import org.apache.solr.analytics.value.FloatValueStream.AbstractFloatValueStream;
+import org.apache.solr.analytics.value.IntValue.AbstractIntValue;
+import org.apache.solr.analytics.value.IntValueStream.AbstractIntValueStream;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.value.LongValueStream.AbstractLongValueStream;
+import org.apache.solr.analytics.value.StringValue.AbstractStringValue;
+import org.apache.solr.analytics.value.StringValueStream.AbstractStringValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * An if-else mapping function. 
+ * <p>
+ * Three arguments are required. The first, the conditional parameter, must be a {@link BooleanValue} and
+ * the later two, the if and else parameters, can be any type of {@link AnalyticsValueStream}. 
+ * For each document, if the conditional value is true then the if-value is used otherwise the else-value is used.
+ * <p>
+ * The resulting Value or ValueStream will be typed with the closest super-type of the two non-conditional parameters.
+ * (e.g. {@value #name}(boolean,double,int) will return a double)
+ * If two {@link AnalyticsValue}s are passed as the if-else parameters, an {@link AnalyticsValue} will be returned.
+ * If either parameter isn't single-valued, a {@link AnalyticsValueStream} will be returned.
+ */
+public class IfFunction implements AnalyticsValueStream {
+  private final BooleanValue ifExpr;
+  private final AnalyticsValueStream thenExpr;
+  private final AnalyticsValueStream elseExpr;
+  public static final String name = "if";
+  private final String exprStr;
+  private final ExpressionType funcType;
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 3) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 3 paramaters, " + params.length + " found.");
+    }
+    if (!(params[0] instanceof BooleanValue)) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires single-valued numeric parameters. " + 
+                      "Incorrect parameter: "+params[0].getExpressionStr());
+    }
+    
+    BooleanValue castedIf = (BooleanValue) params[0];
+    AnalyticsValueStream thenExpr = params[1];
+    AnalyticsValueStream elseExpr = params[2];
+
+    if (thenExpr instanceof DateValue && elseExpr instanceof DateValue) {
+      return new DateIfFunction(castedIf,(DateValue)thenExpr,(DateValue)elseExpr);
+    }
+    if (thenExpr instanceof DateValueStream && elseExpr instanceof DateValueStream) {
+      return new DateStreamIfFunction(castedIf,(DateValueStream)thenExpr,(DateValueStream)elseExpr);
+    }
+    if (thenExpr instanceof BooleanValue && elseExpr instanceof BooleanValue) {
+      return new BooleanIfFunction(castedIf,(BooleanValue)thenExpr,(BooleanValue)elseExpr);
+    }
+    if (thenExpr instanceof BooleanValueStream && elseExpr instanceof BooleanValueStream) {
+      return new BooleanStreamIfFunction(castedIf,(BooleanValueStream)thenExpr,(BooleanValueStream)elseExpr);
+    }
+    if (thenExpr instanceof IntValue && elseExpr instanceof IntValue) {
+      return new IntIfFunction(castedIf,(IntValue)thenExpr,(IntValue)elseExpr);
+    }
+    if (thenExpr instanceof IntValueStream && elseExpr instanceof IntValueStream) {
+      return new IntStreamIfFunction(castedIf,(IntValueStream)thenExpr,(IntValueStream)elseExpr);
+    }
+    if (thenExpr instanceof LongValue && elseExpr instanceof LongValue) {
+      return new LongIfFunction(castedIf,(LongValue)thenExpr,(LongValue)elseExpr);
+    }
+    if (thenExpr instanceof LongValueStream && elseExpr instanceof LongValueStream) {
+      return new LongStreamIfFunction(castedIf,(LongValueStream)thenExpr,(LongValueStream)elseExpr);
+    }
+    if (thenExpr instanceof FloatValue && elseExpr instanceof FloatValue) {
+      return new FloatIfFunction(castedIf,(FloatValue)thenExpr,(FloatValue)elseExpr);
+    }
+    if (thenExpr instanceof FloatValueStream && elseExpr instanceof FloatValueStream) {
+      return new FloatStreamIfFunction(castedIf,(FloatValueStream)thenExpr,(FloatValueStream)elseExpr);
+    }
+    if (thenExpr instanceof DoubleValue && elseExpr instanceof DoubleValue) {
+      return new DoubleIfFunction(castedIf,(DoubleValue)thenExpr,(DoubleValue)elseExpr);
+    }
+    if (thenExpr instanceof DoubleValueStream && elseExpr instanceof DoubleValueStream) {
+      return new DoubleStreamIfFunction(castedIf,(DoubleValueStream)thenExpr,(DoubleValueStream)elseExpr);
+    }
+    if (thenExpr instanceof StringValue && elseExpr instanceof StringValue) {
+      return new StringIfFunction(castedIf,(StringValue)thenExpr,(StringValue)elseExpr);
+    }
+    if (thenExpr instanceof StringValueStream && elseExpr instanceof StringValueStream) {
+      return new StringStreamIfFunction(castedIf,(StringValueStream)thenExpr,(StringValueStream)elseExpr);
+    }
+    if (thenExpr instanceof AnalyticsValue && elseExpr instanceof AnalyticsValue) {
+      return new ValueIfFunction(castedIf,(AnalyticsValue)thenExpr,(AnalyticsValue)elseExpr);
+    }
+    return new IfFunction(castedIf,thenExpr,elseExpr);
+  });
+  
+  public IfFunction(BooleanValue ifExpr, AnalyticsValueStream thenExpr, AnalyticsValueStream elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        thenExpr.streamObjects(cons);
+      }
+      else {
+        elseExpr.streamObjects(cons);
+      }
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class ValueIfFunction extends AbstractAnalyticsValue {
+  private final BooleanValue ifExpr;
+  private final AnalyticsValue thenExpr;
+  private final AnalyticsValue elseExpr;
+  public static final String name = IfFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public ValueIfFunction(BooleanValue ifExpr, AnalyticsValue thenExpr, AnalyticsValue elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  private boolean exists = false;
+
+  @Override
+  public Object getObject() {
+    exists = false;
+    Object value = null;
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        value = thenExpr.getObject();
+        exists = thenExpr.exists();
+      }
+      else {
+        value = elseExpr.getObject();
+        exists = elseExpr.exists();
+      }
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanStreamIfFunction extends AbstractBooleanValueStream {
+  private final BooleanValue ifExpr;
+  private final BooleanValueStream thenExpr;
+  private final BooleanValueStream elseExpr;
+  public static final String name = IfFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanStreamIfFunction(BooleanValue ifExpr, BooleanValueStream thenExpr, BooleanValueStream elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        thenExpr.streamBooleans(cons);
+      }
+      else {
+        elseExpr.streamBooleans(cons);
+      }
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanIfFunction extends AbstractBooleanValue {
+  private final BooleanValue ifExpr;
+  private final BooleanValue thenExpr;
+  private final BooleanValue elseExpr;
+  public static final String name = IfFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanIfFunction(BooleanValue ifExpr, BooleanValue thenExpr, BooleanValue elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  private boolean exists = false;
+
+  @Override
+  public boolean getBoolean() {
+    exists = false;
+    boolean value = false;
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        value = thenExpr.getBoolean();
+        exists = thenExpr.exists();
+      }
+      else {
+        value = elseExpr.getBoolean();
+        exists = elseExpr.exists();
+      }
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntStreamIfFunction extends AbstractIntValueStream {
+  private final BooleanValue ifExpr;
+  private final IntValueStream thenExpr;
+  private final IntValueStream elseExpr;
+  public static final String name = IfFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntStreamIfFunction(BooleanValue ifExpr, IntValueStream thenExpr, IntValueStream elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  @Override
+  public void streamInts(IntConsumer cons) {
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        thenExpr.streamInts(cons);
+      }
+      else {
+        elseExpr.streamInts(cons);
+      }
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntIfFunction extends AbstractIntValue {
+  private final BooleanValue ifExpr;
+  private final IntValue thenExpr;
+  private final IntValue elseExpr;
+  public static final String name = IfFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntIfFunction(BooleanValue ifExpr, IntValue thenExpr, IntValue elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  private boolean exists = false;
+
+  @Override
+  public int getInt() {
+    exists = false;
+    int value = 0;
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        value = thenExpr.getInt();
+        exists = thenExpr.exists();
+      }
+      else {
+        value = elseExpr.getInt();
+        exists = elseExpr.exists();
+      }
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongStreamIfFunction extends AbstractLongValueStream {
+  private final BooleanValue ifExpr;
+  private final LongValueStream thenExpr;
+  private final LongValueStream elseExpr;
+  public static final String name = IfFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongStreamIfFunction(BooleanValue ifExpr, LongValueStream thenExpr, LongValueStream elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        thenExpr.streamLongs(cons);
+      }
+      else {
+        elseExpr.streamLongs(cons);
+      }
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongIfFunction extends AbstractLongValue {
+  private final BooleanValue ifExpr;
+  private final LongValue thenExpr;
+  private final LongValue elseExpr;
+  public static final String name = IfFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongIfFunction(BooleanValue ifExpr, LongValue thenExpr, LongValue elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  private boolean exists = false;
+
+  @Override
+  public long getLong() {
+    exists = false;
+    long value = 0;
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        value = thenExpr.getLong();
+        exists = thenExpr.exists();
+      }
+      else {
+        value = elseExpr.getLong();
+        exists = elseExpr.exists();
+      }
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatStreamIfFunction extends AbstractFloatValueStream {
+  private final BooleanValue ifExpr;
+  private final FloatValueStream thenExpr;
+  private final FloatValueStream elseExpr;
+  public static final String name = IfFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatStreamIfFunction(BooleanValue ifExpr, FloatValueStream thenExpr, FloatValueStream elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        thenExpr.streamFloats(cons);
+      }
+      else {
+        elseExpr.streamFloats(cons);
+      }
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatIfFunction extends AbstractFloatValue {
+  private final BooleanValue ifExpr;
+  private final FloatValue thenExpr;
+  private final FloatValue elseExpr;
+  public static final String name = IfFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatIfFunction(BooleanValue ifExpr, FloatValue thenExpr, FloatValue elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  private boolean exists = false;
+
+  @Override
+  public float getFloat() {
+    exists = false;
+    float value = 0;
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        value = thenExpr.getFloat();
+        exists = thenExpr.exists();
+      }
+      else {
+        value = elseExpr.getFloat();
+        exists = elseExpr.exists();
+      }
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DoubleStreamIfFunction extends AbstractDoubleValueStream {
+  private final BooleanValue ifExpr;
+  private final DoubleValueStream thenExpr;
+  private final DoubleValueStream elseExpr;
+  public static final String name = IfFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DoubleStreamIfFunction(BooleanValue ifExpr, DoubleValueStream thenExpr, DoubleValueStream elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        thenExpr.streamDoubles(cons);
+      }
+      else {
+        elseExpr.streamDoubles(cons);
+      }
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DoubleIfFunction extends AbstractDoubleValue {
+  private final BooleanValue ifExpr;
+  private final DoubleValue thenExpr;
+  private final DoubleValue elseExpr;
+  public static final String name = IfFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DoubleIfFunction(BooleanValue ifExpr, DoubleValue thenExpr, DoubleValue elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  private boolean exists = false;
+
+  @Override
+  public double getDouble() {
+    exists = false;
+    double value = 0;
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        value = thenExpr.getDouble();
+        exists = thenExpr.exists();
+      }
+      else {
+        value = elseExpr.getDouble();
+        exists = elseExpr.exists();
+      }
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DateStreamIfFunction extends AbstractDateValueStream {
+  private final BooleanValue ifExpr;
+  private final DateValueStream thenExpr;
+  private final DateValueStream elseExpr;
+  public static final String name = IfFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateStreamIfFunction(BooleanValue ifExpr, DateValueStream thenExpr, DateValueStream elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        thenExpr.streamLongs(cons);
+      }
+      else {
+        elseExpr.streamLongs(cons);
+      }
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DateIfFunction extends AbstractDateValue {
+  private final BooleanValue ifExpr;
+  private final DateValue thenExpr;
+  private final DateValue elseExpr;
+  public static final String name = IfFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateIfFunction(BooleanValue ifExpr, DateValue thenExpr, DateValue elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  private boolean exists = false;
+
+  @Override
+  public long getLong() {
+    exists = false;
+    long value = 0;
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        value = thenExpr.getLong();
+        exists = thenExpr.exists();
+      }
+      else {
+        value = elseExpr.getLong();
+        exists = elseExpr.exists();
+      }
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringStreamIfFunction extends AbstractStringValueStream {
+  private final BooleanValue ifExpr;
+  private final StringValueStream thenExpr;
+  private final StringValueStream elseExpr;
+  public static final String name = IfFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StringStreamIfFunction(BooleanValue ifExpr, StringValueStream thenExpr, StringValueStream elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        thenExpr.streamStrings(cons);
+      }
+      else {
+        elseExpr.streamStrings(cons);
+      }
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringIfFunction extends AbstractStringValue {
+  private final BooleanValue ifExpr;
+  private final StringValue thenExpr;
+  private final StringValue elseExpr;
+  public static final String name = IfFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StringIfFunction(BooleanValue ifExpr, StringValue thenExpr, StringValue elseExpr) throws SolrException {
+    this.ifExpr = ifExpr;
+    this.thenExpr = thenExpr;
+    this.elseExpr = elseExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,ifExpr,thenExpr,elseExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,ifExpr,thenExpr,elseExpr);
+  }
+
+  private boolean exists = false;
+
+  @Override
+  public String getString() {
+    exists = false;
+    String value = null;
+    boolean ifValue = ifExpr.getBoolean();
+    if (ifExpr.exists()) {
+      if (ifValue) {
+        value = thenExpr.getString();
+        exists = thenExpr.exists();
+      }
+      else {
+        value = elseExpr.getString();
+        exists = elseExpr.exists();
+      }
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/JoinFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/JoinFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/JoinFunction.java
new file mode 100644
index 0000000..528aae2
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/JoinFunction.java
@@ -0,0 +1,57 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.constant.ConstantStringValue;
+
+/**
+ * A string join mapping function.
+ * <p>
+ * Takes a {@link StringValueStream} as the first parameter and a {@link ConstantStringValue} (e.g. ",") as the second parameter
+ * and a {@link StringValue} is returned.
+ * <br>
+ * The second parameter is used as the separator while joining all values the first parameter has for a document.
+ * No order is guaranteed while joining the string values
+ */
+public class JoinFunction {
+  public static final String name = "join";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 2) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 2 parameters.");
+    } 
+    AnalyticsValueStream param1 = params[0];
+    AnalyticsValueStream param2 = params[1];
+    if (!(param2 instanceof ConstantStringValue)) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires the second parameter to be a constant string");
+    }
+    String sep = ((StringValue)param2).getString();
+    if (!(param1 instanceof StringValueStream)) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires the first parameter to be castable to a string");
+    }
+    if (param1 instanceof StringValue) {
+      return param1;
+    }
+    String uniqueName = name + "(" + sep + ")";
+    return LambdaFunction.createStringLambdaFunction(uniqueName, (a,b) -> a + sep + b, (StringValueStream)params[0]);
+  });
+}
\ No newline at end of file


[16/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongField.java
new file mode 100644
index 0000000..ac4da2e
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongField.java
@@ -0,0 +1,107 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.NumericDocValues;
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+import org.apache.solr.analytics.value.LongValue.CastingLongValue;
+import org.apache.solr.schema.LongPointField;
+import org.apache.solr.schema.TrieLongField;
+
+/**
+ * An analytics wrapper for a single-valued {@link TrieLongField} or {@link LongPointField} with DocValues enabled.
+ */
+public class LongField extends AnalyticsField implements CastingLongValue {
+  private NumericDocValues docValues;
+  private long value;
+  private boolean exists;
+
+  public LongField(String fieldName) {
+    super(fieldName);
+  }
+
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getNumeric(context.reader(), fieldName);
+  }
+
+  @Override
+  public void collect(int doc) throws IOException {
+    exists = docValues.advanceExact(doc);
+    if (exists) {
+      value = docValues.longValue();
+    }
+  }
+
+  @Override
+  public long getLong() {
+    return value;
+  }
+  @Override
+  public double getDouble() {
+    return (double)value;
+  }
+  @Override
+  public String getString() {
+    return exists ? Long.toString(value) : null;
+  }
+  @Override
+  public Object getObject() {
+    return exists ? value : null;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    if (exists) {
+      cons.accept((double)value);
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    if (exists) {
+      cons.accept(Long.toString(value));
+    }
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+
+  @Override
+  public ExpressionComparator<Long> getObjectComparator(String expression) {
+    return new ExpressionComparator<>(expression);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiField.java
new file mode 100644
index 0000000..dc2a953
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiField.java
@@ -0,0 +1,89 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.solr.analytics.value.LongValueStream.CastingLongValueStream;
+import org.apache.solr.legacy.LegacyNumericUtils;
+import org.apache.solr.schema.TrieLongField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link TrieLongField} with DocValues enabled.
+ */
+public class LongMultiField extends AnalyticsField implements CastingLongValueStream {
+  private SortedSetDocValues docValues;
+  private int count;
+  private long[] values;
+
+  public LongMultiField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new long[initialArrayLength];
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSortedSet(context.reader(), fieldName);
+  }
+  @Override
+  public void collect(int doc) throws IOException {
+    count = 0;
+    if (docValues.advanceExact(doc)) {
+      int term;
+      while ((term = (int)docValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) {
+        if (count == values.length) {
+          resizeValues();
+        }
+        values[count++] = LegacyNumericUtils.prefixCodedToLong(docValues.lookupOrd(term));
+      }
+    }
+  }
+  
+  private void resizeValues() {
+    long[] newValues = new long[values.length*2];
+    for (int i = 0; i < count; ++i) {
+      newValues[i] = values[i];
+    }
+    values = newValues;
+  }
+  
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    streamLongs(value -> cons.accept((double)value));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamLongs(value -> cons.accept(Long.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamLongs(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiPointField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiPointField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiPointField.java
new file mode 100644
index 0000000..31d14ae
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiPointField.java
@@ -0,0 +1,86 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedNumericDocValues;
+import org.apache.solr.analytics.value.LongValueStream.CastingLongValueStream;
+import org.apache.solr.schema.LongPointField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link LongPointField} with DocValues enabled.
+ */
+public class LongMultiPointField extends AnalyticsField implements CastingLongValueStream {
+  private SortedNumericDocValues docValues;
+  private int count;
+  private long[] values;
+
+  public LongMultiPointField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new long[initialArrayLength];
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSortedNumeric(context.reader(), fieldName);
+  }
+  
+  @Override
+  public void collect(int doc) throws IOException {
+    if (docValues.advanceExact(doc)) {
+      count = docValues.docValueCount();
+      resizeEmptyValues(count);
+      for (int i = 0; i < count; ++i) {
+        values[i] = docValues.nextValue();
+      }
+    } else {
+      count = 0;
+    }
+  }
+  
+  private void resizeEmptyValues(int count) {
+    if (count > values.length) {
+      values = new long[count];
+    }
+  }
+  
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    streamLongs(value -> cons.accept((double)value));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamLongs(value -> cons.accept(Long.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamLongs(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringField.java
new file mode 100644
index 0000000..207a95a
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringField.java
@@ -0,0 +1,85 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+
+import org.apache.lucene.index.BinaryDocValues;
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+import org.apache.solr.analytics.value.StringValue.CastingStringValue;
+import org.apache.solr.schema.StrField;
+
+/**
+ * An analytics wrapper for a single-valued {@link StrField} with DocValues enabled.
+ */
+public class StringField extends AnalyticsField implements CastingStringValue {
+  private BinaryDocValues docValues;
+  String value;
+  boolean exists;
+
+  public StringField(String fieldName) {
+    super(fieldName);
+    exists = false;
+  }
+
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getBinary(context.reader(), fieldName);
+  }
+
+  @Override
+  public void collect(int doc) throws IOException {
+    exists = docValues.advanceExact(doc);
+    if (exists) {
+      value = docValues.binaryValue().utf8ToString();
+    }
+  }
+
+  @Override
+  public String getString() {
+    return exists ? value : null;
+  }
+  @Override
+  public Object getObject() {
+    return exists ? value : null;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+
+  @Override
+  public ExpressionComparator<String> getObjectComparator(String expression) {
+    return new ExpressionComparator<>(expression);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringMultiField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringMultiField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringMultiField.java
new file mode 100644
index 0000000..39c60f0
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringMultiField.java
@@ -0,0 +1,66 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.solr.analytics.value.StringValueStream.CastingStringValueStream;
+import org.apache.solr.schema.StrField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link StrField} with DocValues enabled.
+ */
+public class StringMultiField extends AnalyticsField implements CastingStringValueStream {
+  private SortedSetDocValues docValues;
+  private ArrayList<String> values;
+
+  public StringMultiField(String fieldName) {
+    super(fieldName);
+    values = new ArrayList<>(initialArrayLength);
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSortedSet(context.reader(), fieldName);
+  }
+  @Override
+  public void collect(int doc) throws IOException {
+    values.clear();
+    if (docValues.advanceExact(doc)) {
+      int term;
+      while ((term = (int)docValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) {
+        values.add(docValues.lookupOrd(term).utf8ToString());
+      }
+    }
+  }
+  
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    values.forEach(value -> cons.accept(value));
+  }
+
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    values.forEach(value -> cons.accept(value));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/package-info.java
new file mode 100644
index 0000000..a0e5421
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+ 
+/** 
+ * Fields to use for analytics expressions.
+ */
+package org.apache.solr.analytics.function.field;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AbsoluteValueFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AbsoluteValueFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AbsoluteValueFunction.java
new file mode 100644
index 0000000..d52810f
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AbsoluteValueFunction.java
@@ -0,0 +1,54 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * An absolute value mapping function.
+ * <p>
+ * Takes a numeric ValueStream or Value and returns a ValueStream or Value of the same numeric type.
+ */
+public class AbsoluteValueFunction {
+  public static final String name = "abs";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 1) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 1 paramaters, " + params.length + " found.");
+    }
+    AnalyticsValueStream param = params[0];
+    if (param instanceof IntValueStream) {
+      return LambdaFunction.createIntLambdaFunction(name, x -> (x<0)? x*-1:x, (IntValueStream)param);
+    }
+    if (param instanceof LongValueStream) {
+      return LambdaFunction.createLongLambdaFunction(name, x -> (x<0)? x*-1:x, (LongValueStream)param);
+    }
+    if (param instanceof FloatValueStream) {
+      return LambdaFunction.createFloatLambdaFunction(name, x -> (x<0)? x*-1:x, (FloatValueStream)param);
+    }
+    if (param instanceof DoubleValueStream) {
+      return LambdaFunction.createDoubleLambdaFunction(name, x -> (x<0)? x*-1:x, (DoubleValueStream)param);
+      }
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a numeric parameter, "+param.getExpressionStr()+" found.");
+  });
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AddFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AddFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AddFunction.java
new file mode 100644
index 0000000..d66f84e
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AddFunction.java
@@ -0,0 +1,68 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+
+/**
+ * An addition mapping function.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If a single numeric ValueStream is passed in, a {@link DoubleValue} representing the sum of the values for each document is returned.
+ * <li>If a numeric ValueStream and a numeric Value are passed in, a {@link DoubleValueStream} representing the sum of 
+ * the Value and each of the values of the ValueStream for a document is returned.
+ * (Or the other way, since the Value and ValueStream can be used in either order)
+ * <li>If multiple numeric Values are passed in, a {@link DoubleValue} representing the sum of all values is returned.
+ * </ul>
+ */
+public class AddFunction {
+  public static final String name = "add";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires parameters.");
+    } 
+    else if (params.length == 1) {
+      if (params[0] instanceof DoubleValueStream) {
+        return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> a+b, (DoubleValueStream)params[0]);
+      }
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires numeric parameters. Incorrect param: "+params[0].getExpressionStr());
+    } 
+    else if (params.length == 2) {
+      AnalyticsValueStream param1 = params[0];
+      AnalyticsValueStream param2 = params[1];
+      if (param1 instanceof DoubleValueStream && param2 instanceof DoubleValueStream) {
+        return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> a+b, (DoubleValueStream)param1, (DoubleValueStream)param2);
+      }
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires numeric parameters.");
+    }
+    DoubleValue[] castedParams = new DoubleValue[params.length];
+    for (int i = 0; i < params.length; i++) {
+      if (params[i] instanceof DoubleValue) {
+        castedParams[i] = (DoubleValue) params[i];
+      } else {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires that all parameters be single-valued if more than 2 are given.");
+      }
+    }
+    return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> a+b, castedParams);
+  });
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/BottomFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/BottomFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/BottomFunction.java
new file mode 100644
index 0000000..f896da4
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/BottomFunction.java
@@ -0,0 +1,163 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValue;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValue;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A bottom mapping function, returning the lowest value found.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If a single comparable ValueStream is passed in, a Value (of the same type) representing the minimum of the values for each document is returned.
+ * <li>If multiple comparable Values are passed in, a Value (of the same type) representing the minimum of all values is returned.
+ * </ul>
+ */
+public class BottomFunction {
+  public static final String name = "bottom";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires paramaters.");
+    }
+    AnalyticsValueStream param = params[0];
+    if (param instanceof DateValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createDateLambdaFunction(name, (a,b) -> (a<b)? a:b, (DateValueStream)param);
+      }
+      DateValue[] castedParams = new DateValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof DateValue) {
+          castedParams[i] = (DateValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createDateLambdaFunction(name, (a,b) -> (a<b)? a:b, castedParams, false);
+      }
+    }
+    if (param instanceof IntValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createIntLambdaFunction(name, (a,b) -> (a<b)? a:b, (IntValueStream)param);
+      }
+      IntValue[] castedParams = new IntValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof IntValue) {
+          castedParams[i] = (IntValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createIntLambdaFunction(name, (a,b) -> (a<b)? a:b, castedParams, false);
+      }
+    }
+    if (param instanceof LongValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createLongLambdaFunction(name, (a,b) -> (a<b)? a:b, (LongValueStream)param);
+      }
+      LongValue[] castedParams = new LongValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof LongValue) {
+          castedParams[i] = (LongValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createLongLambdaFunction(name, (a,b) -> (a<b)? a:b, castedParams, false);
+      }
+    }
+    if (param instanceof FloatValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createFloatLambdaFunction(name, (a,b) -> (a<b)? a:b, (FloatValueStream)param);
+      }
+      FloatValue[] castedParams = new FloatValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof FloatValue) {
+          castedParams[i] = (FloatValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createFloatLambdaFunction(name, (a,b) -> (a<b)? a:b, castedParams, false);
+      }
+    }
+    if (param instanceof DoubleValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> (a<b)? a:b, (DoubleValueStream)param);
+      }
+      DoubleValue[] castedParams = new DoubleValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof DoubleValue) {
+          castedParams[i] = (DoubleValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> (a<b)? a:b, castedParams, false);
+      }
+    }
+    if (param instanceof StringValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createStringLambdaFunction(name, (a,b) -> (a.compareTo(b)<=0)? a:b, (StringValueStream)param);
+      }
+      StringValue[] castedParams = new StringValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof StringValue) {
+          castedParams[i] = (StringValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createStringLambdaFunction(name, (a,b) -> (a.compareTo(b)<=0)? a:b, castedParams, false);
+      }
+    }
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a comparable parameter. " + 
+          "Incorrect parameter: "+params[0].getExpressionStr());
+  });
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/CompareFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/CompareFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/CompareFunction.java
new file mode 100644
index 0000000..a06f7b8
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/CompareFunction.java
@@ -0,0 +1,614 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.value.AnalyticsValue;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.BooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.BooleanValue.AbstractBooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream.AbstractBooleanValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * Contains all comparable functions. Comparable functions accept two parameters and return a BooleanValueStream.
+ * The two parameters must be able to be cast to the same type.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If a two comparable {@link AnalyticsValue}s are passed in, a {@link BooleanValue} representing the comparison of the two values for each document is returned.
+ * <li>If a comparable {@link AnalyticsValue} and a comparable {@link AnalyticsValueStream} are passed in, 
+ * a {@link BooleanValueStream} representing the comparison of the Value and each of the values of the ValueStream for the document is returned.
+ * </ul>
+ */
+public class CompareFunction {
+  
+  private static BooleanValueStream createCompareFunction(String name, CompResultFunction comp, AnalyticsValueStream... params) {
+    if (params.length != 2) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 2 paramaters, " + params.length + " found.");
+    }
+    AnalyticsValueStream paramA = params[0];
+    AnalyticsValueStream paramB = params[1];
+    if (paramA instanceof DateValueStream && paramB instanceof DateValueStream) {
+      if (paramA instanceof DateValue) {
+        if (paramB instanceof DateValue) {
+          return new CompareDateValueFunction(name,(DateValue)paramA,(DateValue)paramB,comp);
+        }
+        return new CompareDateStreamFunction(name,(DateValue)paramA,(DateValueStream)paramB,comp);
+      }
+      if (paramB instanceof DateValue) {
+        return new CompareDateStreamFunction(name,(DateValue)paramB,(DateValueStream)paramA,reverse(comp));
+      }
+    } else if (paramA instanceof DoubleValueStream && paramB instanceof DoubleValueStream) {
+      if (paramA instanceof DoubleValue) {
+        if (paramB instanceof DoubleValue) {
+          return new CompareDoubleValueFunction(name,(DoubleValue)paramA,(DoubleValue)paramB,comp);
+        }
+        return new CompareDoubleStreamFunction(name,(DoubleValue)paramA,(DoubleValueStream)paramB,comp);
+      }
+      if (paramB instanceof DoubleValue) {
+        return new CompareDoubleStreamFunction(name,(DoubleValue)paramB,(DoubleValueStream)paramA,reverse(comp));
+      }
+    } else if (paramA instanceof StringValueStream && paramB instanceof StringValueStream) {
+      if (paramA instanceof StringValue) {
+        if (paramB instanceof StringValue) {
+          return new CompareStringValueFunction(name,(StringValue)paramA,(StringValue)paramB,comp);
+        }
+        return new CompareStringStreamFunction(name,(StringValue)paramA,(StringValueStream)paramB,comp);
+      }
+      if (paramB instanceof StringValue) {
+        return new CompareStringStreamFunction(name,(StringValue)paramB,(StringValueStream)paramA,reverse(comp));
+      }
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires comparable parameters.");
+    }
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires that at least 1 parameter be single-valued.");
+  }
+  
+  /**
+   * A comparison function that tests equality.
+   */
+  public static class EqualFunction {
+    public static final String name = "equal";
+    public static final CreatorFunction creatorFunction = (params -> {
+      try {
+        return CompareFunction.createCompareFunction(name, val -> {
+          return val == 0;
+        }, params);
+      } catch (SolrException e) {
+        if (params.length != 2) {
+          throw e;
+        }
+        
+        AnalyticsValueStream paramA = params[0];
+        AnalyticsValueStream paramB = params[1];
+        
+        // Booleans aren't really comparable, so just enable the equal function
+        if (paramA instanceof BooleanValueStream && paramB instanceof BooleanValueStream) {
+          if (paramA instanceof BooleanValue) {
+            if (paramB instanceof BooleanValue) {
+              return new BooleanValueEqualFunction((BooleanValue)paramA,(BooleanValue)paramB);
+            }
+            return new BooleanStreamEqualFunction((BooleanValue)paramA,(BooleanValueStream)paramB);
+          } else if (paramB instanceof BooleanValue) {
+            return new BooleanStreamEqualFunction((BooleanValue)paramB,(BooleanValueStream)paramA);
+          }
+        }
+        
+        // This means that the Objects created by the AnalyticsValueStreams are not comparable, so use the .equals() method instead
+        else if (paramA instanceof AnalyticsValue) {
+          if (paramB instanceof AnalyticsValue) {
+            return new ValueEqualFunction((AnalyticsValue)paramA,(AnalyticsValue)paramB);
+          }
+          return new StreamEqualFunction((AnalyticsValue)paramA,paramB);
+        }
+        if (paramB instanceof AnalyticsValue) {
+          return new StreamEqualFunction((AnalyticsValue)paramB,paramA);
+        }
+      }
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires that at least 1 parameter be single-valued.");
+    });
+  }
+  
+  /**
+   * A comparison function that tests whether the first parameter is greater than the second parameter
+   */
+  public static class GTFunction {
+    public static final String name = "gt";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return CompareFunction.createCompareFunction(name, val -> {
+        return val > 0;
+      }, params);
+    });
+  }
+
+  /**
+   * A comparison function that tests whether the first parameter is greater than or equal to the second parameter
+   */
+  public static class GTEFunction {
+    public static final String name = "gte";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return CompareFunction.createCompareFunction(name, val -> {
+        return val >= 0;
+      }, params);
+    });
+  }
+
+  /**
+   * A comparison function that tests whether the first parameter is less than the second parameter
+   */
+  public static class LTFunction {
+    public static final String name = "lt";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return CompareFunction.createCompareFunction(name, val -> {
+        return val < 0;
+      }, params);
+    });
+  }
+
+  /**
+   * A comparison function that tests whether the first parameter is less than or equal to the second parameter
+   */
+  public static class LTEFunction {
+    public static final String name = "lte";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return CompareFunction.createCompareFunction(name, val -> {
+        return val <= 0;
+      }, params);
+    });
+  }
+  
+  private static CompResultFunction reverse(CompResultFunction original) {
+    return val -> original.apply(val*-1);
+  }
+}
+@FunctionalInterface
+interface CompResultFunction {
+  public boolean apply(int compResult);
+}
+/**
+ * A comparison function for two {@link DoubleValue}s.
+ */
+class CompareDoubleValueFunction extends AbstractBooleanValue {
+  private final DoubleValue exprA;
+  private final DoubleValue exprB;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareDoubleValueFunction(String name, DoubleValue exprA, DoubleValue exprB, CompResultFunction comp) {
+    this.name = name;
+    this.exprA = exprA;
+    this.exprB = exprB;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,exprA,exprB);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,exprA,exprB);
+  }
+
+  private boolean exists = false;
+  @Override
+  public boolean getBoolean() {
+    double valueA = exprA.getDouble();
+    double valueB = exprB.getDouble();
+    exists = exprA.exists() && exprB.exists();
+    return exists ? comp.apply(Double.compare(valueA,valueB)) : false;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A comparison function for a {@link DoubleValue} and a {@link DoubleValueStream}.
+ */
+class CompareDoubleStreamFunction extends AbstractBooleanValueStream {
+  private final DoubleValue baseExpr;
+  private final DoubleValueStream compExpr;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareDoubleStreamFunction(String name, DoubleValue baseExpr, DoubleValueStream compExpr, CompResultFunction comp) throws SolrException {
+    this.name = name;
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,baseExpr,compExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    double baseValue = baseExpr.getDouble();
+    if (baseExpr.exists()) {
+      compExpr.streamDoubles(compValue -> cons.accept(comp.apply(Double.compare(baseValue,compValue))));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A comparison function for two {@link DateValue}s.
+ */
+class CompareDateValueFunction extends AbstractBooleanValue {
+  private final DateValue exprA;
+  private final DateValue exprB;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareDateValueFunction(String name, DateValue exprA, DateValue exprB, CompResultFunction comp) {
+    this.name = name;
+    this.exprA = exprA;
+    this.exprB = exprB;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,exprA,exprB);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,exprA,exprB);
+  }
+
+  private boolean exists = false;
+  @Override
+  public boolean getBoolean() {
+    long valueA = exprA.getLong();
+    long valueB = exprB.getLong();
+    exists = exprA.exists() && exprB.exists();
+    return exists ? comp.apply(Long.compare(valueA,valueB)) : false;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A comparison function for a {@link DateValue} and a {@link DateValueStream}.
+ */
+class CompareDateStreamFunction extends AbstractBooleanValueStream {
+  private final DateValue baseExpr;
+  private final DateValueStream compExpr;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareDateStreamFunction(String name, DateValue baseExpr, DateValueStream compExpr, CompResultFunction comp) throws SolrException {
+    this.name = name;
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,baseExpr,compExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    long baseValue = baseExpr.getLong();
+    if (baseExpr.exists()) {
+      compExpr.streamLongs(compValue -> cons.accept(comp.apply(Long.compare(baseValue,compValue))));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A comparison function for two {@link StringValue}s.
+ */
+class CompareStringValueFunction extends AbstractBooleanValue {
+  private final StringValue exprA;
+  private final StringValue exprB;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareStringValueFunction(String name, StringValue exprA, StringValue exprB, CompResultFunction comp) {
+    this.name = name;
+    this.exprA = exprA;
+    this.exprB = exprB;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,exprA,exprB);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,exprA,exprB);
+  }
+
+  private boolean exists = false;
+  @Override
+  public boolean getBoolean() {
+    String valueA = exprA.toString();
+    String valueB = exprB.toString();
+    exists = exprA.exists() && exprB.exists();
+    return exists ? comp.apply(valueA.compareTo(valueB)) : false;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A comparison function for a {@link StringValue} and a {@link StringValueStream}.
+ */
+class CompareStringStreamFunction extends AbstractBooleanValueStream {
+  private final StringValue baseExpr;
+  private final StringValueStream compExpr;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareStringStreamFunction(String name, StringValue baseExpr, StringValueStream compExpr, CompResultFunction comp) throws SolrException {
+    this.name = name;
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,baseExpr,compExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    String baseValue = baseExpr.toString();
+    if (baseExpr.exists()) {
+      compExpr.streamStrings(compValue -> cons.accept(comp.apply(baseValue.compareTo(compValue))));
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * An equal function for two {@link BooleanValue}s.
+ */
+class BooleanValueEqualFunction extends AbstractBooleanValue {
+  private final BooleanValue exprA;
+  private final BooleanValue exprB;
+  public static final String name = CompareFunction.EqualFunction.name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public BooleanValueEqualFunction(BooleanValue exprA, BooleanValue exprB) {
+    this.exprA = exprA;
+    this.exprB = exprB;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,exprA,exprB);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,exprA,exprB);
+  }
+
+  private boolean exists = false;
+  @Override
+  public boolean getBoolean() {
+    boolean valueA = exprA.getBoolean();
+    boolean valueB = exprB.getBoolean();
+    exists = exprA.exists() && exprB.exists();
+    return exists ? valueA == valueB : false;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * An equal function for a {@link BooleanValue} and a {@link BooleanValueStream}.
+ */
+class BooleanStreamEqualFunction extends AbstractBooleanValueStream {
+  private final BooleanValue baseExpr;
+  private final BooleanValueStream compExpr;
+  public static final String name = CompareFunction.EqualFunction.name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public BooleanStreamEqualFunction(BooleanValue baseExpr, BooleanValueStream compExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,baseExpr,compExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    boolean baseValue = baseExpr.getBoolean();
+    if (baseExpr.exists()) {
+      compExpr.streamBooleans(compValue -> cons.accept(baseValue == compValue));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A catch-all equal function for two {@link AnalyticsValue}s.
+ */
+class ValueEqualFunction extends AbstractBooleanValue {
+  private final AnalyticsValue exprA;
+  private final AnalyticsValue exprB;
+  public static final String name = CompareFunction.EqualFunction.name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public ValueEqualFunction(AnalyticsValue exprA, AnalyticsValue exprB) {
+    this.exprA = exprA;
+    this.exprB = exprB;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,exprA,exprB);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,exprA,exprB);
+  }
+
+  private boolean exists = false;
+  @Override
+  public boolean getBoolean() {
+    Object valueA = exprA.getObject();
+    Object valueB = exprB.getObject();
+    exists = exprA.exists() && exprB.exists();
+    return exists ? valueA.equals(valueB) : false;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A catch-all equal function for an {@link AnalyticsValue} and an {@link AnalyticsValueStream}.
+ */
+class StreamEqualFunction extends AbstractBooleanValueStream {
+  private final AnalyticsValue baseExpr;
+  private final AnalyticsValueStream compExpr;
+  public static final String name = CompareFunction.EqualFunction.name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public StreamEqualFunction(AnalyticsValue baseExpr, AnalyticsValueStream compExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,baseExpr,compExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    Object baseValue = baseExpr.getObject();
+    if (baseExpr.exists()) {
+      compExpr.streamObjects(compValue -> cons.accept(baseValue.equals(compValue)));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ConcatFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ConcatFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ConcatFunction.java
new file mode 100644
index 0000000..e54e8c5
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ConcatFunction.java
@@ -0,0 +1,78 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.constant.ConstantStringValue;
+
+/**
+ * A concatenation mapping function, combining the string values of the given parameters. (At least 1 parameter is required)
+ * <p>
+ * Multiple comparable {@link StringValue}s are passed in and a {@link StringValue} representing the concatenation of all values is returned. 
+ */
+public class ConcatFunction {
+  public static final String name = "concat";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires parameters.");
+    } 
+    else if (params.length == 1 && params[0] instanceof StringValue) {
+      return params[0];
+    } 
+    StringValue[] castedParams = new StringValue[params.length];
+    for (int i = 0; i < params.length; i++) {
+      if (params[i] instanceof StringValue) {
+        castedParams[i] = (StringValue) params[i];
+      } else {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires that all parameters be single-valued and convertible to string values.");
+      }
+    }
+    return LambdaFunction.createStringLambdaFunction(name, (a,b) -> a+b, castedParams, false);
+  });
+  
+  /**
+   * A concatenation mapping function, combining the string values of the given parameters with a given separating string.
+   * <br>
+   * Multiple comparable {@link StringValue}s are passed in and a {@link StringValue} representing the separated concatenation of all values is returned.
+   * <p>
+   * The first parameter must be a constant string (e.g. ",").
+   * The remaining parameters are the {@link StringValue} expressions to concatenate. (At least 1 expression is required)
+   */
+  public static class ConcatSeparatedFunction {
+    public static final String name = "concatsep";
+    public static final CreatorFunction creatorFunction = (params -> {
+      if (params.length < 2) {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires at least 2 parameters.");
+      } else if (!(params[0] instanceof ConstantStringValue)) {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires that the first parameter to be a constant string.");
+      }
+      final String sep = ((ConstantStringValue)params[0]).getString();
+      StringValue[] castedParams = new StringValue[params.length - 1];
+      for (int i = 0; i < castedParams.length; i++) {
+        if (params[i + 1] instanceof StringValue) {
+          castedParams[i] = (StringValue) params[i + 1];
+        } else {
+          throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires that all non-separator parameters be single-valued and convertible to string values.");
+        }
+      }
+      return LambdaFunction.createStringLambdaFunction(name, (a,b) -> a + sep + b, castedParams, false);
+    });
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateMathFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateMathFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateMathFunction.java
new file mode 100644
index 0000000..9901625
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateMathFunction.java
@@ -0,0 +1,156 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import java.util.Date;
+import java.util.function.Consumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DateValueStream.AbstractDateValueStream;
+import org.apache.solr.analytics.value.constant.ConstantStringValue;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.util.DateMathParser;
+
+/**
+ * A mapping function that computes date math.
+ * <p>
+ * The first parameter is the {@link DateValue} or {@link DateValueStream} to compute date math on. (Required)
+ * <br>
+ * The trailing parameters must be constant date math strings (e.g. "+1DAY"). (At least 1 required)
+ */
+public class DateMathFunction {
+  public static final String name = "date_math";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length < 2) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires at least 2 paramaters, " + params.length + " found.");
+    }
+    StringBuilder mathParam = new StringBuilder();
+    for (int i = 1; i < params.length; ++i) {
+      if (params[i] instanceof ConstantStringValue) {
+        mathParam.append(((ConstantStringValue) params[i]).getString());
+      } else {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires all math parameters to be a constant strings.");
+      }
+    }
+    if (params[0] instanceof DateValue) {
+      return new DateMathValueFunction((DateValue)params[0], new ConstantStringValue(mathParam.toString()));
+    } else if (params[0] instanceof DateValueStream) {
+      return new DateMathStreamFunction((DateValueStream)params[0], new ConstantStringValue(mathParam.toString()));
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a date as the first parameter.");
+    }
+  });
+}
+/**
+ * DateMath function that supports {@link DateValue}s.
+ */
+class DateMathValueFunction extends AbstractDateValue {
+  private final DateValue dateParam;
+  private final String mathParam;
+  DateMathParser parser = new DateMathParser();
+  public static final String name = DateMathFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateMathValueFunction(DateValue dateParam, ConstantStringValue mathParam) throws SolrException {
+    this.dateParam = dateParam;
+    this.mathParam = "NOW" + mathParam.getString();
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,dateParam,mathParam);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,dateParam);
+  }
+
+  private boolean exists = false;
+  
+  @Override
+  public long getLong() {
+    Date date = getDate();
+    return (date == null) ? 0 : date.getTime();
+  }
+  @Override
+  public Date getDate() {
+    Date date = dateParam.getDate();
+    if (dateParam.exists()) {
+      exists = true;
+      return DateMathParser.parseMath(date,mathParam);
+    } else {
+      exists = false;
+      return null;
+    }
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * DateMath function that supports {@link DateValueStream}s.
+ */
+class DateMathStreamFunction extends AbstractDateValueStream {
+  private final DateValueStream dateParam;
+  private final String mathParam;
+  public static final String name = DateMathFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateMathStreamFunction(DateValueStream dateParam, ConstantStringValue mathParam) throws SolrException {
+    this.dateParam = dateParam;
+    this.mathParam = "NOW" + mathParam.getString();
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,dateParam,mathParam);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,dateParam);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    streamDates(value -> cons.accept(value.getTime()));
+  }
+  @Override
+  public void streamDates(Consumer<Date> cons) {
+    dateParam.streamDates(value -> cons.accept(DateMathParser.parseMath(value, mathParam)));
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateParseFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateParseFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateParseFunction.java
new file mode 100644
index 0000000..5929b88
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateParseFunction.java
@@ -0,0 +1,210 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.LongValue;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DateValueStream.AbstractDateValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A mapping function that converts long or string representations of dates to actual date objects.
+ * <p>
+ * The only parameter is the {@link LongValue}, {@link LongValueStream}, {@link DateValue}, or {@link DateValueStream} to convert. (Required)
+ */
+public class DateParseFunction {
+  public static final String name = "date";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 1) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 1 paramater, " + params.length + " found.");
+    }
+    if (params[0] instanceof LongValue) {
+      return new LongToDateParseFunction((LongValue)params[0]);
+    }
+    else if (params[0] instanceof LongValueStream) {
+      return new LongStreamToDateParseFunction((LongValueStream)params[0]);
+    }
+    else if (params[0] instanceof StringValue) {
+      return new StringToDateParseFunction((StringValue)params[0]);
+    }
+    else if (params[0] instanceof StringValueStream) {
+      return new StringStreamToDateParseFunction((StringValueStream)params[0]);
+    }
+    else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a string or long parameter. " + 
+          "Incorrect parameter: "+params[0].getExpressionStr());
+    }
+  });
+}
+class LongToDateParseFunction extends AbstractDateValue {
+  private final LongValue param;
+  public static final String name = DateParseFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongToDateParseFunction(LongValue param) throws SolrException {
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  @Override
+  public long getLong() {
+    return param.getLong();
+  }
+  @Override
+  public boolean exists() {
+    return param.exists();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongStreamToDateParseFunction extends AbstractDateValueStream {
+  private final LongValueStream param;
+  public static final String name = DateParseFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  public static final DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss.SSS", Locale.ROOT);
+  
+  public LongStreamToDateParseFunction(LongValueStream param) throws SolrException {
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    param.streamLongs(cons);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringToDateParseFunction extends AbstractDateValue {
+  private final StringValue param;
+  public static final String name = DateParseFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  public static final DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss.SSS", Locale.ROOT);
+  
+  public StringToDateParseFunction(StringValue param) throws SolrException {
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  private boolean exists = false;
+  @Override
+  public long getLong() {
+    long value = 0;
+    try {
+      value = formatter.parse(param.toString()).getTime();
+      exists = param.exists();
+    } catch (ParseException e) {
+      exists = false;
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringStreamToDateParseFunction extends AbstractDateValueStream {
+  private final StringValueStream param;
+  public static final String name = DateParseFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  public static final DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss.SSS", Locale.ROOT);
+  
+  public StringStreamToDateParseFunction(StringValueStream param) throws SolrException {
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    param.streamStrings(value -> {
+      try {
+        cons.accept(formatter.parse(value).getTime());
+      } catch (ParseException e) {
+      }
+    });
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DivideFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DivideFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DivideFunction.java
new file mode 100644
index 0000000..e644812
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DivideFunction.java
@@ -0,0 +1,51 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A division mapping function. No checking on divisor value is done. An error will occur if a zero divisor is used.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If two numeric Values are passed in, a {@link DoubleValue} representing the divison of the two values is returned.
+ * <li>If a numeric ValueStream and a numeric Value are passed in, a {@link DoubleValueStream} representing the division of 
+ * the Value and each of the values of the ValueStream for a document is returned. 
+ * (Or the other way, since the Value and ValueStream can be used in either order)
+ * </ul>
+ */
+public class DivideFunction {
+  public static final String name = "div";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 2) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 2 paramaters, " + params.length + " found.");
+    }
+    AnalyticsValueStream param1 = params[0];
+    AnalyticsValueStream param2 = params[1];
+    if (param1 instanceof DoubleValueStream && param2 instanceof DoubleValueStream) {
+      return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> a/b, (DoubleValueStream)param1, (DoubleValueStream)param2);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires numeric parameters.");
+    }
+  });
+}
\ No newline at end of file


[10/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsRequest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsRequest.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsRequest.java
deleted file mode 100644
index d147e6e..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsRequest.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.solr.analytics.request;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Contains the specifications of an Analytics Request, specifically a name,
- * a list of Expressions, a list of field facets, a list of range facets, a list of query facets
- * and the list of expressions and their results calculated in previous AnalyticsRequests.
- */
-public class AnalyticsRequest {
-  
-  private String name;
-  private List<ExpressionRequest> expressions;
-  private Set<String> hiddenExpressions;
-  private List<FieldFacetRequest> fieldFacets;
-  private List<RangeFacetRequest> rangeFacets;
-  private List<QueryFacetRequest> queryFacets;
-  
-  public AnalyticsRequest(String name) {
-    this.name = name;
-    expressions = new ArrayList<>();
-    hiddenExpressions = new HashSet<>();
-    fieldFacets = new ArrayList<>();
-    rangeFacets = new ArrayList<>();
-    queryFacets = new ArrayList<>();
-  }
-  
-  public String getName() {
-    return name;
-  }
-  
-  public void setExpressions(List<ExpressionRequest> expressions) {
-    this.expressions = expressions;
-  }
-
-  public void addExpression(ExpressionRequest expressionRequest) {
-    expressions.add(expressionRequest);
-  }
-  
-  public List<ExpressionRequest> getExpressions() {
-    return expressions;
-  }
-
-  public void addHiddenExpression(ExpressionRequest expressionRequest) {
-    expressions.add(expressionRequest);
-    hiddenExpressions.add(expressionRequest.getName());
-  }
-  
-  public Set<String> getHiddenExpressions() {
-    return hiddenExpressions;
-  }
-  
-  public void setFieldFacets(List<FieldFacetRequest> fieldFacets) {
-    this.fieldFacets = fieldFacets;
-  }
-  
-  public List<FieldFacetRequest> getFieldFacets() {
-    return fieldFacets;
-  }
-  
-  public void setRangeFacets(List<RangeFacetRequest> rangeFacets) {
-    this.rangeFacets = rangeFacets;
-  }
-  
-  public List<RangeFacetRequest> getRangeFacets() {
-    return rangeFacets;
-  }
-  
-  public void setQueryFacets(List<QueryFacetRequest> queryFacets) {
-    this.queryFacets = queryFacets;
-  }
-  
-  public List<QueryFacetRequest> getQueryFacets() {
-    return queryFacets;
-  }
-  
-  @Override
-  public String toString() {
-    StringBuilder builder = new StringBuilder("<AnalyticsRequest name=" + name + ">");
-    for (ExpressionRequest exp : expressions) {
-      builder.append(exp.toString());
-    }
-    for (FieldFacetRequest facet : fieldFacets) {
-      builder.append(facet.toString());
-    }
-    for (RangeFacetRequest facet : rangeFacets) {
-      builder.append(facet.toString());
-    }
-    for (QueryFacetRequest facet : queryFacets) {
-      builder.append(facet.toString());
-    }
-    builder.append("</AnalyticsRequest>");
-    return builder.toString();
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsRequestFactory.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsRequestFactory.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsRequestFactory.java
deleted file mode 100644
index 3773ff6..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsRequestFactory.java
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * 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.solr.analytics.request;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.solr.analytics.request.FieldFacetRequest.FacetSortSpecification;
-import org.apache.solr.analytics.util.AnalyticsParams;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.SolrException.ErrorCode;
-import org.apache.solr.common.params.FacetParams.FacetRangeInclude;
-import org.apache.solr.common.params.FacetParams.FacetRangeOther;
-import org.apache.solr.common.params.SolrParams;
-import org.apache.solr.schema.IndexSchema;
-
-/**
- * Parses the SolrParams to create a list of analytics requests.
- */
-public class AnalyticsRequestFactory implements AnalyticsParams {
-
-  public static final Pattern statPattern = Pattern.compile("^o(?:lap)?\\.([^\\.]+)\\.(?:"+EXPRESSION+")\\.([^\\.]+)$", Pattern.CASE_INSENSITIVE);
-  public static final Pattern hiddenStatPattern = Pattern.compile("^o(?:lap)?\\.([^\\.]+)\\.(?:"+HIDDEN_EXPRESSION+")\\.([^\\.]+)$", Pattern.CASE_INSENSITIVE);
-  public static final Pattern fieldFacetPattern = Pattern.compile("^o(?:lap)?\\.([^\\.]+)\\.(?:"+FIELD_FACET+")$", Pattern.CASE_INSENSITIVE);
-  public static final Pattern fieldFacetParamPattern = Pattern.compile("^o(?:lap)?\\.([^\\.]+)\\.(?:"+FIELD_FACET+")\\.([^\\.]+)\\.("+LIMIT+"|"+OFFSET+"|"+HIDDEN+"|"+SHOW_MISSING+"|"+SORT_STATISTIC+"|"+SORT_DIRECTION+")$", Pattern.CASE_INSENSITIVE);
-  public static final Pattern rangeFacetPattern = Pattern.compile("^o(?:lap)?\\.([^\\.]+)\\.(?:"+RANGE_FACET+")$", Pattern.CASE_INSENSITIVE);
-  public static final Pattern rangeFacetParamPattern = Pattern.compile("^o(?:lap)?\\.([^\\.]+)\\.(?:"+RANGE_FACET+")\\.([^\\.]+)\\.("+START+"|"+END+"|"+GAP+"|"+HARDEND+"|"+INCLUDE_BOUNDARY+"|"+OTHER_RANGE+")$", Pattern.CASE_INSENSITIVE);
-  public static final Pattern queryFacetPattern = Pattern.compile("^o(?:lap)?\\.([^\\.]+)\\.(?:"+QUERY_FACET+")$", Pattern.CASE_INSENSITIVE);
-  public static final Pattern queryFacetParamPattern = Pattern.compile("^o(?:lap)?\\.([^\\.]+)\\.(?:"+QUERY_FACET+")\\.([^\\.]+)\\.("+QUERY+"|"+DEPENDENCY+")$", Pattern.CASE_INSENSITIVE);
-  
-  public static List<AnalyticsRequest> parse(IndexSchema schema, SolrParams params) {
-    Map<String, AnalyticsRequest> requestMap = new HashMap<>();
-    Map<String, Map<String,FieldFacetRequest>> fieldFacetMap = new HashMap<>();
-    Map<String, Set<String>> fieldFacetSet = new HashMap<>();
-    Map<String, Map<String,RangeFacetRequest>> rangeFacetMap = new HashMap<>();
-    Map<String, Set<String>> rangeFacetSet = new HashMap<>();
-    Map<String, Map<String,QueryFacetRequest>> queryFacetMap = new HashMap<>();
-    Map<String, Set<String>> queryFacetSet = new HashMap<>();
-    List<AnalyticsRequest> requestList = new ArrayList<>();
-    
-    Iterator<String> paramsIterator = params.getParameterNamesIterator();
-    while (paramsIterator.hasNext()) {
-      String param = paramsIterator.next();
-      CharSequence paramSequence = param.subSequence(0, param.length());
-      
-      // Check if stat
-      Matcher m = statPattern.matcher(paramSequence);
-      if (m.matches()) {
-        makeExpression(requestMap,m.group(1),m.group(2),params.get(param));
-      } else {
-        // Check if hidden stat
-        m = hiddenStatPattern.matcher(paramSequence);
-        if (m.matches()) {
-          makeHiddenExpression(requestMap,m.group(1),m.group(2),params.get(param));
-        } else {
-          // Check if field facet
-          m = fieldFacetPattern.matcher(paramSequence);
-          if (m.matches()) {
-            makeFieldFacet(schema,fieldFacetMap,fieldFacetSet,m.group(1),params.getParams(param));
-          } else {
-            // Check if field facet parameter
-            m = fieldFacetParamPattern.matcher(paramSequence);
-            if (m.matches()) {
-              setFieldFacetParam(schema,fieldFacetMap,m.group(1),m.group(2),m.group(3),params.getParams(param));
-            } else {
-              // Check if range facet
-              m = rangeFacetPattern.matcher(paramSequence);
-              if (m.matches()) {
-                makeRangeFacet(schema,rangeFacetSet,m.group(1),params.getParams(param));
-              }  else {
-                // Check if range facet parameter
-                m = rangeFacetParamPattern.matcher(paramSequence);
-                if (m.matches()) {
-                  setRangeFacetParam(schema,rangeFacetMap,m.group(1),m.group(2),m.group(3),params.getParams(param));
-                }  else {
-                  // Check if query facet
-                  m = queryFacetPattern.matcher(paramSequence);
-                  if (m.matches()) {
-                    makeQueryFacet(schema,queryFacetSet,m.group(1),params.getParams(param));
-                  }  else {
-                    // Check if query
-                    m = queryFacetParamPattern.matcher(paramSequence);
-                    if (m.matches()) {
-                      setQueryFacetParam(schema,queryFacetMap,m.group(1),m.group(2),m.group(3),params.getParams(param));
-                    } 
-                  }
-                }
-              }
-            }
-          }
-        }
-      }
-    }
-    for (String reqName : requestMap.keySet()) {
-      AnalyticsRequest ar = requestMap.get(reqName);
-      List<FieldFacetRequest> ffrs = new ArrayList<>();
-      if (fieldFacetSet.get(reqName)!=null) {
-        for (String field : fieldFacetSet.get(reqName)) {
-          ffrs.add(fieldFacetMap.get(reqName).get(field));
-        }
-      }
-      ar.setFieldFacets(ffrs);
-      
-      List<RangeFacetRequest> rfrs = new ArrayList<>();
-      if (rangeFacetSet.get(reqName)!=null) {
-        for (String field : rangeFacetSet.get(reqName)) {
-          RangeFacetRequest rfr = rangeFacetMap.get(reqName).get(field);
-          if (rfr != null) {
-            rfrs.add(rfr);
-          }
-        }
-      }
-      ar.setRangeFacets(rfrs);
-      
-      List<QueryFacetRequest> qfrs = new ArrayList<>();
-      if (queryFacetSet.get(reqName)!=null) {
-        for (String name : queryFacetSet.get(reqName)) {
-          QueryFacetRequest qfr = queryFacetMap.get(reqName).get(name);
-          if (qfr != null) {
-            addQueryFacet(qfrs,qfr);
-          }
-        }
-      }
-      for (QueryFacetRequest qfr : qfrs) {
-        if (qfr.getDependencies().size()>0) {
-          throw new SolrException(ErrorCode.BAD_REQUEST,"The query facet dependencies "+qfr.getDependencies().toString()+" either do not exist or are defined in a dependency looop.");
-        }
-      }
-      ar.setQueryFacets(qfrs);
-      requestList.add(ar);
-    }
-    return requestList; 
-  }
-
-  private static void makeFieldFacet(IndexSchema schema, Map<String, Map<String, FieldFacetRequest>> fieldFacetMap, Map<String, Set<String>> fieldFacetSet, String requestName, String[] fields) {
-    Map<String, FieldFacetRequest> facetMap = fieldFacetMap.get(requestName);
-    if (facetMap == null) {
-      facetMap = new HashMap<>();
-      fieldFacetMap.put(requestName, facetMap);
-    }
-    Set<String> set = fieldFacetSet.get(requestName);
-    if (set == null) {
-      set = new HashSet<>();
-      fieldFacetSet.put(requestName, set);
-    }
-    for (String field : fields) {
-      if (facetMap.get(field) == null) {
-        facetMap.put(field,new FieldFacetRequest(schema.getField(field)));
-      }
-      set.add(field);
-    }
-  }
-
-  private static void setFieldFacetParam(IndexSchema schema, Map<String, Map<String, FieldFacetRequest>> fieldFacetMap, String requestName, String field, String paramType, String[] params) {
-    Map<String, FieldFacetRequest> facetMap = fieldFacetMap.get(requestName);
-    if (facetMap == null) {
-      facetMap = new HashMap<>();
-      fieldFacetMap.put(requestName, facetMap);
-    }
-    FieldFacetRequest fr = facetMap.get(field);
-    if (fr == null) {
-      fr = new FieldFacetRequest(schema.getField(field));
-      facetMap.put(field,fr);
-    }
-    if (paramType.equals("limit")||paramType.equals("l")) {
-      fr.setLimit(Integer.parseInt(params[0]));
-    } else if (paramType.equals("offset")||paramType.equals("off")) {
-      fr.setOffset(Integer.parseInt(params[0]));
-    } else if (paramType.equals("hidden")||paramType.equals("h")) {
-      fr.setHidden(Boolean.parseBoolean(params[0]));
-    } else if (paramType.equals("showmissing")||paramType.equals("sm")) {
-      fr.showMissing(Boolean.parseBoolean(params[0]));
-    } else if (paramType.equals("sortstatistic")||paramType.equals("sortstat")||paramType.equals("ss")) {
-      fr.setSort(new FacetSortSpecification(params[0],fr.getDirection()));
-    } else if (paramType.equals("sortdirection")||paramType.equals("sd")) {
-      fr.setDirection(params[0]);
-    } 
-  }
-
-  private static void makeRangeFacet(IndexSchema schema, Map<String, Set<String>> rangeFacetSet, String requestName, String[] fields) {
-    Set<String> set = rangeFacetSet.get(requestName);
-    if (set == null) {
-      set = new HashSet<>();
-      rangeFacetSet.put(requestName, set);
-    }
-    for (String field : fields) {
-      set.add(field);
-    }
-  }
-
-  private static void setRangeFacetParam(IndexSchema schema, Map<String, Map<String, RangeFacetRequest>> rangeFacetMap, String requestName, String field, String paramType, String[] params) {
-    Map<String, RangeFacetRequest> facetMap = rangeFacetMap.get(requestName);
-    if (facetMap == null) {
-      facetMap = new HashMap<>();
-      rangeFacetMap.put(requestName, facetMap);
-    }
-    RangeFacetRequest rr = facetMap.get(field);
-    if (rr == null) {
-      rr = new RangeFacetRequest(schema.getField(field));
-      facetMap.put(field,rr);
-    }
-    if (paramType.equals("start")||paramType.equals("st")) {
-      rr.setStart(params[0]);
-    } else if (paramType.equals("end")||paramType.equals("e")) {
-      rr.setEnd(params[0]);
-    } else if (paramType.equals("gap")||paramType.equals("g")) {
-      rr.setGaps(params[0].split(","));
-    } else if (paramType.equals("hardend")||paramType.equals("he")) {
-      rr.setHardEnd(Boolean.parseBoolean(params[0]));
-    } else if (paramType.equals("includebound")||paramType.equals("ib")) {
-      for (String param : params) {
-        rr.addInclude(FacetRangeInclude.get(param));
-      }
-    } else if (paramType.equals("otherrange")||paramType.equals("or")) {
-      for (String param : params) {
-        rr.addOther(FacetRangeOther.get(param));
-      }
-    } 
-  }
-
-  private static void makeQueryFacet(IndexSchema schema,Map<String, Set<String>> queryFacetSet, String requestName, String[] names) {
-    Set<String> set = queryFacetSet.get(requestName);
-    if (set == null) {
-      set = new HashSet<>();
-      queryFacetSet.put(requestName, set);
-    }
-    for (String name : names) {
-      set.add(name);
-    }
-  }
-
-  private static void setQueryFacetParam(IndexSchema schema, Map<String, Map<String, QueryFacetRequest>> queryFacetMap, String requestName, String name, String paramType, String[] params) {
-    Map<String, QueryFacetRequest> facetMap = queryFacetMap.get(requestName);
-    if (facetMap == null) {
-      facetMap = new HashMap<>();
-      queryFacetMap.put(requestName, facetMap);
-    }
-    QueryFacetRequest qr = facetMap.get(name);
-    if (qr == null) {
-      qr = new QueryFacetRequest(name);
-      facetMap.put(name,qr);
-    }
-    if (paramType.equals("query")||paramType.equals("q")) {
-      for (String query : params) {
-        qr.addQuery(query);
-      }
-    } else if (paramType.equals("dependency")||paramType.equals("d")) {
-      for (String depend : params) {
-        qr.addDependency(depend);
-      }
-    }
-  }
-
-  private static void makeHiddenExpression(Map<String, AnalyticsRequest> requestMap, String requestName, String expressionName, String expression) {
-    AnalyticsRequest req = requestMap.get(requestName);
-    if (req == null) {
-      req = new AnalyticsRequest(requestName);
-      requestMap.put(requestName, req);
-    }
-    req.addHiddenExpression(new ExpressionRequest(expressionName,expression));
-  }
-
-  private static void makeExpression(Map<String, AnalyticsRequest> requestMap, String requestName, String expressionName, String expression) {
-    AnalyticsRequest req = requestMap.get(requestName);
-    if (req == null) {
-      req = new AnalyticsRequest(requestName);
-      requestMap.put(requestName, req);
-    }
-    req.addExpression(new ExpressionRequest(expressionName,expression));
-  }
-  
-  private static void addQueryFacet(List<QueryFacetRequest> currentList, QueryFacetRequest queryFacet) {
-    Set<String> depends = queryFacet.getDependencies();
-    int place = 0;
-    for (QueryFacetRequest qfr : currentList) {
-      if (qfr.getDependencies().remove(queryFacet.getName())) {
-        break;
-      }
-      place++;
-      depends.remove(qfr.getName());
-    }
-    currentList.add(place,queryFacet);
-    for (int count = place+1; count < currentList.size(); count++) {
-      currentList.get(count).getDependencies().remove(queryFacet.getName());
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsStats.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsStats.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsStats.java
deleted file mode 100644
index 771aff7..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsStats.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * 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.solr.analytics.request;
-
-import java.io.IOException;
-import java.lang.invoke.MethodHandles;
-import java.util.List;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.search.DocIdSet;
-import org.apache.lucene.search.DocIdSetIterator;
-import org.apache.solr.analytics.accumulator.BasicAccumulator;
-import org.apache.solr.analytics.accumulator.FacetingAccumulator;
-import org.apache.solr.analytics.accumulator.ValueAccumulator;
-import org.apache.solr.analytics.plugin.AnalyticsStatisticsCollector;
-import org.apache.solr.common.params.SolrParams;
-import org.apache.solr.common.util.NamedList;
-import org.apache.solr.request.SolrQueryRequest;
-import org.apache.solr.search.DocSet;
-import org.apache.solr.search.Filter;
-import org.apache.solr.search.SolrIndexSearcher;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Class which computes the set of {@link AnalyticsRequest}s.
- */
-public class AnalyticsStats {
-  protected DocSet docs;
-  protected SolrParams params;
-  protected SolrIndexSearcher searcher;
-  protected SolrQueryRequest req;
-  protected AnalyticsStatisticsCollector statsCollector;
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-  
-  public AnalyticsStats(SolrQueryRequest req, DocSet docs, SolrParams params, AnalyticsStatisticsCollector statsCollector) {
-    this.req = req;
-    this.searcher = req.getSearcher();
-    this.docs = docs;
-    this.params = params; 
-    this.statsCollector = statsCollector;
-  }
-
-  /**
-   * Calculates the analytics requested in the Parameters.
-   * 
-   * @return List of results formated to mirror the input XML.
-   * @throws IOException if execution fails
-   */
-  public NamedList<?> execute() throws IOException {
-    statsCollector.startRequest();
-    NamedList<Object> res = new NamedList<>();
-    List<AnalyticsRequest> requests;
-    
-    requests = AnalyticsRequestFactory.parse(searcher.getSchema(), params);
-
-    if(requests == null || requests.size()==0){
-      return res;
-    }
-    statsCollector.addRequests(requests.size());
-    
-    // Get filter to all docs
-    Filter filter = docs.getTopFilter();
-    
-    // Computing each Analytics Request Separately
-    for( AnalyticsRequest areq : requests ){
-      // The Accumulator which will control the statistics generation
-      // for the entire analytics request
-      ValueAccumulator accumulator; 
-      
-      // The number of total facet requests
-      int facets = areq.getFieldFacets().size()+areq.getRangeFacets().size()+areq.getQueryFacets().size();
-      try {
-        if( facets== 0 ){
-          accumulator = BasicAccumulator.create(searcher, docs, areq);
-        } else {
-          accumulator = FacetingAccumulator.create(searcher, docs, areq, req);
-        }
-      } catch (IOException e) {
-        log.warn("Analytics request '"+areq.getName()+"' failed", e);
-        continue;
-      }
-
-      statsCollector.addStatsCollected(((BasicAccumulator)accumulator).getNumStatsCollectors());
-      statsCollector.addStatsRequests(areq.getExpressions().size());
-      statsCollector.addFieldFacets(areq.getFieldFacets().size());
-      statsCollector.addRangeFacets(areq.getRangeFacets().size());
-      statsCollector.addQueryFacets(areq.getQueryFacets().size());
-      statsCollector.addQueries(((BasicAccumulator)accumulator).getNumQueries());
-      
-      // Loop through the documents returned by the query and add to accumulator
-      List<LeafReaderContext> contexts = searcher.getTopReaderContext().leaves();
-      for (int leafNum = 0; leafNum < contexts.size(); leafNum++) {
-        LeafReaderContext context = contexts.get(leafNum);
-        DocIdSet dis = filter.getDocIdSet(context, null); // solr docsets already exclude any deleted docs
-        DocIdSetIterator disi = null;
-        if (dis != null) {
-          disi = dis.iterator();
-        }
-
-        if (disi != null) {
-          accumulator.getLeafCollector(context);
-          int doc = disi.nextDoc();
-          while( doc != DocIdSetIterator.NO_MORE_DOCS){
-            // Add a document to the statistics being generated
-            accumulator.collect(doc);
-            doc = disi.nextDoc();
-          }
-        }
-      }
-      
-      // do some post-processing
-      accumulator.postProcess();
-     
-      // compute the stats
-      accumulator.compute();
-      
-      res.add(areq.getName(),accumulator.export());
-    }
-
-    statsCollector.endRequest();
-    return res;
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/ExpressionRequest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/ExpressionRequest.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/ExpressionRequest.java
deleted file mode 100644
index a833c80..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/ExpressionRequest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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.solr.analytics.request;
-
-import org.apache.solr.analytics.expression.Expression;
-
-/**
- * Contains name and string representation of an expression.
- */
-public class ExpressionRequest implements Comparable<ExpressionRequest> {
-  private String name;
-  private String expressionString;
-  private Expression expression;
-  
-  /**
-   * @param name The name of the Expression.
-   * @param expressionString The string representation of the desired Expression.
-   */
-  public ExpressionRequest(String name, String expressionString) {
-    this.name = name;
-    this.expressionString = expressionString;
-  }
-
-  public void setExpressionString(String expressionString) {
-    this.expressionString = expressionString;
-  }
-  
-  public String getExpressionString() {
-    return expressionString;
-  }
-  
-  public void setExpression(Expression expression) {
-    this.expression = expression;
-  }
-  
-  public Expression getExpression() {
-    return expression;
-  }
-  
-  public void setName(String name) {
-    this.name = name;
-  }
-  
-  public String getName() {
-    return name;
-  }
-
-  @Override
-  public int compareTo(ExpressionRequest o) {
-    return name.compareTo(o.getName());
-  }
-  
-  @Override
-  public String toString() {
-    return "<ExpressionRequest name=" + name + " expression=" + expressionString + "/>";
-  }
-  
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/FacetRequest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/FacetRequest.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/FacetRequest.java
deleted file mode 100644
index 936af72..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/FacetRequest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.solr.analytics.request;
-
-public interface FacetRequest {
-  
-  /**
-   * Get the name of this facet (commonly the field name)
-   * @return the name
-   */
-  String getName();  
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/FieldFacetRequest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/FieldFacetRequest.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/FieldFacetRequest.java
deleted file mode 100644
index 67d93da..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/FieldFacetRequest.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * 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.solr.analytics.request;
-
-import org.apache.solr.analytics.util.AnalyticsParams;
-import org.apache.solr.schema.SchemaField;
-
-import java.util.Locale;
-
-
-/**
- * Contains all of the specifications for a field facet.
- */
-public class FieldFacetRequest extends AbstractFieldFacetRequest {
-
-  private FacetSortSpecification sort = null;
-  private FacetSortDirection dir = null;
-  private int limit;
-  private int offset;
-  private boolean missing;
-  private boolean hidden;
-  
-  
-  public static enum FacetSortDirection {
-    ASCENDING ,
-    DESCENDING;
-   
-    public static FacetSortDirection fromExternal(String value){
-      final String sort = value.toLowerCase(Locale.ROOT);
-      if( "asc".equals(sort) )            return ASCENDING;
-      if( "ascending".equals(sort) )      return ASCENDING;
-      if( "desc".equals(sort) )           return DESCENDING;
-      if( "descending".equals(sort) )     return DESCENDING;
-      return Enum.valueOf(FacetSortDirection.class, value);
-    }
-  }
-  
-  /**
-   * Specifies how to sort the buckets of a field facet.
-   * 
-   */
-  public static class FacetSortSpecification {
-    private String statistic;
-    private FacetSortDirection direction = FacetSortDirection.DESCENDING;
-    
-    public FacetSortSpecification(){}
-    
-    /**
-     * @param statistic The name of a statistic specified in the {@link AnalyticsRequest}
-     * which is wrapping the {@link FieldFacetRequest} being sorted.
-     */
-    public FacetSortSpecification(String statistic) {
-      this.statistic = statistic;
-    }
-    
-    public FacetSortSpecification(String statistic, FacetSortDirection direction) {
-      this(statistic);
-      this.direction = direction;
-    }
-    
-    public String getStatistic() {
-      return statistic;
-    }
-    public void setStatistic(String statistic) {
-      this.statistic = statistic;
-    }
-    public FacetSortDirection getDirection() {
-      return direction;
-    }
-    public void setDirection(FacetSortDirection direction) {
-      this.direction = direction;
-    }
-
-    public static FacetSortSpecification fromExternal(String spec){
-      String[] parts = spec.split(" ",2);
-      if( parts.length == 1 ){
-        return new FacetSortSpecification(parts[0]);
-      } else {
-        return new FacetSortSpecification(parts[0], FacetSortDirection.fromExternal(parts[1]));
-      }
-    }
-
-    @Override
-    public String toString() {
-      return "<SortSpec stat=" + statistic + " dir=" + direction + ">";
-    }
-  }
-
-  public FieldFacetRequest(SchemaField field) {
-    super(field);
-    this.limit = AnalyticsParams.DEFAULT_LIMIT;
-    this.hidden = AnalyticsParams.DEFAULT_HIDDEN;
-  }
-
-  public FacetSortDirection getDirection() {
-    return dir;
-  }
-
-  public void setDirection(String dir) {
-    this.dir = FacetSortDirection.fromExternal(dir);
-    if (sort!=null) {
-      sort.setDirection(this.dir);
-    }
-  }
-
-  public FacetSortSpecification getSort() {
-    return sort;
-  }
-
-  public void setSort(FacetSortSpecification sort) {
-    this.sort = sort;
-  }
-
-  public boolean showsMissing() {
-    return missing;
-  }
-
-  /**
-   * If there are missing values in the facet field, include the bucket 
-   * for the missing facet values in the facet response.
-   * @param missing true/false if we calculate missing
-   */
-  public void showMissing(boolean missing) {
-    this.missing = missing;
-  }
-
-  public int getLimit() {
-    return limit;
-  }
-
-  public void setLimit(int limit) {
-    this.limit = limit;
-  }
-  
-  public int getOffset() {
-    return offset;
-  }
-
-  public void setOffset(int offset) {
-    this.offset = offset;
-  }
-
-  public boolean isHidden() {
-    return hidden;
-  }
-
-  public void setHidden(boolean hidden) {
-    this.hidden = hidden;
-  }
-
-  @Override
-  public String toString() {
-    return "<FieldFacetRequest field="+field.getName()+(sort==null?"":" sort=" + sort) + " limit=" + limit+" offset="+offset+">";
-  }
-
-
-  
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/QueryFacetRequest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/QueryFacetRequest.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/QueryFacetRequest.java
deleted file mode 100644
index fbe34db..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/QueryFacetRequest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.solr.analytics.request;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Contains all of the specifications for a query facet.
- */
-public class QueryFacetRequest implements FacetRequest {
-  private String name;
-  private List<String> queries;
-  private Set<String> dependencies;
-  
-  public QueryFacetRequest() {
-    dependencies = new HashSet<>();
-  }
-
-  public QueryFacetRequest(String name) {
-    this.name = name;
-    this.queries = new ArrayList<>();
-    dependencies = new HashSet<>();
-  }
- 
-  public List<String> getQueries() {
-    return queries;
-  }
-
-  public void setQueries(List<String> queries) {
-    this.queries = queries;
-  }
-
-  public void addQuery(String query) {
-    queries.add(query);
-  }
-
-  public Set<String> getDependencies() {
-    return dependencies;
-  }
-
-  public void setDependencies(Set<String> dependencies) {
-    this.dependencies = dependencies;
-  }
-
-  public void addDependency(String dependency) {
-    dependencies.add(dependency);
-  }
-
-  public String getName() {
-    return name;
-  }
-
-  public void setName(String name) {
-    this.name = name;
-  }
-  
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/RangeFacetRequest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/RangeFacetRequest.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/RangeFacetRequest.java
deleted file mode 100644
index ec9cf6b..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/RangeFacetRequest.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * 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.solr.analytics.request;
-
-import java.util.Arrays;
-import java.util.EnumSet;
-
-import org.apache.solr.analytics.util.AnalyticsParams;
-import org.apache.solr.common.params.FacetParams.FacetRangeInclude;
-import org.apache.solr.common.params.FacetParams.FacetRangeOther;
-import org.apache.solr.schema.SchemaField;
-
-/**
- * Contains all of the specifications for a range facet.
- */
-public class RangeFacetRequest extends AbstractFieldFacetRequest {
-  protected String start;
-  protected String end;
-  protected String[] gaps;
-  protected boolean hardEnd = false;
-  protected EnumSet<FacetRangeInclude> include;
-  protected boolean includeCalled = false;
-  protected EnumSet<FacetRangeOther> others;
-  protected boolean othersCalled = false;
-  
-  public RangeFacetRequest(SchemaField field) {
-    super(field);
-    include = EnumSet.of(AnalyticsParams.DEFAULT_INCLUDE);
-    others = EnumSet.of(AnalyticsParams.DEFAULT_OTHER);
-  }
-  
-  public RangeFacetRequest(SchemaField field, String start, String end, String[] gaps) {
-    super(field);
-    this.start = start;
-    this.end = end;
-    this.gaps = gaps;
-  }
-
-  public String getStart() {
-    return start;
-  }
-
-  public void setStart(String start) {
-    this.start = start;
-  }
-
-  public String getEnd() {
-    return end;
-  }
-
-  public void setEnd(String end) {
-    this.end = end;
-  }
-
-  public EnumSet<FacetRangeInclude> getInclude() {
-    return include;
-  }
-
-  public void setInclude(EnumSet<FacetRangeInclude> include) {
-    includeCalled = true;
-    this.include = include;
-  }
-
-  public void addInclude(FacetRangeInclude include) {
-    if (includeCalled) {
-      this.include.add(include);
-    } else {
-      includeCalled = true;
-      this.include = EnumSet.of(include);
-    }
-  }
-
-  public String[] getGaps() {
-    return gaps;
-  }
-
-  public void setGaps(String[] gaps) {
-    this.gaps = gaps;
-  }
-
-  public boolean isHardEnd() {
-    return hardEnd;
-  }
-
-  public void setHardEnd(boolean hardEnd) {
-    this.hardEnd = hardEnd;
-  }
-
-  public EnumSet<FacetRangeOther> getOthers() {
-    return others;
-  }
-
-  public void setOthers(EnumSet<FacetRangeOther> others) {
-    othersCalled = true;
-    this.others = others;
-  }
-
-  public void addOther(FacetRangeOther other) {
-    if (othersCalled) {
-      this.others.add(other);
-    } else {
-      othersCalled = true;
-      this.others = EnumSet.of(other);
-    }
-  }
-
-  @Override
-  public String toString() {
-    return "<RangeFacetRequest field="+field.getName() + " start=" + start + ", end=" + end + ", gap=" + Arrays.toString(gaps) + ", hardEnd=" + hardEnd + 
-                               ", include=" + include + ", others=" + others +">";
-  }
-
-  
-  
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/package-info.java
deleted file mode 100644
index de2feb3..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/package-info.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.
- */
- 
-/** 
- * Request objects for creating Analytics requests
- */
-package org.apache.solr.analytics.request;
-
-
-

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/AbstractDelegatingStatsCollector.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/AbstractDelegatingStatsCollector.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/AbstractDelegatingStatsCollector.java
deleted file mode 100644
index 92969f1..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/AbstractDelegatingStatsCollector.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.solr.analytics.statistics;
-
-import java.io.IOException;
-import java.util.Set;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.util.mutable.MutableValue;
-
-/**
- * <code>AbstractDelegationStatsCollector</code> objects wrap other StatsCollectors.
- * While they compute their own statistics they pass along all inputs and requests
- * to the delegates as well.
- */
-public abstract class AbstractDelegatingStatsCollector implements StatsCollector{
-  protected final StatsCollector delegate;
-  protected final Set<String> statsList;
-  MutableValue value;
-  FunctionValues function;
-  
-  /**
-   * @param delegate The delegate computing statistics on the same set of values.
-   */
-  public AbstractDelegatingStatsCollector(StatsCollector delegate) {
-    this.delegate = delegate;
-    this.statsList = delegate.getStatsList();
-  }
-  
-  public void setNextReader(LeafReaderContext context) throws IOException {
-    delegate.setNextReader(context);
-    value = getValue();
-    function = getFunction();
-  }
-  
-  public StatsCollector delegate(){
-    return delegate;
-  }
-  
-  public Set<String> getStatsList(){
-    return statsList;
-  }
-  
-  public MutableValue getValue() {
-    return delegate.getValue();
-  }
-  
-  public FunctionValues getFunction() {
-    return delegate.getFunction();
-  }
-  
-  public void collect(int doc) throws IOException {
-    delegate.collect(doc);
-  }
-  
-  public String valueSourceString() {
-    return delegate.valueSourceString();
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/MedianStatsCollector.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/MedianStatsCollector.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/MedianStatsCollector.java
deleted file mode 100644
index bf71429..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/MedianStatsCollector.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.solr.analytics.statistics;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import org.apache.solr.analytics.util.MedianCalculator;
-
-/**
- * <code>MedianStatsCollector</code> computes the median.
- */
-public class MedianStatsCollector extends AbstractDelegatingStatsCollector{
-
-  private final List<Double> values = new ArrayList<>();
-  protected double median;
-  
-  public MedianStatsCollector(StatsCollector delegate) {
-    super(delegate);
-  }
-
-  public Double getMedian() {
-    return new Double(MedianCalculator.getMedian(values));
-  }
-
-  @Override
-  public Comparable getStat(String stat) {
-    if (stat.equals("median")) {
-      return new Double(median);
-    }
-    return delegate.getStat(stat);
-  }
-  
-  public void compute(){
-    delegate.compute();
-    median = getMedian();
-  }
-  
-  @Override
-  public void collect(int doc) throws IOException {
-    super.collect(doc);
-    if (value.exists) {
-      values.add(function.doubleVal(doc));
-    }
-  }
-}
-class DateMedianStatsCollector extends MedianStatsCollector{
-  
-  public DateMedianStatsCollector(StatsCollector delegate) {
-    super(delegate);
-  }
-
-  @Override
-  public Comparable getStat(String stat) {
-    if (stat.equals("median")) {
-      return new Date((long)median);
-    }
-    return delegate.getStat(stat);
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/MinMaxStatsCollector.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/MinMaxStatsCollector.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/MinMaxStatsCollector.java
deleted file mode 100644
index c21b045..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/MinMaxStatsCollector.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.solr.analytics.statistics;
-
-import java.io.IOException;
-import java.util.Locale;
-import java.util.Set;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.FunctionValues.ValueFiller;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.util.mutable.MutableValue;
-
-/**
- * <code>MinMaxStatsCollector</code> computes the min, max, number of values and number of missing values.
- */
-public class MinMaxStatsCollector implements StatsCollector{
-  protected long missingCount = 0;
-  protected long valueCount = 0;
-  protected MutableValue max;
-  protected MutableValue min;
-  protected MutableValue value;
-  protected final Set<String> statsList;
-  protected final ValueSource source;
-  protected FunctionValues function;
-  protected ValueFiller valueFiller;
-  
-  public MinMaxStatsCollector(ValueSource source, Set<String> statsList) {
-    this.source = source;
-    this.statsList = statsList;
-  }
-  
-  public void setNextReader(LeafReaderContext context) throws IOException {
-    function = source.getValues(null, context);
-    valueFiller = function.getValueFiller();
-    value = valueFiller.getValue();
-  }
-  
-  public void collect(int doc) throws IOException {
-    valueFiller.fillValue(doc);
-    if( value.exists ){
-      valueCount += 1;
-      if ( max==null ) max = value.duplicate();
-      else if( !max.exists || value.compareTo(max) > 0 ) max.copy(value);
-      if ( min==null ) min = value.duplicate();
-      else if( !min.exists || value.compareTo(min) < 0 ) min.copy(value);
-    } else {
-      missingCount += 1;
-    }
-  }
- 
-  @Override
-  public String toString() {
-    return String.format(Locale.ROOT, "<min=%s max=%s c=%d m=%d>", min, max, valueCount, missingCount );
-  }
-  
-  public Comparable getStat(String stat){
-    if (stat.equals("min")&&min!=null) {
-      return (Comparable)min.toObject();
-    }
-    if (stat.equals("max")&&max!=null) {
-      return (Comparable)max.toObject();
-    }
-    if (stat.equals("count")) {
-      return new Long(valueCount);
-    }
-    if (stat.equals("missing")) {
-      return new Long(missingCount);
-    }
-
-    return null;
-//    throw new IllegalArgumentException("No stat named '"+stat+"' in this collector " + this);
-  }
-  
-  public Set<String> getStatsList() {
-    return statsList;
-  }
-
-  @Override
-  public void compute() {  }
-  
-  @Override 
-  public MutableValue getValue() {
-    return value;
-  }
-  
-  @Override 
-  public FunctionValues getFunction() {
-    return function;
-  }
-  
-  public String valueSourceString() {
-    return source.toString();
-  }
-  
-  public String statString(String stat) {
-    return stat+"("+valueSourceString()+")";
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/NumericStatsCollector.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/NumericStatsCollector.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/NumericStatsCollector.java
deleted file mode 100644
index 1f22baa..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/NumericStatsCollector.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.solr.analytics.statistics;
-
-import java.io.IOException;
-import java.util.Set;
-
-import org.apache.lucene.queries.function.ValueSource;
-
-/**
- * <code>NumericStatsCollector</code> computes the sum, sum of squares, mean and standard deviation.
- */
-public class NumericStatsCollector extends MinMaxStatsCollector {
-  protected double sum = 0;
-  protected double sumOfSquares = 0;
-  protected double mean = 0;
-  protected double stddev = 0;
-  
-  public NumericStatsCollector(ValueSource source, Set<String> statsList) {
-    super(source, statsList);
-  }
-  
-  public void collect(int doc) throws IOException {
-    super.collect(doc);
-    double value = function.doubleVal(doc);
-    sum += value;
-    sumOfSquares += (value * value);
-  }
-  
-  @Override
-  public Comparable getStat(String stat) {
-    if (stat.equals("sum")) {
-      return new Double(sum);
-    }
-    if (stat.equals("sumofsquares")) {
-      return new Double(sumOfSquares);
-    }
-    if (stat.equals("mean")) {
-      return new Double(mean);
-    }
-    if (stat.equals("stddev")) {
-      return new Double(stddev);
-    }
-    return super.getStat(stat);
-  }  
-  
-  @Override
-  public void compute(){
-    super.compute();
-    mean = (valueCount==0)? 0:sum / valueCount;
-    stddev = (valueCount <= 1) ? 0.0D : Math.sqrt((sumOfSquares/valueCount) - (mean*mean));
-  }
-  
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/PercentileStatsCollector.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/PercentileStatsCollector.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/PercentileStatsCollector.java
deleted file mode 100644
index e12cb83..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/PercentileStatsCollector.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.solr.analytics.statistics;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Pattern;
-
-import org.apache.solr.analytics.util.PercentileCalculator;
-
-import com.google.common.collect.Iterables;
-
-/**
- * <code>PercentileStatsCollector</code> computes a given list of percentiles.
- */
-@SuppressWarnings("rawtypes")
-public class PercentileStatsCollector extends AbstractDelegatingStatsCollector{
-  public final List<Comparable> values = new ArrayList<>();
-  public static final Pattern PERCENTILE_PATTERN = Pattern.compile("perc(?:entile)?_(\\d+)",Pattern.CASE_INSENSITIVE);
-  protected final double[] percentiles;
-  protected final String[] percentileNames;
-  protected Comparable[] results;
-  
-  public PercentileStatsCollector(StatsCollector delegate, double[] percentiles, String[] percentileNames) {
-    super(delegate);
-    this.percentiles = percentiles;
-    this.percentileNames = percentileNames;
-  }
-
-  @Override
-  public Comparable getStat(String stat) {
-    for( int i=0; i < percentiles.length; i++ ){
-      if (stat.equals(percentileNames[i])) {
-        if (results!=null) {
-          return results[i];
-        } else {
-          return null;
-        }
-      }
-    }
-    return delegate.getStat(stat);
-  }
-
-  public void compute(){
-    delegate.compute();
-    if (values.size()>0) {
-      results = Iterables.toArray(getPercentiles(),Comparable.class);
-    } else {
-      results = null;
-    }
-  }
-
-  @SuppressWarnings({ "unchecked"})
-  protected List<Comparable> getPercentiles() {
-    return PercentileCalculator.getPercentiles(values, percentiles);
-  }
-  
-  public void collect(int doc) throws IOException {
-    super.collect(doc);
-    if (value.exists) {
-      values.add((Comparable)value.toObject());
-    }
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/StatsCollector.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/StatsCollector.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/StatsCollector.java
deleted file mode 100644
index 039300f..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/StatsCollector.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.solr.analytics.statistics;
-
-import java.io.IOException;
-import java.util.Set;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.util.mutable.MutableValue;
-
-/**
- * <code>StatsCollector</code> implementations reduce a list of Objects to a single value.
- * Most implementations reduce a list to a statistic on that list.
- */
-public interface StatsCollector {
-  
-  /**
-   * Collect values from the value source and add to statistics.
-   * @param doc Document to collect from
-   */
-  void collect(int doc) throws IOException;
-  
-  /**
-   * @param context The context to read documents from.
-   * @throws IOException if setting next reader fails
-   */
-  void setNextReader(LeafReaderContext context) throws IOException;
-  
-  MutableValue getValue();
-  FunctionValues getFunction();
-  
-  /**
-   * @return The set of statistics being computed by the stats collector.
-   */
-  Set<String> getStatsList();
-  
-  /**
-   * Return the value of the given statistic.
-   * @param stat the stat
-   * @return a comparable
-   */
-  Comparable getStat(String stat);
-  
-  /**
-   * After all documents have been collected, this method should be
-   * called to finalize the calculations of each statistic.
-   */
-  void compute();
-  
-  /**
-   * @return The string representation of the value source.
-   */
-  String valueSourceString();
-}


[07/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/LongDataArrayWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/LongDataArrayWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/LongDataArrayWriter.java
new file mode 100644
index 0000000..12fc86e
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/LongDataArrayWriter.java
@@ -0,0 +1,36 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+
+public class LongDataArrayWriter extends ReductionDataArrayWriter<LongSupplier> {
+
+  public LongDataArrayWriter(DataOutput output, LongSupplier extractor, IntSupplier sizeSupplier) {
+    super(output, extractor, sizeSupplier);
+  }
+  
+  @Override
+  public void write(int size) throws IOException {
+    for (int i = 0; i < size; ++i) {
+      output.writeLong(extractor.getAsLong());
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/LongDataWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/LongDataWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/LongDataWriter.java
new file mode 100644
index 0000000..3b8af52
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/LongDataWriter.java
@@ -0,0 +1,33 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.LongSupplier;
+
+public class LongDataWriter extends ReductionDataWriter<LongSupplier> {
+  
+  public LongDataWriter(DataOutput output, LongSupplier extractor) {
+    super(output, extractor);
+  }
+
+  @Override
+  public void write() throws IOException {
+    output.writeLong(extractor.getAsLong());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/ReductionCheckedDataWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/ReductionCheckedDataWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/ReductionCheckedDataWriter.java
new file mode 100644
index 0000000..a5a2273
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/ReductionCheckedDataWriter.java
@@ -0,0 +1,60 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.BooleanSupplier;
+
+/**
+ * Abstract class to manage the extraction and writing of data to a {@link DataOutput} stream.
+ * The data being written may not exist, so the writer first writes whether the data exists before writing the data.
+ */
+public abstract class ReductionCheckedDataWriter<C> extends ReductionDataWriter<C> {
+  private final BooleanSupplier existsSupplier;
+  
+  public ReductionCheckedDataWriter(DataOutput output, C extractor, BooleanSupplier existsSupplier) {
+    super(output, extractor);
+    
+    this.existsSupplier = existsSupplier;
+  }
+  
+  /**
+   * Write a piece of data, retrieved from the extractor, to the output stream.
+   * <br>
+   * First writes whether the data exists, then if it does exists writes the data.
+   * 
+   * @throws IOException if an exception occurs while writing to the output stream
+   */
+  @Override
+  public void write() throws IOException {
+    boolean exists = existsSupplier.getAsBoolean();
+    output.writeBoolean(exists);
+    if (exists) {
+      checkedWrite();
+    }
+  }
+  
+  /**
+   * Write a piece of data, retrieved from the extractor, to the output stream.
+   * <br>
+   * The data being written is guaranteed to exist.
+   * 
+   * @throws IOException if an exception occurs while writing to the output stream
+   */
+  protected abstract void checkedWrite() throws IOException;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/ReductionDataArrayWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/ReductionDataArrayWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/ReductionDataArrayWriter.java
new file mode 100644
index 0000000..29ba77e
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/ReductionDataArrayWriter.java
@@ -0,0 +1,53 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.IntSupplier;
+
+/**
+ * Abstract class to manage the extraction and writing of array data to a {@link DataOutput} stream.
+ */
+public abstract class ReductionDataArrayWriter<C> extends ReductionDataWriter<C> {
+  private final IntSupplier sizeSupplier;
+  
+  public ReductionDataArrayWriter(DataOutput output, C extractor, IntSupplier sizeSupplier) {
+    super(output, extractor);
+    
+    this.sizeSupplier = sizeSupplier;
+  }
+  
+  /**
+   * Write an array of data, retrieved from the extractor, and its size, received from the sizeSupplier, to the output stream.
+   * 
+   * @throws IOException if an exception occurs while writing to the output stream
+   */
+  @Override
+  public void write() throws IOException {
+    int size = sizeSupplier.getAsInt();
+    output.writeInt(size);
+    write(size);
+  }
+  
+  /**
+   * Write an array of data, retrieved from the extractor, with the given size to the output stream.
+   * 
+   * @throws IOException if an exception occurs while writing to the output stream
+   */
+  protected abstract void write(int size) throws IOException;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/ReductionDataWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/ReductionDataWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/ReductionDataWriter.java
new file mode 100644
index 0000000..504a2be
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/ReductionDataWriter.java
@@ -0,0 +1,40 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+
+/**
+ * Abstract public class to manage the extraction and writing of data to a {@link DataOutput} stream.
+ */
+public abstract class ReductionDataWriter<E> {
+  protected final DataOutput output;
+  protected final E extractor;
+  
+  public ReductionDataWriter(DataOutput output, E extractor) {
+    this.output = output;
+    this.extractor = extractor;
+  }
+
+  /**
+   * Write a piece of data, retrieved from the extractor, to the output stream.
+   * 
+   * @throws IOException if an exception occurs while writing to the output stream
+   */
+  public abstract void write() throws IOException;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/StringCheckedDataWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/StringCheckedDataWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/StringCheckedDataWriter.java
new file mode 100644
index 0000000..6560a8f
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/StringCheckedDataWriter.java
@@ -0,0 +1,34 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.BooleanSupplier;
+import java.util.function.Supplier;
+
+public class StringCheckedDataWriter extends ReductionCheckedDataWriter<Supplier<String>> {
+  
+  public StringCheckedDataWriter(DataOutput output, Supplier<String> extractor, BooleanSupplier existsSupplier) {
+    super(output, extractor, existsSupplier);
+  }
+
+  @Override
+  public void checkedWrite() throws IOException {
+    output.writeUTF(extractor.get());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/StringDataArrayWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/StringDataArrayWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/StringDataArrayWriter.java
new file mode 100644
index 0000000..18c71d1
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/StringDataArrayWriter.java
@@ -0,0 +1,36 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.IntSupplier;
+import java.util.function.Supplier;
+
+public class StringDataArrayWriter extends ReductionDataArrayWriter<Supplier<String>> {
+
+  public StringDataArrayWriter(DataOutput output, Supplier<String> extractor, IntSupplier sizeSupplier) {
+    super(output, extractor, sizeSupplier);
+  }
+  
+  @Override
+  public void write(int size) throws IOException {
+    for (int i = 0; i < size; ++i) {
+      output.writeUTF(extractor.get());
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/StringDataWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/StringDataWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/StringDataWriter.java
new file mode 100644
index 0000000..4aac07c
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/StringDataWriter.java
@@ -0,0 +1,37 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.Supplier;
+
+public class StringDataWriter extends ReductionDataWriter<Supplier<String>> {
+  
+  public StringDataWriter(DataOutput output, Supplier<String> extractor) {
+    super(output, extractor);
+  }
+
+  @Override
+  public void write() throws IOException {
+    String temp = extractor.get();
+    output.writeBoolean(temp != null);
+    if (temp != null) {
+      output.writeUTF(temp);
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/package-info.java
new file mode 100644
index 0000000..53a5168
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+ 
+/** 
+ * Writing classes for a single type of data being stored by one Reduction Data Collector.
+ * These writers are used to export data between shards during the streaming process.
+ */
+package org.apache.solr.analytics.stream.reservation.write;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsParams.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsParams.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsParams.java
deleted file mode 100644
index f6716ff..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsParams.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.solr.analytics.util;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.solr.common.params.FacetParams.FacetRangeInclude;
-import org.apache.solr.common.params.FacetParams.FacetRangeOther;
-import org.apache.solr.search.function.ConcatStringFunction;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-
-public interface AnalyticsParams {
-  // Full length Analytics Params
-  public static final String ANALYTICS = "olap";
-  
-  public static final String REQUEST = "o|olap";
-
-  public static final String EXPRESSION = "s|stat|statistic";
-  public static final String HIDDEN_EXPRESSION = "hs|hiddenstat|hiddenstatistic";
-
-  public static final String FIELD_FACET = "ff|fieldfacet";
-  public static final String LIMIT = "l|limit";
-  public static final String OFFSET = "off|offset";
-  public static final String HIDDEN = "h|hidden";
-  public static final String SHOW_MISSING = "sm|showmissing";
-  public static final String SORT_STATISTIC ="ss|sortstat|sortstatistic";
-  public static final String SORT_DIRECTION ="sd|sortdirection";
-  
-  public static final String RANGE_FACET = "rf|rangefacet";
-  public static final String START = "st|start";
-  public static final String END = "e|end";
-  public static final String GAP = "g|gap";
-  public static final String HARDEND = "he|hardend";
-  public static final String INCLUDE_BOUNDARY = "ib|includebound";
-  public static final String OTHER_RANGE = "or|otherrange";
-  
-  public static final String QUERY_FACET = "qf|queryfacet";
-  public static final String DEPENDENCY = "d|dependecy";
-  public static final String QUERY = "q|query";
-  
-  //Defaults
-  public static final boolean DEFAULT_ABBREVIATE_PREFIX = true;
-  public static final String DEFAULT_SORT_DIRECTION = "ascending";
-  public static final int DEFAULT_LIMIT = -1;
-  public static final boolean DEFAULT_HIDDEN = false;
-  public static final boolean DEFAULT_HARDEND = false;
-  public static final boolean DEFAULT_SHOW_MISSING = false;
-  public static final FacetRangeInclude DEFAULT_INCLUDE = FacetRangeInclude.LOWER;
-  public static final FacetRangeOther DEFAULT_OTHER = FacetRangeOther.NONE;
-  
-  // Statistic Function Names (Cannot share names with ValueSource & Expression Functions)
-  public static final String STAT_COUNT = "count";
-  public static final String STAT_MISSING = "missing";
-  public static final String STAT_SUM = "sum";
-  public static final String STAT_SUM_OF_SQUARES = "sumofsquares";
-  public static final String STAT_STANDARD_DEVIATION = "stddev";
-  public static final String STAT_MEAN = "mean";
-  public static final String STAT_UNIQUE = "unique";
-  public static final String STAT_MEDIAN = "median";
-  public static final String STAT_PERCENTILE = "percentile";
-  public static final String STAT_MIN = "min";
-  public static final String STAT_MAX = "max";
-  
-  public static final List<String> ALL_STAT_LIST = Collections.unmodifiableList(Lists.newArrayList(STAT_COUNT, STAT_MISSING, STAT_SUM, STAT_SUM_OF_SQUARES, STAT_STANDARD_DEVIATION, STAT_MEAN, STAT_UNIQUE, STAT_MEDIAN, STAT_PERCENTILE,STAT_MIN,STAT_MAX));
-  public static final Set<String> ALL_STAT_SET = Collections.unmodifiableSet(Sets.newLinkedHashSet(ALL_STAT_LIST));
-
-  // ValueSource & Expression Function Names (Cannot share names with Statistic Functions)
-  // No specific type
-  final static String FILTER = "filter";
-  final static String RESULT = "result";
-  final static String QUERY_RESULT = "qresult";
-  
-  // Numbers
-  final static String CONSTANT_NUMBER = "const_num";
-  final static String NEGATE = "neg";
-  final static String ABSOLUTE_VALUE = "abs";
-  final static String LOG = "log";
-  final static String ADD = "add";
-  final static String MULTIPLY = "mult";
-  final static String DIVIDE = "div";
-  final static String POWER = "pow";
-  public static final Set<String> NUMERIC_OPERATION_SET = Collections.unmodifiableSet(Sets.newLinkedHashSet(Lists.newArrayList(CONSTANT_NUMBER,NEGATE,ABSOLUTE_VALUE,LOG,ADD,MULTIPLY,DIVIDE,POWER)));
-  
-  // Dates
-  final static String CONSTANT_DATE = "const_date";
-  final static String DATE_MATH = "date_math";
-  public static final Set<String> DATE_OPERATION_SET = Collections.unmodifiableSet(Sets.newLinkedHashSet(Lists.newArrayList(CONSTANT_DATE,DATE_MATH)));
-  
-  //Strings
-  final static String CONSTANT_STRING = "const_str";
-  final static String REVERSE = "rev";
-  final static String CONCATENATE = ConcatStringFunction.NAME;
-  public static final Set<String> STRING_OPERATION_SET = Collections.unmodifiableSet(Sets.newLinkedHashSet(Lists.newArrayList(CONSTANT_STRING,REVERSE,CONCATENATE)));
-  
-  // Field Source Wrappers
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsParsers.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsParsers.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsParsers.java
deleted file mode 100644
index dd64c3f..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsParsers.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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.solr.analytics.util;
-
-import java.io.IOException;
-import java.time.Instant;
-import java.util.Arrays;
-
-import org.apache.solr.legacy.LegacyNumericUtils;
-import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.NumericUtils;
-import org.apache.solr.schema.FieldType;
-import org.apache.solr.schema.TrieDateField;
-import org.apache.solr.schema.TrieDoubleField;
-import org.apache.solr.schema.TrieFloatField;
-import org.apache.solr.schema.TrieIntField;
-import org.apache.solr.schema.TrieLongField;
-
-/** 
- * Class to hold the parsers used for Solr Analytics.
- */
-public class AnalyticsParsers {
-
-  /**
-   * Returns a parser that will translate a BytesRef or long from DocValues into 
-   * a String that correctly represents the value.
-   * @param class1 class of the FieldType of the field being faceted on.
-   * @return A Parser
-   */
-  public static Parser getParser(Class<? extends FieldType> class1) {
-    if (class1.equals(TrieIntField.class)) {
-      return AnalyticsParsers.INT_DOC_VALUES_PARSER;
-    } else if (class1.equals(TrieLongField.class)) {
-      return AnalyticsParsers.LONG_DOC_VALUES_PARSER;
-    } else if (class1.equals(TrieFloatField.class)) {
-      return AnalyticsParsers.FLOAT_DOC_VALUES_PARSER;
-    } else if (class1.equals(TrieDoubleField.class)) {
-      return AnalyticsParsers.DOUBLE_DOC_VALUES_PARSER;
-    } else if (class1.equals(TrieDateField.class)) {
-      return AnalyticsParsers.DATE_DOC_VALUES_PARSER;
-    } else {
-      return AnalyticsParsers.STRING_PARSER;
-    }
-  }
-
-  /**
-   * For use in classes that grab values by docValue.
-   * Converts a BytesRef object into the correct readable text.
-   */
-  public static interface Parser {
-    String parse(BytesRef bytes) throws IOException;
-  }
-  
-  /**
-   * Converts the long returned by NumericDocValues into the
-   * correct number and return it as a string.
-   */
-  public static interface NumericParser extends Parser {
-    String parseNum(long l);
-  }
-  
-  /**
-   * Converts the BytesRef or long to the correct int string.
-   */
-  public static final NumericParser INT_DOC_VALUES_PARSER = new NumericParser() {
-    public String parse(BytesRef bytes) throws IOException {
-      try {
-        return ""+ LegacyNumericUtils.prefixCodedToInt(bytes);
-      } catch (NumberFormatException e) {
-        throw new IOException("The byte array "+Arrays.toString(bytes.bytes)+" cannot be converted to an int.");
-      }
-    }
-    @Override
-    public String parseNum(long l) {
-      return ""+(int)l;
-    }
-  };
-  
-  /**
-   * Converts the BytesRef or long to the correct long string.
-   */
-  public static final NumericParser LONG_DOC_VALUES_PARSER = new NumericParser() {
-    public String parse(BytesRef bytes) throws IOException {
-      try {
-        return ""+ LegacyNumericUtils.prefixCodedToLong(bytes);
-      } catch (NumberFormatException e) {
-        throw new IOException("The byte array "+Arrays.toString(bytes.bytes)+" cannot be converted to a long.");
-      }
-    }
-    @Override
-    public String parseNum(long l) {
-      return ""+l;
-    }
-  };
-  
-  /**
-   * Converts the BytesRef or long to the correct float string.
-   */
-  public static final NumericParser FLOAT_DOC_VALUES_PARSER = new NumericParser() {
-    public String parse(BytesRef bytes) throws IOException {
-      try {
-        return ""+ NumericUtils.sortableIntToFloat(LegacyNumericUtils.prefixCodedToInt(bytes));
-      } catch (NumberFormatException e) {
-        throw new IOException("The byte array "+Arrays.toString(bytes.bytes)+" cannot be converted to a float.");
-      }
-    }
-    @Override
-    public String parseNum(long l) {
-      return ""+ NumericUtils.sortableIntToFloat((int) l);
-    }
-  };
-  
-  /**
-   * Converts the BytesRef or long to the correct double string.
-   */
-  public static final NumericParser DOUBLE_DOC_VALUES_PARSER = new NumericParser() {
-    public String parse(BytesRef bytes) throws IOException {
-      try {
-        return ""+ NumericUtils.sortableLongToDouble(LegacyNumericUtils.prefixCodedToLong(bytes));
-      } catch (NumberFormatException e) {
-        throw new IOException("The byte array "+Arrays.toString(bytes.bytes)+" cannot be converted to a double.");
-      }
-    }
-    @Override
-    public String parseNum(long l) {
-      return ""+ NumericUtils.sortableLongToDouble(l);
-    }
-  };
-  
-  /**
-   * Converts the BytesRef or long to the correct date string.
-   */
-  public static final NumericParser DATE_DOC_VALUES_PARSER = new NumericParser() {
-    @SuppressWarnings("deprecation")
-    public String parse(BytesRef bytes) throws IOException {
-      try {
-        return Instant.ofEpochMilli(LegacyNumericUtils.prefixCodedToLong(bytes)).toString();
-      } catch (NumberFormatException e) {
-        throw new IOException("The byte array "+Arrays.toString(bytes.bytes)+" cannot be converted to a date.");
-      }
-    }
-    @SuppressWarnings("deprecation")
-    @Override
-    public String parseNum(long l) {
-      return Instant.ofEpochMilli(l).toString();
-    }
-  };
-  
-  /**
-   * Converts the BytesRef to the correct string.
-   */
-  public static final Parser STRING_PARSER = new Parser() {
-    public String parse(BytesRef bytes) {
-      return bytes.utf8ToString();
-    }
-  };
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsResponseHeadings.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsResponseHeadings.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsResponseHeadings.java
new file mode 100644
index 0000000..00e0afb
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/AnalyticsResponseHeadings.java
@@ -0,0 +1,36 @@
+/*
+ * 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.solr.analytics.util;
+
+/**
+ * Holds the headers for analytics responses.
+ */
+public class AnalyticsResponseHeadings {
+
+  public static final String COMPLETED_HEADER = "analytics_response";
+  public static final String RESULTS = "results";
+  public static final String GROUPINGS = "groupings";
+  public static final String FACET_VALUE = "value";
+  public static final String PIVOT_NAME = "pivot";
+  public static final String PIVOT_CHILDREN = "children";
+  
+  // Old Olap-style
+  public static final String COMPLETED_OLD_HEADER = "stats";
+  public static final String FIELD_FACETS = "fieldFacets";
+  public static final String RANGE_FACETS = "rangeFacets";
+  public static final String QUERY_FACETS = "queryFacets";
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/FacetRangeGenerator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/FacetRangeGenerator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/FacetRangeGenerator.java
new file mode 100644
index 0000000..0576096
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/FacetRangeGenerator.java
@@ -0,0 +1,356 @@
+/*
+ * 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.solr.analytics.util;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.solr.analytics.facet.RangeFacet;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.FacetParams.FacetRangeInclude;
+import org.apache.solr.common.params.FacetParams.FacetRangeOther;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.schema.TrieDateField;
+import org.apache.solr.schema.TrieField;
+import org.apache.solr.util.DateMathParser;
+
+
+/**
+ * Calculates a set of {@link FacetRange}s for a given {@link RangeFacet}.
+ */
+public abstract class FacetRangeGenerator<T extends Comparable<T>> {
+  protected final SchemaField field;
+  protected final RangeFacet rangeFacet;
+  
+  public FacetRangeGenerator(final RangeFacet rangeFacet) {
+    this.field = rangeFacet.getField();
+    this.rangeFacet = rangeFacet;
+  }
+
+  /**
+   * Formats a Range endpoint for use as a range label name in the response.
+   * Default Impl just uses toString()
+   */
+  public String formatValue(final T val) {
+    return val.toString();
+  }
+  
+  /**
+   * Parses a String param into an Range endpoint value throwing 
+   * a useful exception if not possible
+   */
+  public final T getValue(final String rawval) {
+    try {
+      return parseVal(rawval);
+    } catch (Exception e) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Can't parse value "+rawval+" for field: " + field.getName(), e);
+    }
+  }
+  
+  /**
+   * Parses a String param into an Range endpoint. 
+   * Can throw a low level format exception as needed.
+   */
+  protected abstract T parseVal(final String rawval) throws java.text.ParseException;
+
+  /** 
+   * Parses a String param into a value that represents the gap and 
+   * can be included in the response, throwing 
+   * a useful exception if not possible.
+   *
+   * Note: uses Object as the return type instead of T for things like 
+   * Date where gap is just a DateMathParser string 
+   */
+  public final Object getGap(final String gap) {
+    try {
+      return parseGap(gap);
+    } catch (Exception e) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Can't parse gap "+gap+" for field: " + field.getName(), e);
+    }
+  }
+
+  /**
+   * Parses a String param into a value that represents the gap and 
+   * can be included in the response. 
+   * Can throw a low level format exception as needed.
+   *
+   * Default Impl calls parseVal
+   */
+  protected Object parseGap(final String rawval) throws java.text.ParseException {
+    return parseVal(rawval);
+  }
+
+  /**
+   * Adds the String gap param to a low Range endpoint value to determine 
+   * the corrisponding high Range endpoint value, throwing 
+   * a useful exception if not possible.
+   */
+  public final T addGap(T value, String gap) {
+    try {
+      return parseAndAddGap(value, gap);
+    } catch (Exception e) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Can't add gap "+gap+" to value " + value + " for field: " + field.getName(), e);
+    }
+  }
+  
+  /**
+   * Adds the String gap param to a low Range endpoint value to determine 
+   * the corrisponding high Range endpoint value.
+   * Can throw a low level format exception as needed.
+   */
+  protected abstract T parseAndAddGap(T value, String gap) throws java.text.ParseException;
+
+  public static class FacetRange {
+    public final String name;
+    public final String lower;
+    public final String upper;
+    public final boolean includeLower;
+    public final boolean includeUpper;
+    private final String facetValue;
+    
+    public FacetRange(String name, String lower, String upper, boolean includeLower, boolean includeUpper) {
+      this.name = name;
+      this.lower = lower;
+      this.upper = upper;
+      this.includeLower = includeLower;
+      this.includeUpper = includeUpper;
+      
+      String value = "(*";
+      if (lower != null) {
+        value = (includeLower ? "[" : "(") + lower;
+      }
+      value += " TO ";
+      if (upper == null) {
+        value += "*)";
+      } else {
+        value += upper + (includeUpper? "]" : ")");
+      }
+      facetValue = value;
+    }
+    
+    @Override
+    public String toString() {
+        return facetValue;
+    }
+  }
+  
+  public List<FacetRange> getRanges(){
+
+    final T start = getValue(rangeFacet.getStart());
+    T end = getValue(rangeFacet.getEnd()); // not final, hardend may change this
+    
+    if( end.compareTo(start) < 0 ){
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "range facet 'end' comes before 'start': "+end+" < "+start);
+    }
+    
+    // explicitly return the gap.  compute this early so we are more 
+    // likely to catch parse errors before attempting math
+    final List<String> gaps = rangeFacet.getGaps();
+    String gap = gaps.get(0);
+    
+    final EnumSet<FacetRangeInclude> include = rangeFacet.getInclude();
+        
+    T low = start;
+    
+    List<FacetRange> ranges = new ArrayList<>();
+    
+    int gapCounter = 0;
+    
+    while (low.compareTo(end) < 0) {
+      if (gapCounter<gaps.size()) {
+        gap = gaps.get(gapCounter++);
+      }
+      T high = addGap(low,gap);
+      if (end.compareTo(high) < 0) {
+        if (rangeFacet.isHardEnd()){
+          high = end;
+        } else {
+          end = high;
+        }
+      }
+      
+      if (high.compareTo(low) < 0) {
+        throw new SolrException (SolrException.ErrorCode.BAD_REQUEST, "range facet infinite loop (is gap negative? did the math overflow?)");
+      }
+      
+      if (high.compareTo(low) == 0) {
+        throw new SolrException (SolrException.ErrorCode.BAD_REQUEST, "range facet infinite loop: gap is either zero, or too small relative start/end and caused underflow: " + low + " + " + gap + " = " + high );
+      }
+      
+      final boolean includeLower = (include.contains(FacetRangeInclude.ALL) ||
+                                    include.contains(FacetRangeInclude.LOWER) ||
+                                   (include.contains(FacetRangeInclude.EDGE) && 
+                                   0 == low.compareTo(start)));
+      final boolean includeUpper = (include.contains(FacetRangeInclude.ALL) ||
+                                    include.contains(FacetRangeInclude.UPPER) ||
+                                   (include.contains(FacetRangeInclude.EDGE) && 
+                                   0 == high.compareTo(end)));
+      
+      final String lowS = formatValue(low);
+      final String highS = formatValue(high);
+
+      ranges.add( new FacetRange(lowS,lowS,highS,includeLower,includeUpper) );
+      low = high;
+    }
+    
+    final Set<FacetRangeOther> others = rangeFacet.getOthers();
+    if (null != others && 0 < others.size() ) {
+      
+      // no matter what other values are listed, we don't do
+      // anything if "none" is specified.
+      if( !others.contains(FacetRangeOther.NONE) ) {
+        
+        boolean all = others.contains(FacetRangeOther.ALL);
+
+        if (all || others.contains(FacetRangeOther.BEFORE)) {
+          // include upper bound if "outer" or if first gap doesn't already include it
+          ranges.add( new FacetRange(FacetRangeOther.BEFORE.toString(), 
+                                        null, formatValue(start), false, include.contains(FacetRangeInclude.OUTER) || include.contains(FacetRangeInclude.ALL) ||
+                                                            !(include.contains(FacetRangeInclude.LOWER) || include.contains(FacetRangeInclude.EDGE)) ) );
+          
+        }
+        if (all || others.contains(FacetRangeOther.AFTER)) {
+          // include lower bound if "outer" or if last gap doesn't already include it
+          ranges.add( new FacetRange(FacetRangeOther.AFTER.toString(), 
+                                        formatValue(end), null, include.contains(FacetRangeInclude.OUTER) || include.contains(FacetRangeInclude.ALL) ||
+                                                   !(include.contains(FacetRangeInclude.UPPER) || include.contains(FacetRangeInclude.EDGE)), false) );
+        }
+        if (all || others.contains(FacetRangeOther.BETWEEN)) {
+          ranges.add( new FacetRange(FacetRangeOther.BETWEEN.toString(), formatValue(start), formatValue(end),
+                                        include.contains(FacetRangeInclude.LOWER) || include.contains(FacetRangeInclude.EDGE) || include.contains(FacetRangeInclude.ALL),
+                                        include.contains(FacetRangeInclude.UPPER) || include.contains(FacetRangeInclude.EDGE) || include.contains(FacetRangeInclude.ALL)) );
+        }
+      }
+      
+    }
+  
+    return ranges;
+  }
+  
+  public static FacetRangeGenerator<? extends Comparable<?>> create(RangeFacet rangeFacet){
+    final SchemaField sf = rangeFacet.getField();
+    final FieldType ft = sf.getType();
+    final FacetRangeGenerator<?> calc;
+    if (ft instanceof TrieField) {
+      switch (ft.getNumberType()) {
+        case FLOAT:
+          calc = new FloatFacetRangeGenerator(rangeFacet);
+          break;
+        case DOUBLE:
+          calc = new DoubleFacetRangeGenerator(rangeFacet);
+          break;
+        case INTEGER:
+          calc = new IntegerFacetRangeGenerator(rangeFacet);
+          break;
+        case LONG:
+          calc = new LongFacetRangeGenerator(rangeFacet);
+          break;
+        case DATE:
+          calc = new DateFacetRangeGenerator(rangeFacet, null);
+          break;
+        default:
+          throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unable to range facet on tried field of unexpected type:" + sf.getName());
+      }
+    } else {
+      throw new SolrException (SolrException.ErrorCode.BAD_REQUEST, "Unable to range facet on field:" + sf);
+    } 
+    return calc;
+  }
+}
+class IntegerFacetRangeGenerator extends FacetRangeGenerator<Integer> {
+  public IntegerFacetRangeGenerator(final RangeFacet rangeFacet) { super(rangeFacet); }
+
+  @Override
+  protected Integer parseVal(String rawval) {
+    return Integer.valueOf(rawval);
+  }
+  @Override
+  public Integer parseAndAddGap(Integer value, String gap) {
+    return new Integer(value.intValue() + Integer.valueOf(gap).intValue());
+  }
+}
+class LongFacetRangeGenerator extends FacetRangeGenerator<Long> {
+  public LongFacetRangeGenerator(final RangeFacet rangeFacet) { super(rangeFacet); }
+
+  @Override
+  protected Long parseVal(String rawval) {
+    return Long.valueOf(rawval);
+  }
+  @Override
+  public Long parseAndAddGap(Long value, String gap) {
+    return new Long(value.longValue() + Long.valueOf(gap).longValue());
+  }
+}
+
+class FloatFacetRangeGenerator extends FacetRangeGenerator<Float> {
+  public FloatFacetRangeGenerator(final RangeFacet rangeFacet) { super(rangeFacet); }
+
+  @Override
+  protected Float parseVal(String rawval) {
+    return Float.valueOf(rawval);
+  }
+  @Override
+  public Float parseAndAddGap(Float value, String gap) {
+    return new Float(value.floatValue() + Float.valueOf(gap).floatValue());
+  }
+}
+
+class DoubleFacetRangeGenerator extends FacetRangeGenerator<Double> {
+  public DoubleFacetRangeGenerator(final RangeFacet rangeFacet) { super(rangeFacet); }
+
+  @Override
+  protected Double parseVal(String rawval) {
+    return Double.valueOf(rawval);
+  }
+  @Override
+  public Double parseAndAddGap(Double value, String gap) {
+    return new Double(value.doubleValue() + Double.valueOf(gap).doubleValue());
+  }
+}
+class DateFacetRangeGenerator extends FacetRangeGenerator<Date> {
+  private final Date now;
+  public DateFacetRangeGenerator(final RangeFacet rangeFacet, final Date now) { 
+    super(rangeFacet); 
+    this.now = now;
+    if (! (field.getType() instanceof TrieDateField) ) {
+      throw new IllegalArgumentException("SchemaField must use field type extending TrieDateField");
+    }
+  }
+  
+  @Override
+  public String formatValue(Date val) {
+    return val.toInstant().toString();
+  }
+  @Override
+  protected Date parseVal(String rawval) {
+    return DateMathParser.parseMath(now, rawval);
+  }
+  @Override
+  protected Object parseGap(final String rawval) {
+    return rawval;
+  }
+  @Override
+  public Date parseAndAddGap(Date value, String gap) throws java.text.ParseException {
+    final DateMathParser dmp = new DateMathParser();
+    dmp.setNow(value);
+    return dmp.parseMath(gap);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/MedianCalculator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/MedianCalculator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/MedianCalculator.java
index 52935e9..541cff0 100644
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/MedianCalculator.java
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/MedianCalculator.java
@@ -18,6 +18,10 @@ package org.apache.solr.analytics.util;
 
 import java.util.List;
 
+/**
+ * Only used for testing.
+ * Medians are calculated with the {@link OrdinalCalculator} for actual analytics requests.
+ */
 public class MedianCalculator {
 
   /**

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/OldAnalyticsParams.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/OldAnalyticsParams.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/OldAnalyticsParams.java
new file mode 100644
index 0000000..084d997
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/OldAnalyticsParams.java
@@ -0,0 +1,177 @@
+/*
+ * 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.solr.analytics.util;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+
+import org.apache.solr.analytics.AnalyticsRequestParser.AnalyticsExpressionSortRequest;
+import org.apache.solr.analytics.AnalyticsRequestParser.AnalyticsRangeFacetRequest;
+import org.apache.solr.analytics.AnalyticsRequestParser.AnalyticsSortRequest;
+import org.apache.solr.analytics.AnalyticsRequestParser.AnalyticsValueFacetRequest;
+
+/**
+ * Specifies the format of the old olap-style analytics requests.
+ */
+public interface OldAnalyticsParams {
+  // Old request language
+  public static final String OLD_ANALYTICS = "olap";
+
+  public static final String OLD_PREFIX = "o|olap";
+
+  public static final String OLD_EXPRESSION = "s|stat|statistic";
+  
+  public static class OldRequest {
+    public String name;
+    public Map<String,String> expressions = new HashMap<>();
+    public Map<String,OldFieldFacet> fieldFacets = new HashMap<>();
+    public Map<String,OldRangeFacet> rangeFacets = new HashMap<>();
+    public Map<String,OldQueryFacet> queryFacets = new HashMap<>();
+  }
+
+  public static final String FIELD_FACET = "ff|fieldfacet";
+  public static final String VALUE_FACET = "vf|valuefacet";
+  public static final String LIMIT = "l|limit";
+  public static final String OFFSET = "off|offset";
+  public static final String SHOW_MISSING = "sm|showmissing";
+  public static final String SORT_EXPRESSION ="se|sortexpr|sortexpression";
+  public static final String OLAP_SORT_EXPRESSION ="ss|sortstat|sortstatistic";
+  public static final String SORT_DIRECTION ="sd|sortdirection";
+  
+  public static class OldFieldFacet {
+    public String field;
+    public String showMissing;
+    public String limit;
+    public String offset;
+    public String sortExpr;
+    public String sortDir;
+  }
+  
+  public static class FieldFacetParamParser {
+    public static String regexParamList = LIMIT + "|" + OFFSET + "|" + SHOW_MISSING + "|" + OLAP_SORT_EXPRESSION + "|" + SORT_DIRECTION;
+
+    private static Predicate<String> isLimit = Pattern.compile("^" + LIMIT + "$", Pattern.CASE_INSENSITIVE).asPredicate();
+    private static Predicate<String> isOffset = Pattern.compile("^" + OFFSET + "$", Pattern.CASE_INSENSITIVE).asPredicate();
+    private static Predicate<String> isShowMissing = Pattern.compile("^" + SHOW_MISSING + "$", Pattern.CASE_INSENSITIVE).asPredicate();
+    private static Predicate<String> isSortExpr = Pattern.compile("^" + OLAP_SORT_EXPRESSION + "$", Pattern.CASE_INSENSITIVE).asPredicate();
+    private static Predicate<String> isSortDir = Pattern.compile("^" + SORT_DIRECTION + "$", Pattern.CASE_INSENSITIVE).asPredicate();
+    
+    public static void applyParam(AnalyticsValueFacetRequest facet, String param, String value) {
+      if (isLimit.test(param)) {
+        getSort(facet).limit = Integer.parseInt(value);
+      } else if (isOffset.test(param)) {
+        getSort(facet).offset = Integer.parseInt(value);
+      } else if (isShowMissing.test(param)) {
+        facet.expression = "fillmissing(" + facet.expression + ",\"(MISSING)\")";
+      } else if (isSortExpr.test(param)) {
+        AnalyticsSortRequest sort = getSort(facet);
+        AnalyticsExpressionSortRequest criterion;
+        if (sort.criteria.size() == 0) {
+          criterion = new AnalyticsExpressionSortRequest();
+          sort.criteria.add(criterion);
+        } else {
+          criterion = (AnalyticsExpressionSortRequest) sort.criteria.get(0);
+        }
+        criterion.expression = value;
+      } else if (isSortDir.test(param)) {
+        AnalyticsSortRequest sort = getSort(facet);
+        AnalyticsExpressionSortRequest criterion;
+        if (sort.criteria.size() == 0) {
+          criterion = new AnalyticsExpressionSortRequest();
+          sort.criteria.add(criterion);
+        } else {
+          criterion = (AnalyticsExpressionSortRequest) sort.criteria.get(0);
+        }
+        criterion.direction = value;
+      }
+    }
+    
+    public static AnalyticsSortRequest getSort(AnalyticsValueFacetRequest facet) {
+      if (facet.sort == null) {
+        facet.sort = new AnalyticsSortRequest();
+        facet.sort.criteria = new ArrayList<>();
+      }
+      return facet.sort;
+    }
+  }
+  
+  public static final String RANGE_FACET = "rf|rangefacet";
+  public static final String START = "st|start";
+  public static final String END = "e|end";
+  public static final String GAP = "g|gap";
+  public static final String HARDEND = "he|hardend";
+  public static final String INCLUDE_BOUNDARY = "ib|includebound";
+  public static final String OTHER_RANGE = "or|otherrange";
+  
+  public static class OldRangeFacet {
+    public String field;
+    public String start;
+    public String end;
+    public String gaps;
+    public String hardend;
+    public String[] include;
+    public String[] others;
+  }
+  
+  public static class RangeFacetParamParser {
+    public static String regexParamList = START + "|" + END + "|" + GAP + "|" + HARDEND + "|" + INCLUDE_BOUNDARY + "|" + OTHER_RANGE;
+
+    private static Predicate<String> isStart = Pattern.compile("^" + START + "$", Pattern.CASE_INSENSITIVE).asPredicate();
+    private static Predicate<String> isEnd = Pattern.compile("^" + END + "$", Pattern.CASE_INSENSITIVE).asPredicate();
+    private static Predicate<String> isGap = Pattern.compile("^" + GAP + "$", Pattern.CASE_INSENSITIVE).asPredicate();
+    private static Predicate<String> isHardEnd = Pattern.compile("^" + HARDEND + "$", Pattern.CASE_INSENSITIVE).asPredicate();
+    private static Predicate<String> isTrue = Pattern.compile("^t|true$", Pattern.CASE_INSENSITIVE).asPredicate();
+    private static Predicate<String> isFalse = Pattern.compile("^f|false$", Pattern.CASE_INSENSITIVE).asPredicate();
+    private static Predicate<String> isInclude = Pattern.compile("^" + INCLUDE_BOUNDARY + "$", Pattern.CASE_INSENSITIVE).asPredicate();
+    private static Predicate<String> isOther = Pattern.compile("^" + OTHER_RANGE + "$", Pattern.CASE_INSENSITIVE).asPredicate();
+    
+    public static void applyParam(AnalyticsRangeFacetRequest facet, String param, String[] values) {
+      if (isStart.test(param)) {
+        facet.start = values[0];
+      } else if (isEnd.test(param)) {
+        facet.end = values[0];
+      } else if (isGap.test(param)) {
+        facet.gaps = Arrays.asList(values[0].split(","));
+      } else if (isHardEnd.test(param)) {
+        if (isTrue.test(values[0])) {
+          facet.hardend = true;
+        } else if (isFalse.test(values[0])) {
+          facet.hardend = false;
+        }
+      } else if (isInclude.test(param)) {
+        facet.include = Arrays.asList(values);
+      } else if (isOther.test(param)) {
+        facet.others = Arrays.asList(values);
+      }
+    }
+  }
+  
+  public static class OldQueryFacet {
+    public String name;
+    public String[] queries;
+  }
+  
+  public static final String QUERY_FACET = "qf|queryfacet";
+  public static final String QUERY = "q|query";
+  
+  //Defaults
+  public static final boolean DEFAULT_ABBREVIATE_PREFIX = true;
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/OldAnalyticsRequestConverter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/OldAnalyticsRequestConverter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/OldAnalyticsRequestConverter.java
new file mode 100644
index 0000000..0124dc8
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/OldAnalyticsRequestConverter.java
@@ -0,0 +1,177 @@
+/*
+ * 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.solr.analytics.util;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.solr.analytics.AnalyticsRequestParser.AnalyticsGroupingRequest;
+import org.apache.solr.analytics.AnalyticsRequestParser.AnalyticsQueryFacetRequest;
+import org.apache.solr.analytics.AnalyticsRequestParser.AnalyticsRangeFacetRequest;
+import org.apache.solr.analytics.AnalyticsRequestParser.AnalyticsRequest;
+import org.apache.solr.analytics.AnalyticsRequestParser.AnalyticsValueFacetRequest;
+import org.apache.solr.common.params.SolrParams;
+
+/**
+ * Converts Analytics Requests in the old olap-style format to the new format.
+ */
+public class OldAnalyticsRequestConverter implements OldAnalyticsParams {
+  // Old language Parsing
+  private static final Pattern oldExprPattern = 
+      Pattern.compile("^(?:"+OLD_PREFIX+")\\.([^\\.]+)\\.(?:"+OLD_EXPRESSION+")\\.([^\\.]+)$", Pattern.CASE_INSENSITIVE);
+  private static final Pattern oldFieldFacetPattern = 
+      Pattern.compile("^(?:"+OLD_PREFIX+")\\.([^\\.]+)\\.(?:"+FIELD_FACET+")$", Pattern.CASE_INSENSITIVE);
+  private static final Pattern oldFieldFacetParamPattern = 
+      Pattern.compile("^(?:"+OLD_PREFIX+")\\.([^\\.]+)\\.(?:"+FIELD_FACET+")\\.([^\\.]+)\\.("+FieldFacetParamParser.regexParamList+")$", Pattern.CASE_INSENSITIVE);
+  private static final Pattern oldRangeFacetParamPattern =
+      Pattern.compile("^(?:"+OLD_PREFIX+")\\.([^\\.]+)\\.(?:"+RANGE_FACET+")\\.([^\\.]+)\\.("+RangeFacetParamParser.regexParamList+")$", Pattern.CASE_INSENSITIVE);
+  private static final Pattern oldQueryFacetParamPattern = 
+      Pattern.compile("^(?:"+OLD_PREFIX+")\\.([^\\.]+)\\.(?:"+QUERY_FACET+")\\.([^\\.]+)\\.("+QUERY+")$", Pattern.CASE_INSENSITIVE);
+  
+  /**
+   * Convert the old olap-style Analytics Request in the given params to
+   * an analytics request string using the current format.
+   * 
+   * @param params to find the analytics request in
+   * @return an analytics request string
+   */
+  public static AnalyticsRequest convert(SolrParams params) {
+    AnalyticsRequest request = new AnalyticsRequest();
+    request.expressions = new HashMap<>();
+    request.groupings = new HashMap<>();
+    Iterator<String> paramsIterator = params.getParameterNamesIterator();
+    while (paramsIterator.hasNext()) {
+      String param = paramsIterator.next();
+      CharSequence paramSequence = param.subSequence(0, param.length());
+      parseParam(request, param, paramSequence, params);
+    }
+    return request;
+  }
+  
+  private static void parseParam(AnalyticsRequest request, String param, CharSequence paramSequence, SolrParams params) {
+    // Check if grouped expression
+    Matcher m = oldExprPattern.matcher(paramSequence);
+    if (m.matches()) {
+      addExpression(request,m.group(1),m.group(2),params.get(param));
+      return;
+    }
+    
+    // Check if field facet parameter
+    m = oldFieldFacetPattern.matcher(paramSequence);
+    if (m.matches()) {
+      addFieldFacets(request,m.group(1),params.getParams(param));
+      return;
+    }
+    
+    // Check if field facet parameter
+    m = oldFieldFacetParamPattern.matcher(paramSequence);
+    if (m.matches()) {
+      setFieldFacetParam(request,m.group(1),m.group(2),m.group(3),params.getParams(param));
+      return;
+    }
+    
+    // Check if field facet parameter
+    m = oldFieldFacetParamPattern.matcher(paramSequence);
+    if (m.matches()) {
+      setFieldFacetParam(request,m.group(1),m.group(2),m.group(3),params.getParams(param));
+      return;
+    }
+
+    // Check if range facet parameter
+    m = oldRangeFacetParamPattern.matcher(paramSequence);
+    if (m.matches()) {
+      setRangeFacetParam(request,m.group(1),m.group(2),m.group(3),params.getParams(param));
+      return;
+    }
+
+    // Check if query
+    m = oldQueryFacetParamPattern.matcher(paramSequence);
+    if (m.matches()) {
+      setQueryFacetParam(request,m.group(1),m.group(2),m.group(3),params.getParams(param));
+      return;
+    } 
+  }
+  
+  private static AnalyticsGroupingRequest getGrouping(AnalyticsRequest request, String name) {
+    AnalyticsGroupingRequest grouping = request.groupings.get(name);
+    if (grouping == null) {
+      grouping = new AnalyticsGroupingRequest();
+      grouping.expressions = new HashMap<>();
+      grouping.facets = new HashMap<>();
+      request.groupings.put(name, grouping);
+    }
+    return grouping;
+  }
+
+  private static void addFieldFacets(AnalyticsRequest request, String groupingName, String[] params) {
+    AnalyticsGroupingRequest grouping = getGrouping(request, groupingName);
+    
+    for (String param : params) {
+      if (!grouping.facets.containsKey(param)) {
+        AnalyticsValueFacetRequest fieldFacet = new AnalyticsValueFacetRequest();
+        fieldFacet.expression = param;
+        grouping.facets.put(param, fieldFacet);
+      }
+    }
+  }
+
+  private static void setFieldFacetParam(AnalyticsRequest request, String groupingName, String field, String paramType, String[] params) {
+    AnalyticsGroupingRequest grouping = getGrouping(request, groupingName);
+    
+    AnalyticsValueFacetRequest fieldFacet = (AnalyticsValueFacetRequest) grouping.facets.get(field);
+    
+    if (fieldFacet == null) {
+      fieldFacet = new AnalyticsValueFacetRequest();
+      fieldFacet.expression = field;
+      grouping.facets.put(field, fieldFacet);
+    }
+    FieldFacetParamParser.applyParam(fieldFacet, paramType, params[0]);
+  }
+
+  private static void setRangeFacetParam(AnalyticsRequest request, String groupingName, String field, String paramType, String[] params) {
+    AnalyticsGroupingRequest grouping = getGrouping(request, groupingName);
+    
+    AnalyticsRangeFacetRequest rangeFacet = (AnalyticsRangeFacetRequest) grouping.facets.get(field);
+    if (rangeFacet == null) {
+      rangeFacet = new AnalyticsRangeFacetRequest();
+      rangeFacet.field = field;
+      grouping.facets.put(field, rangeFacet);
+    }
+    RangeFacetParamParser.applyParam(rangeFacet, paramType, params);
+  }
+
+  private static void setQueryFacetParam(AnalyticsRequest request, String groupingName, String facetName, String paramType, String[] params) {
+    AnalyticsGroupingRequest grouping = getGrouping(request, groupingName);
+    
+    AnalyticsQueryFacetRequest queryFacet = new AnalyticsQueryFacetRequest();
+    queryFacet.queries = new HashMap<>();
+    if (paramType.equals("query")||paramType.equals("q")) {
+      for (String param : params) {
+        queryFacet.queries.put(param, param);
+      }
+    }
+    grouping.facets.put(facetName, queryFacet);
+  }
+
+  private static void addExpression(AnalyticsRequest request, String groupingName, String expressionName, String expression) {
+    request.expressions.put(groupingName + expressionName, expression);
+    
+    getGrouping(request, groupingName).expressions.put(expressionName, expression);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/OrdinalCalculator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/OrdinalCalculator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/OrdinalCalculator.java
new file mode 100644
index 0000000..e484e7c
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/OrdinalCalculator.java
@@ -0,0 +1,173 @@
+/*
+ * 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.solr.analytics.util;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Calculates ordinals of a comparable list by placing them in the correct positions in the list.
+ * <p>
+ * Implements the QuickSelect algorithm, but modifies it to select multiple ordinals all at once.
+ */
+public class OrdinalCalculator {
+  /**
+   * Calculates a set of ordinal values for a given list of comparable objects.
+   * Once the method returns, the each ordinal is guaranteed to have the correct value in the list.
+   *
+   * @param list the list of {@link Comparable} objects
+   * @param ordinals the collection ordinals to calculate (0 to (size of list) - 1) 
+   */
+  public static <T extends Comparable<T>> void putOrdinalsInPosition(List<T> list, Collection<Integer> ordinals) {
+    int size = list.size();
+    if (size == 0) {
+      return;
+    }
+
+    int[] ords = new int[ordinals.size()];
+    int i = 0;
+    for (int ord : ordinals) {
+      ords[i++] = ord;
+    }
+    Arrays.sort(ords);
+
+    if (ords[0] < 0 || ords[ords.length - 1] > size - 1) {
+      throw new IllegalArgumentException();
+    }
+    distributeAndFind(list, ords, 0, ords.length - 1);
+  }
+
+  private static <T extends Comparable<T>> void distributeAndFind(List<T> list, int[] ordinals, int beginIdx, int endIdx) {
+    if (endIdx < beginIdx) {
+      return;
+    }
+    int middleIdxb = beginIdx;
+    int middleIdxe = beginIdx;
+    int begin = (beginIdx == 0) ? -1 : ordinals[beginIdx - 1];
+    int end = (endIdx == ordinals.length - 1) ? list.size() : ordinals[endIdx + 1];
+    double middle = (begin + end) / 2.0;
+    for (int i = beginIdx; i <= endIdx; i++) {
+      double value = Math.abs(ordinals[i] - middle) - Math.abs(ordinals[middleIdxb] - middle);
+      if (ordinals[i] == ordinals[middleIdxb]) {
+        middleIdxe = i;
+      } else if (value < 0) {
+        middleIdxb = i;
+        do {
+          middleIdxe = i;
+          i++;
+        } while (i <= endIdx && ordinals[middleIdxb] == ordinals[i]);
+        break;
+      }
+    }
+
+    int middlePlace = ordinals[middleIdxb];
+    int beginPlace = begin + 1;
+    int endPlace = end - 1;
+
+    select(list, middlePlace, beginPlace, endPlace);
+    distributeAndFind(list, ordinals, beginIdx, middleIdxb - 1);
+    distributeAndFind(list, ordinals, middleIdxe + 1, endIdx);
+  }
+
+  private static <T extends Comparable<T>> void select(List<T> list, int place, int begin, int end) {
+    T split;
+    if (end - begin < 10) {
+      split = list.get((int) (Math.random() * (end - begin + 1)) + begin);
+    } else {
+      split = split(list, begin, end);
+    }
+
+    Point result = partition(list, begin, end, split);
+
+    if (place <= result.low) {
+      select(list, place, begin, result.low);
+    } else if (place >= result.high) {
+      select(list, place, result.high, end);
+    }
+  }
+
+  private static <T extends Comparable<T>> T split(List<T> list, int begin, int end) {
+    T temp;
+    int num = (end - begin + 1);
+    int recursiveSize = (int) Math.sqrt((double) num);
+    int step = num / recursiveSize;
+    for (int i = 1; i < recursiveSize; i++) {
+      int swapFrom = i * step + begin;
+      int swapTo = i + begin;
+      temp = list.get(swapFrom);
+      list.set(swapFrom, list.get(swapTo));
+      list.set(swapTo, temp);
+    }
+    recursiveSize--;
+    select(list, recursiveSize / 2 + begin, begin, recursiveSize + begin);
+    return list.get(recursiveSize / 2 + begin);
+  }
+
+  private static <T extends Comparable<T>> Point partition(List<T> list, int begin, int end, T indexElement) {
+    T temp;
+    int left, right;
+    for (left = begin, right = end; left <= right; left++, right--) {
+      while (list.get(left).compareTo(indexElement) < 0) {
+        left++;
+      }
+      while (right != begin - 1 && list.get(right).compareTo(indexElement) >= 0) {
+        right--;
+      }
+      if (right <= left) {
+        left--;
+        right++;
+        break;
+      }
+      temp = list.get(left);
+      list.set(left, list.get(right));
+      list.set(right, temp);
+    }
+    while (left > begin - 1 && list.get(left).compareTo(indexElement) >= 0) {
+      left--;
+    }
+    while (right < end + 1 && list.get(right).compareTo(indexElement) <= 0) {
+      right++;
+    }
+    int rightMove = right + 1;
+    while (rightMove < end + 1) {
+      if (list.get(rightMove).equals(indexElement)) {
+        temp = list.get(rightMove);
+        list.set(rightMove, list.get(right));
+        list.set(right, temp);
+        do {
+          right++;
+        } while (list.get(right).equals(indexElement));
+        if (rightMove <= right) {
+          rightMove = right;
+        }
+      }
+      rightMove++;
+    }
+    return new Point(left, right);
+  }
+}
+
+class Point {
+  public int low;
+  public int high;
+
+  public Point(int low, int high) {
+    this.low = low;
+    this.high = high;
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/PercentileCalculator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/PercentileCalculator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/PercentileCalculator.java
deleted file mode 100644
index 4ae5cc0..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/PercentileCalculator.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * 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.solr.analytics.util;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-public class PercentileCalculator {
-  /**
-   * Calculates a list of percentile values for a given list of objects and percentiles.
-   *
-   * @param list     The list of {@link Comparable} objects to calculate the percentiles of.
-   * @param percents The array of percentiles (.01 to .99) to calculate.
-   * @return a list of comparables
-   */
-  public static <T extends Comparable<T>> List<T> getPercentiles(List<T> list, double[] percents) {
-    int size = list.size();
-    if (size == 0) {
-      return null;
-    }
-
-    int[] percs = new int[percents.length];
-    for (int i = 0; i < percs.length; i++) {
-      percs[i] = (int) Math.round(percents[i] * size - .5);
-    }
-    int[] percentiles = Arrays.copyOf(percs, percs.length);
-    Arrays.sort(percentiles);
-
-    if (percentiles[0] < 0 || percentiles[percentiles.length - 1] > size - 1) {
-      throw new IllegalArgumentException();
-    }
-
-    List<T> results = new ArrayList<>(percs.length);
-
-    distributeAndFind(list, percentiles, 0, percentiles.length - 1);
-
-    for (int i = 0; i < percs.length; i++) {
-      results.add(list.get(percs[i]));
-    }
-    return results;
-  }
-
-  private static <T extends Comparable<T>> void distributeAndFind(List<T> list, int[] percentiles, int beginIdx, int endIdx) {
-    if (endIdx < beginIdx) {
-      return;
-    }
-    int middleIdxb = beginIdx;
-    int middleIdxe = beginIdx;
-    int begin = (beginIdx == 0) ? -1 : percentiles[beginIdx - 1];
-    int end = (endIdx == percentiles.length - 1) ? list.size() : percentiles[endIdx + 1];
-    double middle = (begin + end) / 2.0;
-    for (int i = beginIdx; i <= endIdx; i++) {
-      double value = Math.abs(percentiles[i] - middle) - Math.abs(percentiles[middleIdxb] - middle);
-      if (percentiles[i] == percentiles[middleIdxb]) {
-        middleIdxe = i;
-      } else if (value < 0) {
-        middleIdxb = i;
-        do {
-          middleIdxe = i;
-          i++;
-        } while (i <= endIdx && percentiles[middleIdxb] == percentiles[i]);
-        break;
-      }
-    }
-
-    int middlePlace = percentiles[middleIdxb];
-    int beginPlace = begin + 1;
-    int endPlace = end - 1;
-
-    select(list, middlePlace, beginPlace, endPlace);
-    distributeAndFind(list, percentiles, beginIdx, middleIdxb - 1);
-    distributeAndFind(list, percentiles, middleIdxe + 1, endIdx);
-  }
-
-  private static <T extends Comparable<T>> void select(List<T> list, int place, int begin, int end) {
-    T split;
-    if (end - begin < 10) {
-      split = list.get((int) (Math.random() * (end - begin + 1)) + begin);
-    } else {
-      split = split(list, begin, end);
-    }
-
-    Point result = partition(list, begin, end, split);
-
-    if (place <= result.low) {
-      select(list, place, begin, result.low);
-    } else if (place >= result.high) {
-      select(list, place, result.high, end);
-    }
-  }
-
-  private static <T extends Comparable<T>> T split(List<T> list, int begin, int end) {
-    T temp;
-    int num = (end - begin + 1);
-    int recursiveSize = (int) Math.sqrt((double) num);
-    int step = num / recursiveSize;
-    for (int i = 1; i < recursiveSize; i++) {
-      int swapFrom = i * step + begin;
-      int swapTo = i + begin;
-      temp = list.get(swapFrom);
-      list.set(swapFrom, list.get(swapTo));
-      list.set(swapTo, temp);
-    }
-    recursiveSize--;
-    select(list, recursiveSize / 2 + begin, begin, recursiveSize + begin);
-    return list.get(recursiveSize / 2 + begin);
-  }
-
-  private static <T extends Comparable<T>> Point partition(List<T> list, int begin, int end, T indexElement) {
-    T temp;
-    int left, right;
-    for (left = begin, right = end; left <= right; left++, right--) {
-      while (list.get(left).compareTo(indexElement) < 0) {
-        left++;
-      }
-      while (right != begin - 1 && list.get(right).compareTo(indexElement) >= 0) {
-        right--;
-      }
-      if (right <= left) {
-        left--;
-        right++;
-        break;
-      }
-      temp = list.get(left);
-      list.set(left, list.get(right));
-      list.set(right, temp);
-    }
-    while (left > begin - 1 && list.get(left).compareTo(indexElement) >= 0) {
-      left--;
-    }
-    while (right < end + 1 && list.get(right).compareTo(indexElement) <= 0) {
-      right++;
-    }
-    int rightMove = right + 1;
-    while (rightMove < end + 1) {
-      if (list.get(rightMove).equals(indexElement)) {
-        temp = list.get(rightMove);
-        list.set(rightMove, list.get(right));
-        list.set(right, temp);
-        do {
-          right++;
-        } while (list.get(right).equals(indexElement));
-        if (rightMove <= right) {
-          rightMove = right;
-        }
-      }
-      rightMove++;
-    }
-    return new Point(left, right);
-  }
-}
-
-class Point {
-  public int low;
-  public int high;
-
-  public Point(int low, int high) {
-    this.low = low;
-    this.high = high;
-  }
-}


[11/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/UniqueFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/UniqueFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/UniqueFunction.java
new file mode 100644
index 0000000..f62f7d9
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/UniqueFunction.java
@@ -0,0 +1,101 @@
+/*
+ * 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.solr.analytics.function.reduction;
+
+import java.util.function.UnaryOperator;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.function.ReductionFunction;
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.analytics.function.reduction.data.UniqueCollector;
+import org.apache.solr.analytics.function.reduction.data.UniqueCollector.UniqueDoubleCollector;
+import org.apache.solr.analytics.function.reduction.data.UniqueCollector.UniqueFloatCollector;
+import org.apache.solr.analytics.function.reduction.data.UniqueCollector.UniqueIntCollector;
+import org.apache.solr.analytics.function.reduction.data.UniqueCollector.UniqueLongCollector;
+import org.apache.solr.analytics.function.reduction.data.UniqueCollector.UniqueStringCollector;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A reduction function which returns the number of unique values of the given expression.
+ */
+public class UniqueFunction extends AbstractLongValue implements ReductionFunction {
+  private UniqueCollector<?> collector;
+  public static final String name = "unique";
+  private final String exprStr;
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 1) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 1 paramater, " + params.length + " found.");
+    }
+    AnalyticsValueStream param = params[0];
+    UniqueCollector<?> collector;
+    if (param instanceof IntValueStream) {
+      collector = new UniqueIntCollector((IntValueStream)param);
+    } else if (param instanceof LongValueStream) {
+      collector = new UniqueLongCollector((LongValueStream)param);
+    } else if (param instanceof FloatValueStream) {
+      collector = new UniqueFloatCollector((FloatValueStream)param);
+    } else if (param instanceof DoubleValueStream) {
+      collector = new UniqueDoubleCollector((DoubleValueStream)param);
+    } else if (param instanceof StringValueStream) {
+      collector = new UniqueStringCollector((StringValueStream)param);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a comparable parameter.");
+    }
+    return new UniqueFunction(param, collector);
+  });
+  
+  public UniqueFunction(AnalyticsValueStream param, UniqueCollector<?> collector) {
+    this.collector = collector;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public long getLong() {
+    return collector.count();
+  }
+  @Override
+  public boolean exists() {
+    return true;
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (UniqueCollector<?>)sync.apply(collector);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/CountCollector.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/CountCollector.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/CountCollector.java
new file mode 100644
index 0000000..135b587
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/CountCollector.java
@@ -0,0 +1,188 @@
+/*
+ * 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.solr.analytics.function.reduction.data;
+
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.function.reduction.data.ReductionData;
+import org.apache.solr.analytics.stream.reservation.LongReservation;
+import org.apache.solr.analytics.stream.reservation.ReductionDataReservation;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+
+public abstract class CountCollector extends ReductionDataCollector<CountCollector.CountData> {
+  public static final String name = "count";
+  private final String exprStr;
+  
+  public CountCollector(String exprStr) {
+    this.exprStr = exprStr;
+  }
+
+  private long count;
+  private long docCount;
+
+  /**
+   * The number of Solr Documents for which the given analytics expression exists.
+   * 
+   * @return the count
+   */
+  public long count() {
+    return count;
+  }
+  /**
+   * The number of Solr Documents used in this reduction.
+   * 
+   * @return the number of documents
+   */
+  public long docCount() {
+    return docCount;
+  }
+
+  @Override
+  public CountData newData() {
+    CountData data = new CountData();
+    data.count = 0;
+    data.missing = 0;
+    data.docCount = 0;
+    return data;
+  }
+
+  @Override
+  public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+    // Count
+    consumer.accept(new LongReservation(
+        value -> ioData.count += value,
+        () -> ioData.count
+      ));
+    // DocCount
+    consumer.accept(new LongReservation(
+        value -> ioData.docCount += value,
+        () -> ioData.docCount
+      ));
+  }
+
+  @Override
+  public void setMergedData(ReductionData data) {
+    count = ((CountData)data).count;
+    docCount = ((CountData)data).docCount;
+  }
+
+  @Override
+  public void setData(ReductionData data) {
+    count = ((CountData)data).count;
+    docCount = ((CountData)data).docCount;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  
+  public static class CountData extends ReductionData {
+    long count;
+    long missing;
+    long docCount;
+  }
+
+  /**
+   * Represents a {@code count(expr)} expression. This collects 3 values:
+   * 
+   * docCount - The number of Solr Documents for which the wrapped expression exists.
+   * count - The number of values which wrapped expression contains.
+   * missing - The number of Solr Documents for which the wrapped expression does not exist.
+   */
+  public static class ExpressionCountCollector extends CountCollector {
+    private final AnalyticsValueStream param;
+    
+    public ExpressionCountCollector(AnalyticsValueStream param) {
+      super(AnalyticsValueStream.createExpressionString(name, param));
+      this.param = param;
+    }
+    
+    private long missing;
+    
+    /**
+     * The number of Solr Documents for which the given analytics expression does not exist.
+     * 
+     * @return the number of missing values
+     */
+    public long missing() {
+      return missing;
+    }
+
+    @Override
+    public void setMergedData(ReductionData data) {
+      super.setMergedData(data);
+      missing = ((CountData)data).missing;
+    }
+
+    @Override
+    public void setData(ReductionData data) {
+      super.setData(data);
+      missing = ((CountData)data).missing;
+    }
+    
+    long tempCount;
+    int tempMissing;
+    int tempDocCount;
+    @Override
+    public void collect() {
+      tempCount = 0;
+      param.streamObjects( obj -> {
+        ++tempCount;
+      });
+      tempMissing = tempCount == 0 ? 1 : 0;
+      tempDocCount = tempCount > 0 ? 1 : 0;
+    }
+    
+    @Override
+    protected void apply(CountData data) {
+      data.count += tempCount;
+      data.missing += tempMissing;
+      data.docCount += tempDocCount;
+    }
+
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      super.submitReservations(consumer);
+      // Missing
+      consumer.accept(new LongReservation(
+          value -> ioData.missing += value,
+          () -> ioData.missing
+        ));
+    }
+  }
+  
+  /**
+   * Represents a {@code count()} expression. This collects the number of Solr Documents used in a result set.
+   */
+  public static class TotalCountCollector extends CountCollector {
+    
+    public TotalCountCollector() {
+      super(AnalyticsValueStream.createExpressionString(name));
+    }
+    
+    @Override
+    protected void apply(CountData data) {
+      data.count += 1;
+      data.docCount += 1;
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/MaxCollector.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/MaxCollector.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/MaxCollector.java
new file mode 100644
index 0000000..bf49907
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/MaxCollector.java
@@ -0,0 +1,476 @@
+/*
+ * 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.solr.analytics.function.reduction.data;
+
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.function.reduction.data.ReductionData;
+import org.apache.solr.analytics.stream.reservation.DoubleCheckedReservation;
+import org.apache.solr.analytics.stream.reservation.FloatCheckedReservation;
+import org.apache.solr.analytics.stream.reservation.IntCheckedReservation;
+import org.apache.solr.analytics.stream.reservation.LongCheckedReservation;
+import org.apache.solr.analytics.stream.reservation.ReductionDataReservation;
+import org.apache.solr.analytics.stream.reservation.StringCheckedReservation;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValueStream;
+
+/**
+ * Collector of max values.
+ * <p>
+ * Supported types are:
+ * <ul>
+ * <li>Int
+ * <li>Long
+ * <li>Float
+ * <li>Double
+ * <li>Date (through longs)
+ * <li>String
+ * </ul>
+ *
+ * @param <T> The type of data being processed.
+ */
+public abstract class MaxCollector<T extends ReductionData> extends ReductionDataCollector<T> {
+  public static final String name = "max";
+  private final String exprStr;
+  
+  protected MaxCollector(AnalyticsValueStream param) {
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+  
+  private boolean exists;
+  
+  /**
+   * Returns true if any of the values being reduce exist, and false if none of them do.
+   * 
+   * @return whether a max value exists
+   */
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public void setMergedData(ReductionData data) {
+    exists = data.exists;
+  }
+
+  @Override
+  public void setData(ReductionData data) {
+    exists = data.exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  public static class IntMaxCollector extends MaxCollector<IntMaxCollector.MaxData> {
+    private IntValueStream param;
+    
+    public IntMaxCollector(IntValueStream param) {
+      super(param);
+      this.param = param;
+    }
+
+    @Override
+    public MaxData newData() {
+      MaxData data = new MaxData();
+      data.exists = false;
+      return data;
+    }
+
+    int max;
+    
+    /**
+     * Returns the max value of the set data.
+     * 
+     * @return the max
+     */
+    public int max() {
+      return max;
+    }
+
+    int tempMax;
+    boolean tempExists;
+    @Override
+    public void collect() {
+      tempExists = false;
+      param.streamInts( val -> {
+        if (!tempExists || val > tempMax) {
+          tempMax = val;
+          tempExists = true;
+        }
+      });
+    }
+    @Override
+    protected void apply(MaxData data) {
+      if (tempExists && (!data.exists || tempMax > data.val)) {
+        data.val = tempMax;
+        data.exists = true;
+      }
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new IntCheckedReservation(
+          value -> {
+            if (!ioData.exists || value > ioData.val) {
+              ioData.val = value;
+              ioData.exists = true;
+            }
+          },
+          ()-> ioData.val,
+          ()-> ioData.exists
+        ));
+    }
+
+    @Override
+    public void setMergedData(ReductionData data) {
+      super.setData(data);
+      max = ((MaxData)data).val;
+    }
+
+    @Override
+    public void setData(ReductionData data) {
+      super.setData(data);
+      max = ((MaxData)data).val;
+    }
+    
+    public static class MaxData extends ReductionData {
+      int val;
+    }
+  }
+  
+
+
+  public static class LongMaxCollector extends MaxCollector<LongMaxCollector.MaxData> {
+    private LongValueStream param;
+    
+    public LongMaxCollector(LongValueStream param) {
+      super(param);
+      this.param = param;
+    }
+
+    @Override
+    public MaxData newData() {
+      MaxData data = new MaxData();
+      data.exists = false;
+      return data;
+    }
+
+    long max;
+    
+    /**
+     * Returns the max value of the set data.
+     * 
+     * @return the max
+     */
+    public long max() {
+      return max;
+    }
+
+    long tempMax;
+    boolean tempExists;
+    @Override
+    public void collect() {
+      tempExists = false;
+      param.streamLongs( val -> {
+        if (!tempExists || val > tempMax) {
+          tempMax = val;
+          tempExists = true;
+        }
+      });
+    }
+    @Override
+    protected void apply(MaxData data) {
+      if (tempExists && (!data.exists || tempMax > data.val)) {
+        data.val = tempMax;
+        data.exists = true;
+      }
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new LongCheckedReservation(
+          value -> {
+            if (!ioData.exists || value > ioData.val) {
+              ioData.val = value;
+              ioData.exists = true;
+            }
+          },
+          ()-> ioData.val,
+          ()-> ioData.exists
+        ));
+    }
+
+    @Override
+    public void setMergedData(ReductionData data) {
+      super.setData(data);
+      max = ((MaxData)data).val;
+    }
+
+    @Override
+    public void setData(ReductionData data) {
+      super.setData(data);
+      max = ((MaxData)data).val;
+    }
+    
+    public static class MaxData extends ReductionData {
+      long val;
+    }
+  }
+
+  public static class FloatMaxCollector extends MaxCollector<FloatMaxCollector.MaxData> {
+    private FloatValueStream param;
+    
+    public FloatMaxCollector(FloatValueStream param) {
+      super(param);
+      this.param = param;
+    }
+
+    @Override
+    public MaxData newData() {
+      MaxData data = new MaxData();
+      data.exists = false;
+      return data;
+    }
+
+    float max;
+    
+    /**
+     * Returns the max value of the set data.
+     * 
+     * @return the max
+     */
+    public float max() {
+      return max;
+    }
+
+    float tempMax;
+    boolean tempExists;
+    @Override
+    public void collect() {
+      tempExists = false;
+      param.streamFloats( val -> {
+        if (!tempExists || val > tempMax) {
+          tempMax = val;
+          tempExists = true;
+        }
+      });
+    }
+    @Override
+    protected void apply(MaxData data) {
+      if (tempExists && (!data.exists || tempMax > data.val)) {
+        data.val = tempMax;
+        data.exists = true;
+      }
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new FloatCheckedReservation(
+          value -> {
+            if (!ioData.exists || value > ioData.val) {
+              ioData.val = value;
+              ioData.exists = true;
+            }
+          },
+          ()-> ioData.val,
+          ()-> ioData.exists
+        ));
+    }
+
+    @Override
+    public void setMergedData(ReductionData data) {
+      super.setData(data);
+      max = ((MaxData)data).val;
+    }
+
+    @Override
+    public void setData(ReductionData data) {
+      super.setData(data);
+      max = ((MaxData)data).val;
+    }
+    
+    public static class MaxData extends ReductionData {
+      float val;
+    }
+  }
+
+  public static class DoubleMaxCollector extends MaxCollector<DoubleMaxCollector.MaxData> {
+    private DoubleValueStream param;
+    
+    public DoubleMaxCollector(DoubleValueStream param) {
+      super(param);
+      this.param = param;
+    }
+
+    @Override
+    public MaxData newData() {
+      MaxData data = new MaxData();
+      data.exists = false;
+      return data;
+    }
+
+    double max;
+    
+    /**
+     * Returns the max value of the set data.
+     * 
+     * @return the max
+     */
+    public double max() {
+      return max;
+    }
+
+    double tempMax;
+    boolean tempExists;
+    @Override
+    public void collect() {
+      tempExists = false;
+      param.streamDoubles( val -> {
+        if (!tempExists || val > tempMax) {
+          tempMax = val;
+          tempExists = true;
+        }
+      });
+    }
+    @Override
+    protected void apply(MaxData data) {
+      if (tempExists && (!data.exists || tempMax > data.val)) {
+        data.val = tempMax;
+        data.exists = true;
+      }
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new DoubleCheckedReservation(
+          value -> {
+            if (!ioData.exists || value > ioData.val) {
+              ioData.val = value;
+              ioData.exists = true;
+            }
+          },
+          ()-> ioData.val,
+          ()-> ioData.exists
+        ));
+    }
+
+    @Override
+    public void setMergedData(ReductionData data) {
+      super.setData(data);
+      max = ((MaxData)data).val;
+    }
+
+    @Override
+    public void setData(ReductionData data) {
+      super.setData(data);
+      max = ((MaxData)data).val;
+    }
+    
+    public static class MaxData extends ReductionData {
+      double val;
+    }
+  }
+  
+
+
+  public static class StringMaxCollector extends MaxCollector<StringMaxCollector.MaxData> {
+    private StringValueStream param;
+    
+    public StringMaxCollector(StringValueStream param) {
+      super(param);
+      this.param = param;
+    }
+
+    @Override
+    public MaxData newData() {
+      MaxData data = new MaxData();
+      data.exists = false;
+      return data;
+    }
+
+    String max;
+    
+    /**
+     * Returns the max value of the set data.
+     * 
+     * @return the max
+     */
+    public String max() {
+      return max;
+    }
+
+    String tempMax;
+    boolean tempExists;
+    @Override
+    public void collect() {
+      tempExists = false;
+      param.streamStrings( val -> {
+        if (!tempExists || val.compareTo(tempMax) > 0) {
+          tempMax = val;
+          tempExists = true;
+        }
+      });
+    }
+    @Override
+    protected void apply(MaxData data) {
+      if (tempExists && (!data.exists || tempMax.compareTo(data.val) > 0)) {
+        data.val = tempMax;
+        data.exists = true;
+      }
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new StringCheckedReservation(
+          value -> {
+            if (!ioData.exists || value.compareTo(ioData.val) > 0) {
+              ioData.val = value;
+              ioData.exists = true;
+            }
+          },
+          ()-> ioData.val,
+          ()-> ioData.exists
+        ));
+    }
+
+    @Override
+    public void setMergedData(ReductionData data) {
+      super.setData(data);
+      max = ((MaxData)data).val;
+    }
+
+    @Override
+    public void setData(ReductionData data) {
+      super.setData(data);
+      max = ((MaxData)data).val;
+    }
+    
+    public static class MaxData extends ReductionData {
+      String val;
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/MinCollector.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/MinCollector.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/MinCollector.java
new file mode 100644
index 0000000..3a33660
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/MinCollector.java
@@ -0,0 +1,476 @@
+/*
+ * 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.solr.analytics.function.reduction.data;
+
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.function.reduction.data.ReductionData;
+import org.apache.solr.analytics.stream.reservation.DoubleCheckedReservation;
+import org.apache.solr.analytics.stream.reservation.FloatCheckedReservation;
+import org.apache.solr.analytics.stream.reservation.IntCheckedReservation;
+import org.apache.solr.analytics.stream.reservation.LongCheckedReservation;
+import org.apache.solr.analytics.stream.reservation.ReductionDataReservation;
+import org.apache.solr.analytics.stream.reservation.StringCheckedReservation;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValueStream;
+
+/**
+ * Collector of min values.
+ * <p>
+ * Supported types are:
+ * <ul>
+ * <li>Int
+ * <li>Long
+ * <li>Float
+ * <li>Double
+ * <li>Date (through longs)
+ * <li>String
+ * </ul>
+ *
+ * @param <T> The type of data being processed.
+ */
+public abstract class MinCollector<T extends ReductionData> extends ReductionDataCollector<T> {
+  public static final String name = "min";
+  private final String exprStr;
+  
+  protected MinCollector(AnalyticsValueStream param) {
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+  
+  private boolean exists;
+  
+  /**
+   * Returns true if any of the values being reduce exist, and false if none of them do.
+   * 
+   * @return whether a min value exists
+   */
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public void setMergedData(ReductionData data) {
+    exists = data.exists;
+  }
+
+  @Override
+  public void setData(ReductionData data) {
+    exists = data.exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  public static class IntMinCollector extends MinCollector<IntMinCollector.MinData> {
+    private IntValueStream param;
+    
+    public IntMinCollector(IntValueStream param) {
+      super(param);
+      this.param = param;
+    }
+
+    @Override
+    public MinData newData() {
+      MinData data = new MinData();
+      data.exists = false;
+      return data;
+    }
+
+    int min;
+    
+    /**
+     * Returns the min value of the set data.
+     * 
+     * @return the min
+     */
+    public int min() {
+      return min;
+    }
+
+    int tempMin;
+    boolean tempExists;
+    @Override
+    public void collect() {
+      tempExists = false;
+      param.streamInts( val -> {
+        if (!tempExists || val < tempMin) {
+          tempMin = val;
+          tempExists = true;
+        }
+      });
+    }
+    @Override
+    protected void apply(MinData data) {
+      if (tempExists && (!data.exists || tempMin < data.val)) {
+        data.val = tempMin;
+        data.exists = true;
+      }
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new IntCheckedReservation(
+          value -> {
+            if (!ioData.exists || value < ioData.val) {
+              ioData.val = value;
+              ioData.exists = true;
+            }
+          },
+          ()-> ioData.val,
+          ()-> ioData.exists
+        ));
+    }
+
+    @Override
+    public void setMergedData(ReductionData data) {
+      super.setData(data);
+      min = ((MinData)data).val;
+    }
+
+    @Override
+    public void setData(ReductionData data) {
+      super.setData(data);
+      min = ((MinData)data).val;
+    }
+    
+    public static class MinData extends ReductionData {
+      int val;
+    }
+  }
+  
+
+
+  public static class LongMinCollector extends MinCollector<LongMinCollector.MinData> {
+    private LongValueStream param;
+    
+    public LongMinCollector(LongValueStream param) {
+      super(param);
+      this.param = param;
+    }
+
+    @Override
+    public MinData newData() {
+      MinData data = new MinData();
+      data.exists = false;
+      return data;
+    }
+
+    long min;
+    
+    /**
+     * Returns the min value of the set data.
+     * 
+     * @return the min
+     */
+    public long min() {
+      return min;
+    }
+
+    long tempMin;
+    boolean tempExists;
+    @Override
+    public void collect() {
+      tempExists = false;
+      param.streamLongs( val -> {
+        if (!tempExists || val < tempMin) {
+          tempMin = val;
+          tempExists = true;
+        }
+      });
+    }
+    @Override
+    protected void apply(MinData data) {
+      if (tempExists && (!data.exists || tempMin < data.val)) {
+        data.val = tempMin;
+        data.exists = true;
+      }
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new LongCheckedReservation(
+          value -> {
+            if (!ioData.exists || value < ioData.val) {
+              ioData.val = value;
+              ioData.exists = true;
+            }
+          },
+          ()-> ioData.val,
+          ()-> ioData.exists
+        ));
+    }
+
+    @Override
+    public void setMergedData(ReductionData data) {
+      super.setData(data);
+      min = ((MinData)data).val;
+    }
+
+    @Override
+    public void setData(ReductionData data) {
+      super.setData(data);
+      min = ((MinData)data).val;
+    }
+    
+    public static class MinData extends ReductionData {
+      long val;
+    }
+  }
+
+  public static class FloatMinCollector extends MinCollector<FloatMinCollector.MinData> {
+    private FloatValueStream param;
+    
+    public FloatMinCollector(FloatValueStream param) {
+      super(param);
+      this.param = param;
+    }
+
+    @Override
+    public MinData newData() {
+      MinData data = new MinData();
+      data.exists = false;
+      return data;
+    }
+
+    float min;
+    
+    /**
+     * Returns the min value of the set data.
+     * 
+     * @return the min
+     */
+    public float min() {
+      return min;
+    }
+
+    float tempMin;
+    boolean tempExists;
+    @Override
+    public void collect() {
+      tempExists = false;
+      param.streamFloats( val -> {
+        if (!tempExists || val < tempMin) {
+          tempMin = val;
+          tempExists = true;
+        }
+      });
+    }
+    @Override
+    protected void apply(MinData data) {
+      if (tempExists && (!data.exists || tempMin < data.val)) {
+        data.val = tempMin;
+        data.exists = true;
+      }
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new FloatCheckedReservation(
+          value -> {
+            if (!ioData.exists || value < ioData.val) {
+              ioData.val = value;
+              ioData.exists = true;
+            }
+          },
+          ()-> ioData.val,
+          ()-> ioData.exists
+        ));
+    }
+
+    @Override
+    public void setMergedData(ReductionData data) {
+      super.setData(data);
+      min = ((MinData)data).val;
+    }
+
+    @Override
+    public void setData(ReductionData data) {
+      super.setData(data);
+      min = ((MinData)data).val;
+    }
+    
+    public static class MinData extends ReductionData {
+      float val;
+    }
+  }
+
+  public static class DoubleMinCollector extends MinCollector<DoubleMinCollector.MinData> {
+    private DoubleValueStream param;
+    
+    public DoubleMinCollector(DoubleValueStream param) {
+      super(param);
+      this.param = param;
+    }
+
+    @Override
+    public MinData newData() {
+      MinData data = new MinData();
+      data.exists = false;
+      return data;
+    }
+
+    double min;
+    
+    /**
+     * Returns the min value of the set data.
+     * 
+     * @return the min
+     */
+    public double min() {
+      return min;
+    }
+
+    double tempMin;
+    boolean tempExists;
+    @Override
+    public void collect() {
+      tempExists = false;
+      param.streamDoubles( val -> {
+        if (!tempExists || val < tempMin) {
+          tempMin = val;
+          tempExists = true;
+        }
+      });
+    }
+    @Override
+    protected void apply(MinData data) {
+      if (tempExists && (!data.exists || tempMin < data.val)) {
+        data.val = tempMin;
+        data.exists = true;
+      }
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new DoubleCheckedReservation(
+          value -> {
+            if (!ioData.exists || value < ioData.val) {
+              ioData.val = value;
+              ioData.exists = true;
+            }
+          },
+          ()-> ioData.val,
+          ()-> ioData.exists
+        ));
+    }
+
+    @Override
+    public void setMergedData(ReductionData data) {
+      super.setData(data);
+      min = ((MinData)data).val;
+    }
+
+    @Override
+    public void setData(ReductionData data) {
+      super.setData(data);
+      min = ((MinData)data).val;
+    }
+    
+    public static class MinData extends ReductionData {
+      double val;
+    }
+  }
+  
+
+
+  public static class StringMinCollector extends MinCollector<StringMinCollector.MinData> {
+    private StringValueStream param;
+    
+    public StringMinCollector(StringValueStream param) {
+      super(param);
+      this.param = param;
+    }
+
+    @Override
+    public MinData newData() {
+      MinData data = new MinData();
+      data.exists = false;
+      return data;
+    }
+
+    String min;
+    
+    /**
+     * Returns the min value of the set data.
+     * 
+     * @return the min
+     */
+    public String min() {
+      return min;
+    }
+
+    String tempMin;
+    boolean tempExists;
+    @Override
+    public void collect() {
+      tempExists = false;
+      param.streamStrings( val -> {
+        if (!tempExists || val.compareTo(tempMin) < 0) {
+          tempMin = val;
+          tempExists = true;
+        }
+      });
+    }
+    @Override
+    protected void apply(MinData data) {
+      if (tempExists && (!data.exists || tempMin.compareTo(data.val) < 0)) {
+        data.val = tempMin;
+        data.exists = true;
+      }
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new StringCheckedReservation(
+          value -> {
+            if (!ioData.exists || value.compareTo(ioData.val) < 0) {
+              ioData.val = value;
+              ioData.exists = true;
+            }
+          },
+          ()-> ioData.val,
+          ()-> ioData.exists
+        ));
+    }
+
+    @Override
+    public void setMergedData(ReductionData data) {
+      super.setData(data);
+      min = ((MinData)data).val;
+    }
+
+    @Override
+    public void setData(ReductionData data) {
+      super.setData(data);
+      min = ((MinData)data).val;
+    }
+    
+    public static class MinData extends ReductionData {
+      String val;
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/ReductionData.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/ReductionData.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/ReductionData.java
new file mode 100644
index 0000000..8c265fd
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/ReductionData.java
@@ -0,0 +1,24 @@
+/*
+ * 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.solr.analytics.function.reduction.data;
+
+/**
+ * Base class to store data for {@link ReductionDataCollector}s
+ */
+public class ReductionData {
+  public boolean exists;
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/ReductionDataCollector.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/ReductionDataCollector.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/ReductionDataCollector.java
new file mode 100644
index 0000000..9674d98
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/ReductionDataCollector.java
@@ -0,0 +1,183 @@
+/*
+ * 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.solr.analytics.function.reduction.data;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.function.ReductionFunction;
+import org.apache.solr.analytics.stream.reservation.ReductionDataReservation;
+import org.apache.solr.analytics.value.AnalyticsValue;
+
+/**
+ * Manager of a specific instance of {@link ReductionData} collection. 
+ * 
+ * @param <T> the type of reduction data being collected
+ */
+public abstract class ReductionDataCollector<T extends ReductionData> {
+
+  protected ArrayList<T> lastingTargets;
+  protected ArrayList<T> collectionTargets;
+  protected T ioData;
+  
+  protected ReductionDataCollector() {
+    lastingTargets = new ArrayList<>();
+    collectionTargets = new ArrayList<>();
+  }
+  
+  /**
+   * Submits the data reservations needed for this data collector.
+   * 
+   * @param consumer the consumer which the reservations are submitted to
+   */
+  public abstract void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer);
+
+  /**
+   * A clean slate to start a new reduction.
+   * 
+   * @return the new reduction data
+   */
+  public abstract T newData();
+  
+  /**
+   * Add a reduction data to target during collection.
+   * The given target is valid until the lasting targets are cleared.
+   * 
+   * @param data the data to target
+   */
+  @SuppressWarnings("unchecked")
+  public void addLastingCollectTarget(ReductionData data) {
+    lastingTargets.add((T) data);
+  }
+  
+  /**
+   * Clear the lasting collection targets. After this is called the current lasting
+   * targets will not be affected by future {@link #collectAndApply()} calls. 
+   */
+  public void clearLastingCollectTargets() {
+    lastingTargets.clear();
+  }
+  
+  /**
+   * Create a new reduction data to target during collection.
+   * The given target is only valid for one call to {@link #collectAndApply()}.
+   * 
+   * @return the reduction data created
+   */
+  public T newDataTarget() {
+    T data = newData();
+    collectionTargets.add(data);
+    return data;
+  }
+  
+  /**
+   * Add a reduction data to target during collection.
+   * The given target is only valid for one call to {@link #collectAndApply()}.
+   * 
+   * @param data the data to target
+   */
+  @SuppressWarnings("unchecked")
+  public void addCollectTarget(ReductionData data) {
+    collectionTargets.add((T)data);
+  }
+
+  /**
+   * Collect the info for the current Solr Document and apply the results to the
+   * given collection targets.
+   * 
+   * After application, all non-lasting targets are removed.
+   */
+  public void collectAndApply() {
+    collect();
+    lastingTargets.forEach( target -> apply(target) );
+    collectionTargets.forEach( target -> apply(target) );
+    collectionTargets.clear();
+  }
+  
+  /**
+   * Collect the information from current Solr Document.
+   */
+  protected void collect() { }
+  
+  /**
+   * Apply the collected info to the given reduction data.
+   * Should always be called after a {@link #collect()} call.
+   * 
+   * @param data reduction data to apply collected info to
+   */
+  protected abstract void apply(T data);
+  
+  /**
+   * Create a new reduction data to use in exporting and merging.
+   * 
+   * @return the created reduction data
+   */
+  public T newDataIO() {
+    ioData = newData();
+    return ioData;
+  }
+  
+  /**
+   * Set the reduction data to use in exporting and merging.
+   * 
+   * @param data the data to use
+   */
+  @SuppressWarnings("unchecked")
+  public void dataIO(ReductionData data) {
+    ioData = (T)data;
+  }
+
+  /**
+   * Finalize the reduction with the merged data stored in the parameter.
+   * Once the reduction is finalized, the {@link ReductionFunction}s that use this 
+   * data collector act like regular {@link AnalyticsValue} classes that 
+   * can be accessed through their {@code get<value-type>} methods.
+   * 
+   * (FOR CLOUD)
+   * 
+   * @param data the merged data to compute a reduction for
+   */
+  public abstract void setMergedData(ReductionData data);
+
+  /**
+   * Finalize the reduction with the collected data stored in the parameter.
+   * Once the reduction is finalized, the {@link ReductionFunction}s that use this 
+   * data collector act like regular {@link AnalyticsValue} classes that 
+   * can be accessed through their {@code get<value-type>} methods.
+   * 
+   * (FOR SINGLE-SHARD)
+   * 
+   * @param data the collected data to compute a reduction for
+   */
+  public abstract void setData(ReductionData data);
+  
+  /**
+   * Get the name of the reduction data collector. This is the same across all instances of the data collector.
+   * 
+   * @return the name
+   */
+  public abstract String getName();
+  
+  /**
+   * The unique expression string of the reduction data collector, given all inputs and parameters.
+   * Used during {@link ReductionDataCollector} syncing. Since the string should be unique,
+   * only one of expression is kept.
+   * 
+   * @return the expression string 
+   */
+  public abstract String getExpressionStr();
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/SortedListCollector.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/SortedListCollector.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/SortedListCollector.java
new file mode 100644
index 0000000..c0de54b
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/SortedListCollector.java
@@ -0,0 +1,363 @@
+/*
+ * 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.solr.analytics.function.reduction.data;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.function.reduction.data.ReductionData;
+import org.apache.solr.analytics.stream.reservation.DoubleArrayReservation;
+import org.apache.solr.analytics.stream.reservation.FloatArrayReservation;
+import org.apache.solr.analytics.stream.reservation.IntArrayReservation;
+import org.apache.solr.analytics.stream.reservation.LongArrayReservation;
+import org.apache.solr.analytics.stream.reservation.ReductionDataReservation;
+import org.apache.solr.analytics.stream.reservation.StringArrayReservation;
+import org.apache.solr.analytics.util.OrdinalCalculator;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValueStream;
+
+/**
+ * Collector of sorted lists.
+ * 
+ * Once the sorted list has been collected, it can be reduced by calculating a median, percentiles, or ordinals.
+ * All of the above reductions over the same data share one {@link SortedListCollector}.
+ * <p>
+ * Supported types are:
+ * <ul>
+ * <li>Int
+ * <li>Long
+ * <li>Float
+ * <li>Double
+ * <li>Date (through longs)
+ * <li>String
+ * </ul>
+ *
+ * @param <T> The type of data being processed.
+ */
+public abstract class SortedListCollector<T extends Comparable<T>> extends ReductionDataCollector<SortedListCollector.SortedListData<T>> {
+  public static final String name = "sorted";
+  private final String exprStr;
+  
+  protected SortedListCollector(AnalyticsValueStream param, String specificationName) {
+    this.exprStr = AnalyticsValueStream.createExpressionString(name + "_" + specificationName,param);
+    
+    tempList = new ArrayList<>();
+    
+    calcMedian = false;
+    calcPercs = new HashSet<>();
+    calcOrds = new HashSet<>();
+    calcRevOrds = new HashSet<>();
+  }
+  
+  private List<T> list;
+  
+  private boolean calcMedian;
+  private Set<Double> calcPercs;
+  private Set<Integer> calcOrds;
+  private Set<Integer> calcRevOrds;
+
+  public int size() {
+    return list.size();
+  }
+
+  /**
+   * Informs the collector that the median needs to be computed.
+   */
+  public void calcMedian() {
+    calcMedian = true;
+  }
+  
+  /**
+   * Informs the collector that the following percentile needs to be computed.
+   * 
+   * @param percentile requested percentile
+   */
+  public void calcPercentile(double percentile) {
+    calcPercs.add(percentile);
+  }
+
+  /**
+   * Informs the collector that the following ordinal needs to be computed.
+   * 
+   * @param ordinal requested ordinal
+   */
+  public void calcOrdinal(int ordinal) {
+    calcOrds.add(ordinal);
+  }
+
+  /**
+   * Informs the collector that the following reverse ordinal needs to be computed.
+   * Reverse ordinals are ordinals that start at the end of the list.
+   * 
+   * @param reverseOrdinal requested reverseOrdinal
+   */
+  public void calcReverseOrdinal(int reverseOrdinal) {
+    calcRevOrds.add(reverseOrdinal);
+  }
+
+  /**
+   * Once the data has been set by either {@link #setData} or {@link #setMergedData},
+   * this returns the value at the given sorted index. 
+   * 
+   * Only the indices specified by {@link #calcMedian}, {@link #calcPercentile}, {@link #calcOrdinal}, and {@link #calcReverseOrdinal}
+   * will contain valid data. All other indices may return unsorted data.
+   * 
+   * @param index the index of the sorted data to return
+   */
+  public T get(int index) {
+    return list.get(index);
+  }
+
+  @Override
+  public SortedListData<T> newData() {
+    SortedListData<T> data = new SortedListData<>();
+    data.list = new ArrayList<T>();
+    data.exists = false;
+    return data;
+  }
+  
+  ArrayList<T> tempList; 
+  @Override
+  protected void apply(SortedListData<T> data) {
+    data.list.addAll(tempList);
+  }
+  
+  /**
+   * Starts the import of the shard data.
+   * 
+   * @param size the size of the incoming shard list
+   */
+  protected void startImport(int size) {
+    ioData.list.ensureCapacity(ioData.list.size() + size);
+  }
+  
+  /**
+   * Merges the current list with the incoming value.
+   * 
+   * @param value the next imported value to add
+   */
+  protected void importNext(T value) {
+    ioData.list.add(value);
+  }
+  
+  Iterator<T> iter;
+  /**
+   * The list to be exported is unsorted.
+   * The lists of all shards will be combined with the {@link #startImport} and {@link #importNext} methods.
+   * 
+   * @return the size of the list being exported.
+   */
+  public int startExport() {
+    iter = ioData.list.iterator();
+    return ioData.list.size();
+  }
+  /**
+   * Return the next value in the list.
+   * 
+   * @return the next sorted value
+   */
+  public T exportNext() {
+    return iter.next();
+  }
+
+  /**
+   * Put the given indices in their sorted positions
+   */
+  @Override
+  public void setMergedData(ReductionData data) {
+    setData(data);
+  }
+
+  /**
+   * This is where the given indices are put in their sorted positions.
+   * 
+   * Only the given indices are guaranteed to be in sorted order.
+   */
+  @SuppressWarnings("unchecked")
+  @Override
+  public void setData(ReductionData data) {
+    list = ((SortedListData<T>)data).list;
+    int size = list.size();
+    if (size <= 1) {
+      return;
+    }
+    
+    // Ordinals start at 0 and end at size-1
+    Set<Integer> ordinals = new HashSet<>();
+    for (double percentile : calcPercs) {
+      ordinals.add((int) Math.ceil(percentile * size) - 1);
+    }
+    ordinals.addAll(calcOrds);
+    for (int reverseOrdinal : calcRevOrds) {
+      ordinals.add(size + reverseOrdinal);
+    }
+    if (calcMedian) {
+      int mid = list.size() / 2;
+      ordinals.add(mid);
+      if (list.size() % 2 == 0) {
+        ordinals.add(mid - 1);
+      }
+    }
+    OrdinalCalculator.putOrdinalsInPosition(list, ordinals);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  
+  public static class SortedListData<D extends Comparable<D>> extends ReductionData {
+    ArrayList<D> list;
+  }
+
+  public static class SortedIntListCollector extends SortedListCollector<Integer> {
+    private IntValueStream param;
+    
+    public SortedIntListCollector(IntValueStream param) {
+      super(param, "int");
+      this.param = param;
+    }
+
+    @Override
+    public void collect() {
+      tempList.clear();
+      param.streamInts( val -> tempList.add(val) );
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new IntArrayReservation(
+          value -> importNext(value),
+          importSize -> startImport(importSize),
+          () -> exportNext(),
+          () -> startExport()
+        ));
+    }
+  }
+  
+  public static class SortedLongListCollector extends SortedListCollector<Long> {
+    private LongValueStream param;
+    
+    public SortedLongListCollector(LongValueStream param) {
+      super(param, "long");
+      this.param = param;
+    }
+
+    @Override
+    public void collect() {
+      tempList.clear();
+      param.streamLongs( val -> tempList.add(val) );
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new LongArrayReservation(
+          value -> importNext(value),
+          importSize -> startImport(importSize),
+          () -> exportNext(),
+          () -> startExport()
+        ));
+    }
+  }
+  
+  public static class SortedFloatListCollector extends SortedListCollector<Float> {
+    private FloatValueStream param;
+    
+    public SortedFloatListCollector(FloatValueStream param) {
+      super(param, "float");
+      this.param = param;
+    }
+
+    @Override
+    public void collect() {
+      tempList.clear();
+      param.streamFloats( val -> tempList.add(val) );
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new FloatArrayReservation(
+          value -> importNext(value),
+          importSize -> startImport(importSize),
+          () -> exportNext(),
+          () -> startExport()
+        ));
+    }
+  }
+  
+  public static class SortedDoubleListCollector extends SortedListCollector<Double> {
+    private DoubleValueStream param;
+    
+    public SortedDoubleListCollector(DoubleValueStream param) {
+      super(param, "double");
+      this.param = param;
+    }
+
+    @Override
+    public void collect() {
+      tempList.clear();
+      param.streamDoubles( val -> tempList.add(val) );
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new DoubleArrayReservation(
+          value -> importNext(value),
+          importSize -> startImport(importSize),
+          () -> exportNext(),
+          () -> startExport()
+        ));
+    }
+  }
+  
+  public static class SortedStringListCollector extends SortedListCollector<String> {
+    private StringValueStream param;
+    
+    public SortedStringListCollector(StringValueStream param) {
+      super(param, "string");
+      this.param = param;
+    }
+
+    @Override
+    public void collect() {
+      tempList.clear();
+      param.streamStrings( val -> tempList.add(val) );
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new StringArrayReservation(
+          value -> importNext(value),
+          importSize -> startImport(importSize),
+          () -> exportNext(),
+          () -> startExport()
+        ));
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/SumCollector.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/SumCollector.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/SumCollector.java
new file mode 100644
index 0000000..6f4fc18
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/SumCollector.java
@@ -0,0 +1,124 @@
+/*
+ * 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.solr.analytics.function.reduction.data;
+
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.function.reduction.data.ReductionData;
+import org.apache.solr.analytics.stream.reservation.DoubleCheckedReservation;
+import org.apache.solr.analytics.stream.reservation.ReductionDataReservation;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValueStream;
+
+/**
+ * Collects the sum of the given {@link DoubleValueStream} parameter. 
+ */
+public class SumCollector extends ReductionDataCollector<SumCollector.SumData> {
+  private final DoubleValueStream param;
+  public static final String name = "sum";
+  private final String exprStr;
+  
+  public SumCollector(DoubleValueStream param) {
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+  
+  private double sum;
+  private boolean exists;
+
+  /**
+   * Return the sum of the set data
+   * 
+   * @return the sum
+   */
+  public double sum() {
+    return sum;
+  }
+  
+  /**
+   * Return whether a sum exists.
+   * A sum will always exist if there is at least one existing value for the parameter, 
+   * otherwise the sum does not exist.
+   * 
+   * @return whether a sum exists
+   */
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public SumData newData() {
+    SumData data = new SumData();
+    data.sum = 0;
+    data.exists = false;
+    return data;
+  }
+
+  double tempSum;
+  boolean tempExists;
+  @Override
+  public void collect() {
+    tempSum = 0;
+    tempExists = false;
+    param.streamDoubles( val -> {
+      tempSum += val;
+      tempExists = true;
+    });
+  }
+  @Override
+  protected void apply(SumData data) {
+    data.sum += tempSum;
+    data.exists |= tempExists;
+  }
+
+  @Override
+  public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+    consumer.accept(new DoubleCheckedReservation(
+        value -> {
+          ioData.sum += value;
+          ioData.exists = true;
+        },
+        ()-> ioData.sum,
+        ()-> ioData.exists
+      ));
+  }
+
+  @Override
+  public void setMergedData(ReductionData data) {
+    sum = ((SumData)data).sum;
+    exists = data.exists;
+  }
+
+  @Override
+  public void setData(ReductionData data) {
+    sum = ((SumData)data).sum;
+    exists = data.exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  
+  public static class SumData extends ReductionData {
+    double sum;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/UniqueCollector.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/UniqueCollector.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/UniqueCollector.java
new file mode 100644
index 0000000..3e30114
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/UniqueCollector.java
@@ -0,0 +1,241 @@
+/*
+ * 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.solr.analytics.function.reduction.data;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.function.reduction.data.ReductionData;
+import org.apache.solr.analytics.stream.reservation.DoubleArrayReservation;
+import org.apache.solr.analytics.stream.reservation.FloatArrayReservation;
+import org.apache.solr.analytics.stream.reservation.IntArrayReservation;
+import org.apache.solr.analytics.stream.reservation.LongArrayReservation;
+import org.apache.solr.analytics.stream.reservation.ReductionDataReservation;
+import org.apache.solr.analytics.stream.reservation.StringArrayReservation;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValueStream;
+
+/**
+ * Collects the number of unique values that exist for the given parameter. 
+ * <p>
+ * Supported types are:
+ * <ul>
+ * <li>Int
+ * <li>Long
+ * <li>Float
+ * <li>Double
+ * <li>Date (through longs)
+ * <li>String
+ * </ul>
+ */
+public abstract class UniqueCollector<T> extends ReductionDataCollector<UniqueCollector.UniqueData<T>> {
+  public static final String name = "unique";
+  private final String exprStr;
+  
+  public UniqueCollector(AnalyticsValueStream param) {
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.tempSet = new HashSet<T>();
+  }
+  
+  private long count;
+
+  /**
+   * Get the count of unique values in the set data.
+   * 
+   * @return the count of unique values
+   */
+  public long count() {
+    return count;
+  }
+
+  @Override
+  public UniqueData<T> newData() {
+    UniqueData<T> data = new UniqueData<T>();
+    data.set = new HashSet<>();
+    data.exists = false;
+    return data;
+  }
+  
+  Set<T> tempSet;
+  @Override
+  protected void apply(UniqueData<T> data) {
+    data.set.addAll(tempSet);
+  }
+  
+  Iterator<T> iter;
+  public int startExport() {
+    iter = ioData.set.iterator();
+    return ioData.set.size();
+  }
+  public T exportNext() {
+    return iter.next();
+  }
+
+  @Override
+  public void setMergedData(ReductionData data) {
+    count = ((UniqueData<?>)data).set.size();
+  }
+
+  @Override
+  public void setData(ReductionData data) {
+    count = ((UniqueData<?>)data).set.size();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  
+  public static class UniqueData<T> extends ReductionData {
+    Set<T> set;
+  }
+
+  public static class UniqueIntCollector extends UniqueCollector<Integer> {
+    private IntValueStream param;
+    
+    public UniqueIntCollector(IntValueStream param) {
+      super(param);
+      this.param = param;
+    }
+
+    @Override
+    public void collect() {
+      tempSet.clear();
+      param.streamInts( val -> tempSet.add(val) );
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new IntArrayReservation(
+          value -> ioData.set.add(value),
+          size -> {},
+          () -> exportNext(),
+          () -> startExport()
+        ));
+    }
+  }
+  
+  public static class UniqueLongCollector extends UniqueCollector<Long> {
+    private LongValueStream param;
+    
+    public UniqueLongCollector(LongValueStream param) {
+      super(param);
+      this.param = param;
+    }
+
+    @Override
+    public void collect() {
+      tempSet.clear();
+      param.streamLongs( val -> tempSet.add(val) );
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new LongArrayReservation(
+          value -> ioData.set.add(value),
+          size -> {},
+          () -> exportNext(),
+          () -> startExport()
+        ));
+    }
+  }
+  
+  public static class UniqueFloatCollector extends UniqueCollector<Float> {
+    private FloatValueStream param;
+    
+    public UniqueFloatCollector(FloatValueStream param) {
+      super(param);
+      this.param = param;
+    }
+
+    @Override
+    public void collect() {
+      tempSet.clear();
+      param.streamFloats( val -> tempSet.add(val) );
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new FloatArrayReservation(
+          value -> ioData.set.add(value),
+          size -> {},
+          () -> exportNext(),
+          () -> startExport()
+        ));
+    }
+  }
+  
+  public static class UniqueDoubleCollector extends UniqueCollector<Double> {
+    private DoubleValueStream param;
+    
+    public UniqueDoubleCollector(DoubleValueStream param) {
+      super(param);
+      this.param = param;
+    }
+
+    @Override
+    public void collect() {
+      tempSet.clear();
+      param.streamDoubles( val -> tempSet.add(val) );
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new DoubleArrayReservation(
+          value -> ioData.set.add(value),
+          size -> {},
+          () -> exportNext(),
+          () -> startExport()
+        ));
+    }
+  }
+  
+  public static class UniqueStringCollector extends UniqueCollector<String> {
+    private StringValueStream param;
+    
+    public UniqueStringCollector(StringValueStream param) {
+      super(param);
+      this.param = param;
+    }
+
+    @Override
+    public void collect() {
+      tempSet.clear();
+      param.streamStrings( val -> tempSet.add(val) );
+    }
+    
+    @Override
+    public void submitReservations(Consumer<ReductionDataReservation<?,?>> consumer) {
+      consumer.accept(new StringArrayReservation(
+          value -> ioData.set.add(value),
+          size -> {},
+          () -> exportNext(),
+          () -> startExport()
+        ));
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/package-info.java
new file mode 100644
index 0000000..15b4d18
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/data/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+ 
+/** 
+ * Reduction data collectors to use while computing analytics expressions.
+ * For multi-sharded collections, this is the data that is sent from shard to shard.
+ */
+package org.apache.solr.analytics.function.reduction.data;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/package-info.java
new file mode 100644
index 0000000..ae45ef1
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+ 
+/** 
+ * Reduction functions to use for analytics expressions.
+ */
+package org.apache.solr.analytics.function.reduction;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/package-info.java
new file mode 100644
index 0000000..e5bd519
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+ 
+/** 
+ * A solr component to compute complex analytics over search results. 
+ */
+package org.apache.solr.analytics;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AbstractFieldFacetRequest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AbstractFieldFacetRequest.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AbstractFieldFacetRequest.java
deleted file mode 100644
index 8121f56..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AbstractFieldFacetRequest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.solr.analytics.request;
-
-import org.apache.solr.schema.SchemaField;
-
-/**
- * An abstract request for a facet over a single field, such as a field or range facet.
- */
-public abstract class AbstractFieldFacetRequest implements FacetRequest {
-  protected SchemaField field = null;
-  
-  public AbstractFieldFacetRequest(SchemaField field) {
-    this.field = field;
-  }
-
-  public SchemaField getField() {
-    return field;
-  }
-
-  public void setField(SchemaField field) {
-    this.field = field;
-  }
-
-  public String getName() {
-    return field.getName();
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsContentHandler.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsContentHandler.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsContentHandler.java
deleted file mode 100644
index b93a59e..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/request/AnalyticsContentHandler.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * 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.solr.analytics.request;
-
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.List;
-
-import org.apache.solr.analytics.request.FieldFacetRequest.FacetSortDirection;
-import org.apache.solr.analytics.request.FieldFacetRequest.FacetSortSpecification;
-import org.apache.solr.common.params.FacetParams.FacetRangeInclude;
-import org.apache.solr.common.params.FacetParams.FacetRangeOther;
-import org.apache.solr.schema.IndexSchema;
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.Locator;
-import org.xml.sax.SAXException;
-
-/**
- * Handles the parsing of the AnalysisRequestEnvelope elements if passed in through XML.
- */
-public class AnalyticsContentHandler implements ContentHandler {
-  // XML Element/Attribute Name Constants
-  public static final String ANALYTICS_REQUEST_ENVELOPE="analyticsRequestEnvelope";
-  
-  public static final String ANALYTICS_REQUEST="analyticsRequest";
-  public static final String NAME="name";
-  
-  public static final String STATISTIC="statistic";
-  public static final String EXPRESSION="expression";
-  
-  public static final String FIELD_FACET="fieldFacet";
-  public static final String FIELD="field";
-  public static final String SHOW_MISSING="showMissing";
-  public static final String LIMIT="limit";
-  public static final String MIN_COUNT="minCount";
-  
-  public static final String SORT_SPECIFICATION="sortSpecification";
-  public static final String STAT_NAME="statName";
-  public static final String DIRECTION="direction";
-  
-  public static final String RANGE_FACET="rangeFacet";
-  public static final String START="start";
-  public static final String END="end";
-  public static final String GAP="gap";
-  public static final String INCLUDE_BOUNDARY="includeBoundary";
-  public static final String OTHER_RANGE="otherRange";
-  public static final String HARD_END="hardend";
-  
-  public static final String QUERY_FACET="queryFacet";
-  public static final String QUERY="query";
-  
-  // Default Values
-  public static final int DEFAULT_FACET_LIMIT = -1;
-  public static final boolean DEFAULT_FACET_HARDEND = false;
-  public static final int DEFAULT_FACET_MINCOUNT = 0;
-  public static final boolean DEFAULT_FACET_FIELD_SHOW_MISSING = false;
-
-  boolean inEnvelope = false;
-  boolean inRequest = false;
-  boolean inStatistic = false;
-  boolean inFieldFacet = false;
-  boolean inSortSpecification = false;
-  boolean inQueryFacet = false;
-  boolean inRangeFacet = false;
-  
-  private final IndexSchema schema;
-  
-  // Objects to use while building the Analytics Requests
-  
-  String currentElementText;
-  
-  List<AnalyticsRequest> requests;
-  
-  AnalyticsRequest analyticsRequest;
-  List<ExpressionRequest> expressionList;
-  List<FieldFacetRequest> fieldFacetList;
-  List<RangeFacetRequest> rangeFacetList;
-  List<QueryFacetRequest> queryFacetList;
-  
-  ExpressionRequest expression;
-  
-  FieldFacetRequest fieldFacet;
-  int limit;
-  int minCount;
-  boolean showMissing;
-  FacetSortSpecification sortSpecification;
-  
-  RangeFacetRequest rangeFacet;
-  boolean hardend;
-  List<String> gaps;
-  EnumSet<FacetRangeInclude> includeBoundaries;
-  EnumSet<FacetRangeOther> otherRanges;
-  
-  String queryName;
-  List<String> queries;
-  
-  public AnalyticsContentHandler(IndexSchema schema) {
-    this.schema = schema;
-  }
-
-  @Override
-  public void setDocumentLocator(Locator locator) { }
-
-  @Override
-  public void startDocument() throws SAXException { }
-
-  @Override
-  public void endDocument() throws SAXException { }
-
-  @Override
-  public void startPrefixMapping(String prefix, String uri) throws SAXException { }
-
-  @Override
-  public void endPrefixMapping(String prefix) throws SAXException { }
-
-  @Override
-  public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
-    currentElementText = "";
-    if (inEnvelope) {
-      if (inRequest) {
-        if (localName.equals(STATISTIC)) {
-          // Start a Statistic Request
-          inStatistic = true;
-        } else if (inFieldFacet) {
-          if (localName.equals(SORT_SPECIFICATION)) {
-            // Start a Sort Specification
-            inSortSpecification = true;
-            sortSpecification = new FacetSortSpecification();
-          }
-        } else if (localName.equals(FIELD_FACET)) {
-          // Start a Field Facet Request
-          // Get attributes (limit, minCount, showMissing)
-          String att = atts.getValue(uri,LIMIT);
-          if (att!=null) {
-            limit = Integer.parseInt(att);
-          } else {
-            limit = DEFAULT_FACET_LIMIT;
-          }
-          att = atts.getValue(uri,MIN_COUNT);
-          if (att!=null) {
-            minCount = Integer.parseInt(att);
-          } else {
-            minCount = DEFAULT_FACET_MINCOUNT;
-          }
-          att = atts.getValue(uri,SHOW_MISSING);
-          if (att!=null) {
-            showMissing = Boolean.parseBoolean(att);
-          } else {
-            showMissing = DEFAULT_FACET_FIELD_SHOW_MISSING;
-          }
-          
-          inFieldFacet = true;
-        } else if (localName.equals(RANGE_FACET)) {
-          // Start a Range Facet Request
-          // Get attributes (hardEnd)
-          String att = atts.getValue(uri,HARD_END);
-          if (att!=null) {
-            hardend = Boolean.parseBoolean(att);
-          } else {
-            hardend = false;
-          }
-          
-          // Initiate Range Facet classes
-          gaps = new ArrayList<>();
-          includeBoundaries = EnumSet.noneOf(FacetRangeInclude.class);
-          otherRanges = EnumSet.noneOf(FacetRangeOther.class);
-          inRangeFacet = true;
-        } else if (localName.equals(QUERY_FACET)) {
-          // Start a Query Facet Request
-          queries = new ArrayList<>();
-          inQueryFacet = true;
-        }
-      } else if (localName.equals(ANALYTICS_REQUEST)){
-        // Start an Analytics Request
-        
-        // Renew each list.
-        fieldFacetList = new ArrayList<>();
-        rangeFacetList = new ArrayList<>();
-        queryFacetList = new ArrayList<>();
-        expressionList = new ArrayList<>();
-        inRequest = true;
-      }
-    } else if (localName.equals(ANALYTICS_REQUEST_ENVELOPE)){
-      //Begin the parsing of the Analytics Requests
-      requests = new ArrayList<>();
-      inEnvelope = true;
-    }
-  }
-
-  @Override
-  public void endElement(String uri, String localName, String qName) throws SAXException {
-    if (inEnvelope) {
-      if (inRequest) {
-        if (inStatistic) {
-          if (localName.equals(EXPRESSION)) {
-            expression = new ExpressionRequest(currentElementText,currentElementText);
-          } else if (localName.equals(NAME)) {
-            expression.setName(currentElementText);
-          } else if (localName.equals(STATISTIC)) {
-            // Finished Parsing the Statistic Request
-            expressionList.add(expression);
-            inStatistic = false;
-          } 
-        } else if (inFieldFacet) {
-          if (inSortSpecification) {
-            if (localName.equals(STAT_NAME)) {
-              sortSpecification.setStatistic(currentElementText);
-            } else if (localName.equals(DIRECTION)) {
-              sortSpecification.setDirection(FacetSortDirection.fromExternal(currentElementText));
-            } else if (localName.equals(SORT_SPECIFICATION)) {
-              // Finished Parsing the Sort Specification
-              fieldFacet.setSort(sortSpecification);
-              inSortSpecification = false;
-            } 
-          } else if (localName.equals(FIELD)) {
-            fieldFacet = new FieldFacetRequest(schema.getField(currentElementText));
-          } else if (localName.equals(FIELD_FACET)) {
-            // Finished Parsing the Field Facet Request
-            fieldFacet.setLimit(limit);
-            fieldFacet.showMissing(showMissing);
-            fieldFacetList.add(fieldFacet);
-            inFieldFacet = false;
-          } 
-        } else if (inRangeFacet) {
-          if (localName.equals(FIELD)) {
-            rangeFacet = new RangeFacetRequest(schema.getField(currentElementText), "", "", new String[1]);
-          } else if (localName.equals(START)) {
-            rangeFacet.setStart(currentElementText);
-          } else if (localName.equals(END)) {
-            rangeFacet.setEnd(currentElementText);
-          } else if (localName.equals(GAP)) {
-            gaps.add(currentElementText);
-          } else if (localName.equals(INCLUDE_BOUNDARY)) {
-            includeBoundaries.add(FacetRangeInclude.get(currentElementText));
-          } else if (localName.equals(OTHER_RANGE)) {
-            otherRanges.add(FacetRangeOther.get(currentElementText));
-          } else if (localName.equals(RANGE_FACET)) {
-            // Finished Parsing the Range Facet Request
-            rangeFacet.setHardEnd(hardend);
-            rangeFacet.setGaps(gaps.toArray(new String[1]));
-            rangeFacet.setInclude(includeBoundaries);
-            rangeFacet.setOthers(otherRanges);
-            inRangeFacet = false;
-            rangeFacetList.add(rangeFacet);
-          } 
-        } else if (inQueryFacet) {
-          if (localName.equals(NAME)) {
-            queryName = currentElementText;
-          } else if (localName.equals(QUERY)) {
-            queries.add(currentElementText);
-          } else if (localName.equals(QUERY_FACET)) {
-            // Finished Parsing the Query Facet Request
-            QueryFacetRequest temp = new QueryFacetRequest(queryName);
-            temp.setQueries(queries);
-            queryFacetList.add(temp);
-            inQueryFacet = false;
-          }
-        } else if (localName.equals(NAME)) {
-          analyticsRequest = new AnalyticsRequest(currentElementText);
-        } else if (localName.equals(ANALYTICS_REQUEST)){
-          // Finished Parsing the Analytics Request
-          analyticsRequest.setExpressions(expressionList);
-          analyticsRequest.setFieldFacets(fieldFacetList);
-          analyticsRequest.setRangeFacets(rangeFacetList);
-          analyticsRequest.setQueryFacets(queryFacetList);
-          requests.add(analyticsRequest);
-          inRequest = false;
-        }
-      } else if (localName.equals(ANALYTICS_REQUEST_ENVELOPE)){
-        // Finished Parsing
-        inEnvelope = false;
-      }
-    }
-  }
-
-  @Override
-  public void characters(char[] ch, int start, int length) throws SAXException {
-    currentElementText += new String(ch,start,length);
-  }
-
-  @Override
-  public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { }
-
-  @Override
-  public void processingInstruction(String target, String data) throws SAXException { }
-
-  @Override
-  public void skippedEntity(String name) throws SAXException { }
-  
-  /**
-   * Returns the list of Analytics Requests built during parsing.
-   * 
-   * @return List of {@link AnalyticsRequest} objects specified by the given XML file
-   */
-  public List<AnalyticsRequest> getAnalyticsRequests() {
-    return requests;
-  }
-
-}


[13/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LogFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LogFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LogFunction.java
new file mode 100644
index 0000000..313f2e2
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LogFunction.java
@@ -0,0 +1,51 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A logarithm mapping function.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If one numeric Value or ValueStream is passed in, a {@link DoubleValue} or {@link DoubleValueStream}
+ * representing the natural logarithm is returned.
+ * <li>If two numeric Values are passed in, a {@link DoubleValue} representing the logarithm of the first with the second as the base is returned.
+ * <li>If a numeric ValueStream and a numeric Value are passed in, a {@link DoubleValueStream} representing the logarithm of 
+ * the Value with each of the values of the ValueStream for a document as the base is returned. 
+ * (Or the other way, since the Value and ValueStream can be used in either order)
+ * </ul>
+ */
+public class LogFunction {
+  public static final String name = "log";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires at least 1.");
+    } else if (params.length == 1) {
+      return LambdaFunction.createDoubleLambdaFunction(name, (a) -> Math.log(a), (DoubleValueStream)params[0]);
+    } else if (params.length == 2) {
+      return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> Math.log(a)/Math.log(b), (DoubleValueStream)params[0], (DoubleValueStream)params[1]);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function accepts at most 2 paramaters, " + params.length + " found.");
+    }
+  });
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LogicFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LogicFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LogicFunction.java
new file mode 100644
index 0000000..806cb0a
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LogicFunction.java
@@ -0,0 +1,90 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.function.mapping.LambdaFunction.TwoBoolInBoolOutLambda;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.BooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * Contains all logical mapping functions.
+ * <p>
+ * Logical mapping functions can be used in the following ways:
+ * <ul>
+ * <li>If a single {@link BooleanValueStream} is passed in, a {@link BooleanValue} representing the logical operation
+ * on all of the values for each document is returned.
+ * <li>If a {@link BooleanValueStream} and a {@link BooleanValue} are passed in, a {@link BooleanValue} representing the logical operation on 
+ * the {@link BooleanValue} and each of the values of the {@link BooleanValueStream} for a document is returned.
+ * (Or the other way, since the Value and ValueStream can be used in either order)
+ * <li>If multiple {@link BooleanValue}s are passed in, a {@link BooleanValue} representing the logical operation on all values is returned.
+ * </ul>
+ */
+public class LogicFunction {
+  
+  private static BooleanValueStream createBitwiseFunction(String name, TwoBoolInBoolOutLambda comp, AnalyticsValueStream... params) {
+    if (params.length == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires parameters.");
+    } 
+    else if (params.length == 1) {
+      if (params[0] instanceof BooleanValueStream) {
+        return LambdaFunction.createBooleanLambdaFunction(name, comp, (BooleanValueStream)params[0]);
+      }
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires boolean parameters. Incorrect param: "+params[0].getExpressionStr());
+    } 
+    else if (params.length == 2) {
+      AnalyticsValueStream param1 = params[0];
+      AnalyticsValueStream param2 = params[1];
+      if (param1 instanceof BooleanValueStream && param2 instanceof BooleanValueStream) {
+        return LambdaFunction.createBooleanLambdaFunction(name, (a,b) -> a && b, (BooleanValueStream)param1, (BooleanValueStream)param2);
+      }
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires boolean parameters.");
+    }
+    BooleanValue[] castedParams = new BooleanValue[params.length];
+    for (int i = 0; i < params.length; i++) {
+      if (params[i] instanceof BooleanValue) {
+        castedParams[i] = (BooleanValue) params[i];
+      } else {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires that all parameters be single-valued if more than 2 are given.");
+      }
+    }
+    return LambdaFunction.createBooleanLambdaFunction(name, comp, castedParams);
+  };
+  
+  /**
+   * A mapping function for the logical operation AND.
+   */
+  public static class AndFunction {
+    public static final String name = "and";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return LogicFunction.createBitwiseFunction(name, (a,b) -> a && b, params);
+    });
+  }
+
+  /**
+   * A mapping function for the logical operation OR.
+   */
+  public static class OrFunction {
+    public static final String name = "or";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return LogicFunction.createBitwiseFunction(name, (a,b) -> a || b, params);
+    });
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/MultFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/MultFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/MultFunction.java
new file mode 100644
index 0000000..4a5b173
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/MultFunction.java
@@ -0,0 +1,68 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+
+/**
+ * An multiplication mapping function.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If a single numeric ValueStream is passed in, a {@link DoubleValue} representing the multiplication of the values for each document is returned.
+ * <li>If a numeric ValueStream and a numeric Value are passed in, a {@link DoubleValueStream} representing the multiplication of 
+ * the Value and each of the values of the ValueStream for a document is returned.
+ * (Or the other way, since the Value and ValueStream can be used in either order)
+ * <li>If multiple numeric Values are passed in, a {@link DoubleValue} representing the multiplication of all values is returned.
+ * </ul>
+ */
+public class MultFunction {
+  public static final String name = "mult";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires parameters.");
+    } 
+    else if (params.length == 1) {
+      if (params[0] instanceof DoubleValueStream) {
+        return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> a*b, (DoubleValueStream)params[0]);
+      }
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires numeric parameters. Incorrect param: "+params[0].getExpressionStr());
+    } 
+    else if (params.length == 2) {
+      AnalyticsValueStream param1 = params[0];
+      AnalyticsValueStream param2 = params[1];
+      if (param1 instanceof DoubleValueStream && param2 instanceof DoubleValueStream) {
+        return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> a*b, (DoubleValueStream)param1, (DoubleValueStream)param2);
+      }
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires numeric parameters.");
+    }
+    DoubleValue[] castedParams = new DoubleValue[params.length];
+    for (int i = 0; i < params.length; i++) {
+      if (params[i] instanceof DoubleValue) {
+        castedParams[i] = (DoubleValue) params[i];
+      } else {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires that all parameters be single-valued if more than 2 are given.");
+      }
+    }
+    return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> a*b, castedParams);
+  });
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/NegateFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/NegateFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/NegateFunction.java
new file mode 100644
index 0000000..65916e6
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/NegateFunction.java
@@ -0,0 +1,58 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.BooleanValueStream;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A negation mapping function.
+ * <p>
+ * Takes a numeric or boolean ValueStream or Value and returns a ValueStream or Value of the same numeric type.
+ */
+public class NegateFunction {
+  public static final String name = "neg";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 1) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 1 paramaters, " + params.length + " found.");
+    }
+    AnalyticsValueStream param = params[0];
+    if (param instanceof BooleanValueStream) {
+      return LambdaFunction.createBooleanLambdaFunction(name, x -> !x, (BooleanValueStream)param);
+    }
+    if (param instanceof IntValueStream) {
+      return LambdaFunction.createIntLambdaFunction(name, x -> x*-1, (IntValueStream)param);
+    }
+    if (param instanceof LongValueStream) {
+      return LambdaFunction.createLongLambdaFunction(name, x -> x*-1, (LongValueStream)param);
+    }
+    if (param instanceof FloatValueStream) {
+      return LambdaFunction.createFloatLambdaFunction(name, x -> x*-1, (FloatValueStream)param);
+    }
+    if (param instanceof DoubleValueStream) {
+      return LambdaFunction.createDoubleLambdaFunction(name, x -> x*-1, (DoubleValueStream)param);
+      }
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a boolean or numeric parameter, "+param.getExpressionStr()+" found.");
+  });
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/NumericConvertFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/NumericConvertFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/NumericConvertFunction.java
new file mode 100644
index 0000000..2381f60
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/NumericConvertFunction.java
@@ -0,0 +1,256 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValue;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValue;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.IntValue.AbstractIntValue;
+import org.apache.solr.analytics.value.IntValueStream.AbstractIntValueStream;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.value.LongValueStream.AbstractLongValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * An abstract numeric converting mapping function. For example "round()" would convert a float to an int and a double to a long. 
+ * <p>
+ * Takes a numeric Double or Float ValueStream or Value and returns a Long or Int ValueStream or Value, repectively.
+ */
+public class NumericConvertFunction {
+  
+  private static LongValueStream createConvertFunction(String name, ConvertFloatFunction fconv, ConvertDoubleFunction dconv, AnalyticsValueStream... params) {
+    if (params.length != 1) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 1 paramaters, " + params.length + " found.");
+    }
+    AnalyticsValueStream param = params[0];
+    if (param instanceof FloatValueStream) {
+      if (param instanceof FloatValue) {
+        return new ConvertFloatValueFunction(name, (FloatValue)param, fconv);
+      }
+      return new ConvertFloatStreamFunction(name, (FloatValueStream)param, fconv);
+    } else if (param instanceof DoubleValueStream) {
+      if (param instanceof DoubleValue) {
+        return new ConvertDoubleValueFunction(name, (DoubleValue)param, dconv);
+      }
+      return new ConvertDoubleStreamFunction(name, (DoubleValueStream)param, dconv);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a numeric parameter.");
+    }
+  }
+  
+  /**
+   * A numeric mapping function that returns the floor of the input.
+   */
+  public static class FloorFunction {
+    public static final String name = "floor";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return NumericConvertFunction.createConvertFunction(name, val -> (int)Math.floor(val), val -> (long)Math.floor(val), params);
+    });
+  }
+  
+  /**
+   * A numeric mapping function that returns the ceiling of the input.
+   */
+  public static class CeilingFunction {
+    public static final String name = "ceil";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return NumericConvertFunction.createConvertFunction(name, val -> (int)Math.ceil(val), val -> (long)Math.ceil(val), params);
+    });
+  }
+  
+  /**
+   * A numeric mapping function that returns the rounded input.
+   */
+  public static class RoundFunction {
+    public static final String name = "round";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return NumericConvertFunction.createConvertFunction(name, val -> (int)Math.round(val), val -> (long)Math.round(val), params);
+    });
+  }
+
+}
+@FunctionalInterface
+interface ConvertFloatFunction {
+  public int convert(float value);
+}
+@FunctionalInterface
+interface ConvertDoubleFunction {
+  public long convert(double value);
+}
+/**
+ * A function to convert a {@link FloatValue} to a {@link IntValue}.
+ */
+class ConvertFloatValueFunction extends AbstractIntValue {
+  private final String name;
+  private final FloatValue param;
+  private final ConvertFloatFunction conv;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public ConvertFloatValueFunction(String name, FloatValue param, ConvertFloatFunction conv) {
+    this.name = name;
+    this.param = param;
+    this.conv = conv;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,param);
+  }
+
+  @Override
+  public int getInt() {
+    return conv.convert(param.getFloat());
+  }
+  @Override
+  public boolean exists() {
+    return param.exists();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A function to convert a {@link FloatValueStream} to a {@link IntValueStream}.
+ */
+class ConvertFloatStreamFunction extends AbstractIntValueStream {
+  private final String name;
+  private final FloatValueStream param;
+  private final ConvertFloatFunction conv;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public ConvertFloatStreamFunction(String name, FloatValueStream param, ConvertFloatFunction conv) {
+    this.name = name;
+    this.param = param;
+    this.conv = conv;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,param);
+  }
+
+  @Override
+  public void streamInts(IntConsumer cons) {
+    param.streamFloats( value -> cons.accept(conv.convert(value)));
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A function to convert a {@link DoubleValue} to a {@link LongValue}.
+ */
+class ConvertDoubleValueFunction extends AbstractLongValue {
+  private final String name;
+  private final DoubleValue param;
+  private final ConvertDoubleFunction conv;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public ConvertDoubleValueFunction(String name, DoubleValue param, ConvertDoubleFunction conv) {
+    this.name = name;
+    this.param = param;
+    this.conv = conv;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,param);
+  }
+
+  @Override
+  public long getLong() {
+    return conv.convert(param.getDouble());
+  }
+  @Override
+  public boolean exists() {
+    return param.exists();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A function to convert a {@link DoubleValueStream} to a {@link LongValueStream}.
+ */
+class ConvertDoubleStreamFunction extends AbstractLongValueStream {
+  private final String name;
+  private final DoubleValueStream param;
+  private final ConvertDoubleFunction conv;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public ConvertDoubleStreamFunction(String name, DoubleValueStream param, ConvertDoubleFunction conv) {
+    this.name = name;
+    this.param = param;
+    this.conv = conv;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,param);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    param.streamDoubles( value -> cons.accept(conv.convert(value)));
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/PowerFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/PowerFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/PowerFunction.java
new file mode 100644
index 0000000..0688ba0
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/PowerFunction.java
@@ -0,0 +1,51 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A power mapping function.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If two numeric Values are passed in, a {@link DoubleValue} representing the first taken to the power of the second is returned.
+ * <li>If a numeric ValueStream and a numeric Value are passed in, a {@link DoubleValueStream} representing the power of 
+ * the Value to each of the values of the ValueStream for a document is returned. 
+ * (Or the other way, since the Value and ValueStream can be used in either order)
+ * </ul>
+ */
+public class PowerFunction {
+  public static final String name = "pow";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 2) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 2 paramaters, " + params.length + " found.");
+    }
+    AnalyticsValueStream param1 = params[0];
+    AnalyticsValueStream param2 = params[1];
+    if (param1 instanceof DoubleValueStream && param2 instanceof DoubleValueStream) {
+      return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> Math.pow(a,b), (DoubleValueStream)param1, (DoubleValueStream)param2);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires numeric parameters.");
+    }
+  });
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/RemoveFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/RemoveFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/RemoveFunction.java
new file mode 100644
index 0000000..5800a1b
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/RemoveFunction.java
@@ -0,0 +1,796 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.AnalyticsValue;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.BooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValue;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValue;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.AnalyticsValue.AbstractAnalyticsValue;
+import org.apache.solr.analytics.value.BooleanValue.AbstractBooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream.AbstractBooleanValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DateValueStream.AbstractDateValueStream;
+import org.apache.solr.analytics.value.DoubleValue.AbstractDoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream.AbstractDoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue.AbstractFloatValue;
+import org.apache.solr.analytics.value.FloatValueStream.AbstractFloatValueStream;
+import org.apache.solr.analytics.value.IntValue.AbstractIntValue;
+import org.apache.solr.analytics.value.IntValueStream.AbstractIntValueStream;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.value.LongValueStream.AbstractLongValueStream;
+import org.apache.solr.analytics.value.StringValue.AbstractStringValue;
+import org.apache.solr.analytics.value.StringValueStream.AbstractStringValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A mapping function to remove an {@link AnalyticsValue} from an {@link AnalyticsValue} or an {@link AnalyticsValueStream}.
+ * For each document, the value exists if it doesn't equal the value of the second parameter.
+ * <p>
+ * The first parameter can be any type of analytics expression. If the parameter is multi-valued, then the return will be multi-valued. (Required)
+ * <br>
+ * The second parameter, which is the value to remove from the first parameter, must be an {@link AnalyticsValue}, aka single-valued. (Required)
+ * <p>
+ * The resulting Value or ValueStream will be typed with the closest super-type of the two parameters.
+ * (e.g. {@value #name}(int,float) will return a float)
+ */
+public class RemoveFunction {
+  public static final String name = "remove";
+  
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 2) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 2 paramaters, " + params.length + " found.");
+    }
+    if (!(params[1] instanceof AnalyticsValue)) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires the remove paramater to be single-valued.");
+    }
+
+    AnalyticsValueStream baseExpr = params[0];
+    AnalyticsValue removeExpr = (AnalyticsValue)params[1];
+
+    if (baseExpr instanceof DateValue && removeExpr instanceof DateValue) {
+      return new DateRemoveFunction((DateValue)baseExpr,(DateValue)removeExpr);
+    }
+    if (baseExpr instanceof DateValueStream && removeExpr instanceof DateValue) {
+      return new DateStreamRemoveFunction((DateValueStream)baseExpr,(DateValue)removeExpr);
+    }
+    if (baseExpr instanceof BooleanValue && removeExpr instanceof BooleanValue) {
+      return new BooleanRemoveFunction((BooleanValue)baseExpr,(BooleanValue)removeExpr);
+    }
+    if (baseExpr instanceof BooleanValueStream && removeExpr instanceof BooleanValue) {
+      return new BooleanStreamRemoveFunction((BooleanValueStream)baseExpr,(BooleanValue)removeExpr);
+    }
+    if (baseExpr instanceof IntValue && removeExpr instanceof IntValue) {
+      return new IntRemoveFunction((IntValue)baseExpr,(IntValue)removeExpr);
+    }
+    if (baseExpr instanceof IntValueStream && removeExpr instanceof IntValue) {
+      return new IntStreamRemoveFunction((IntValueStream)baseExpr,(IntValue)removeExpr);
+    }
+    if (baseExpr instanceof LongValue && removeExpr instanceof LongValue) {
+      return new LongRemoveFunction((LongValue)baseExpr,(LongValue)removeExpr);
+    }
+    if (baseExpr instanceof LongValueStream && removeExpr instanceof LongValue) {
+      return new LongStreamRemoveFunction((LongValueStream)baseExpr,(LongValue)removeExpr);
+    }
+    if (baseExpr instanceof FloatValue && removeExpr instanceof FloatValue) {
+      return new FloatRemoveFunction((FloatValue)baseExpr,(FloatValue)removeExpr);
+    }
+    if (baseExpr instanceof FloatValueStream && removeExpr instanceof FloatValue) {
+      return new FloatStreamRemoveFunction((FloatValueStream)baseExpr,(FloatValue)removeExpr);
+    }
+    if (baseExpr instanceof DoubleValue && removeExpr instanceof DoubleValue) {
+      return new DoubleRemoveFunction((DoubleValue)baseExpr,(DoubleValue)removeExpr);
+    }
+    if (baseExpr instanceof DoubleValueStream && removeExpr instanceof DoubleValue) {
+      return new DoubleStreamRemoveFunction((DoubleValueStream)baseExpr,(DoubleValue)removeExpr);
+    }
+    if (baseExpr instanceof StringValue && removeExpr instanceof StringValue) {
+      return new StringRemoveFunction((StringValue)baseExpr,(StringValue)removeExpr);
+    }
+    if (baseExpr instanceof StringValueStream && removeExpr instanceof StringValue) {
+      return new StringStreamRemoveFunction((StringValueStream)baseExpr,(StringValue)removeExpr);
+    }
+    if (baseExpr instanceof AnalyticsValue) {
+      return new ValueRemoveFunction((AnalyticsValue)baseExpr,removeExpr);
+    }
+    return new StreamRemoveFunction(baseExpr,removeExpr);
+  });
+}
+class StreamRemoveFunction implements AnalyticsValueStream {
+  private final AnalyticsValueStream baseExpr;
+  private final AnalyticsValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StreamRemoveFunction(AnalyticsValueStream baseExpr, AnalyticsValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    Object removeValue = removeExpr.getObject();
+    if (removeExpr.exists()) {
+      baseExpr.streamObjects(value -> {
+        if (removeValue.equals(value)) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamObjects(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class ValueRemoveFunction extends AbstractAnalyticsValue {
+  private final AnalyticsValue baseExpr;
+  private final AnalyticsValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public ValueRemoveFunction(AnalyticsValue baseExpr, AnalyticsValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public Object getObject() {
+    Object value = baseExpr.getObject();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value.equals(removeExpr.toString()) ? removeExpr.exists()? false : true : true;
+    }
+    return value;
+  }
+  
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanStreamRemoveFunction extends AbstractBooleanValueStream {
+  private final BooleanValueStream baseExpr;
+  private final BooleanValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanStreamRemoveFunction(BooleanValueStream baseExpr, BooleanValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    boolean removeValue = removeExpr.getBoolean();
+    if (removeExpr.exists()) {
+      baseExpr.streamBooleans(value -> {
+        if (removeValue==value) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamBooleans(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanRemoveFunction extends AbstractBooleanValue {
+  private final BooleanValue baseExpr;
+  private final BooleanValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanRemoveFunction(BooleanValue baseExpr, BooleanValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+  
+  @Override
+  public boolean getBoolean() {
+    boolean value = baseExpr.getBoolean();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value==removeExpr.getBoolean() ? removeExpr.exists()? false : true : true;
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntStreamRemoveFunction extends AbstractIntValueStream {
+  private final IntValueStream baseExpr;
+  private final IntValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntStreamRemoveFunction(IntValueStream baseExpr, IntValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamInts(IntConsumer cons) {
+    int removeValue = removeExpr.getInt();
+    if (removeExpr.exists()) {
+      baseExpr.streamInts(value -> {
+        if (removeValue==value) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamInts(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntRemoveFunction extends AbstractIntValue {
+  private final IntValue baseExpr;
+  private final IntValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntRemoveFunction(IntValue baseExpr, IntValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+  
+  @Override
+  public int getInt() {
+    int value = baseExpr.getInt();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value==removeExpr.getInt() ? removeExpr.exists()? false : true : true;
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongStreamRemoveFunction extends AbstractLongValueStream {
+  private final LongValueStream baseExpr;
+  private final LongValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongStreamRemoveFunction(LongValueStream baseExpr, LongValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    long removeValue = removeExpr.getLong();
+    if (removeExpr.exists()) {
+      baseExpr.streamLongs(value -> {
+        if (removeValue==value) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamLongs(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongRemoveFunction extends AbstractLongValue {
+  private final LongValue baseExpr;
+  private final LongValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongRemoveFunction(LongValue baseExpr, LongValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+  
+  @Override
+  public long getLong() {
+    long value = baseExpr.getLong();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value==removeExpr.getLong() ? removeExpr.exists()? false : true : true;
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatStreamRemoveFunction extends AbstractFloatValueStream {
+  private final FloatValueStream baseExpr;
+  private final FloatValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatStreamRemoveFunction(FloatValueStream baseExpr, FloatValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    float removeValue = removeExpr.getFloat();
+    if (removeExpr.exists()) {
+      baseExpr.streamFloats(value -> {
+        if (removeValue==value) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamFloats(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatRemoveFunction extends AbstractFloatValue {
+  private final FloatValue baseExpr;
+  private final FloatValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatRemoveFunction(FloatValue baseExpr, FloatValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+  
+  @Override
+  public float getFloat() {
+    float value = baseExpr.getFloat();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value==removeExpr.getFloat() ? removeExpr.exists()? false : true : true;
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DoubleStreamRemoveFunction extends AbstractDoubleValueStream {
+  private final DoubleValueStream baseExpr;
+  private final DoubleValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DoubleStreamRemoveFunction(DoubleValueStream baseExpr, DoubleValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    double removeValue = removeExpr.getDouble();
+    if (removeExpr.exists()) {
+      baseExpr.streamDoubles(value -> {
+        if (removeValue==value) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamDoubles(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DoubleRemoveFunction extends AbstractDoubleValue {
+  private final DoubleValue baseExpr;
+  private final DoubleValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DoubleRemoveFunction(DoubleValue baseExpr, DoubleValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+  
+  @Override
+  public double getDouble() {
+    double value = baseExpr.getDouble();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value==removeExpr.getDouble() ? removeExpr.exists()? false : true : true;
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DateStreamRemoveFunction extends AbstractDateValueStream {
+  private final DateValueStream baseExpr;
+  private final DateValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateStreamRemoveFunction(DateValueStream baseExpr, DateValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    long removeValue = removeExpr.getLong();
+    if (removeExpr.exists()) {
+      baseExpr.streamLongs(value -> {
+        if (removeValue==value) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamLongs(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DateRemoveFunction extends AbstractDateValue {
+  private final DateValue baseExpr;
+  private final DateValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateRemoveFunction(DateValue baseExpr, DateValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public long getLong() {
+    long value = baseExpr.getLong();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value==removeExpr.getLong() ? removeExpr.exists() ? false : true : true;
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringStreamRemoveFunction extends AbstractStringValueStream {
+  private final StringValueStream baseExpr;
+  private final StringValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StringStreamRemoveFunction(StringValueStream baseExpr, StringValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    String removeValue = removeExpr.toString();
+    if (removeExpr.exists()) {
+      baseExpr.streamStrings(value -> {
+        if (removeValue.equals(value)) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamStrings(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringRemoveFunction extends AbstractStringValue {
+  private final StringValue baseExpr;
+  private final StringValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StringRemoveFunction(StringValue baseExpr, StringValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public String getString() {
+    String value = baseExpr.getString();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value.equals(removeExpr.getString()) ? removeExpr.exists()? false : true : true;
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ReplaceFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ReplaceFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ReplaceFunction.java
new file mode 100644
index 0000000..0d9e278
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ReplaceFunction.java
@@ -0,0 +1,914 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.AnalyticsValue;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.BooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValue;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValue;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.AnalyticsValue.AbstractAnalyticsValue;
+import org.apache.solr.analytics.value.BooleanValue.AbstractBooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream.AbstractBooleanValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DateValueStream.AbstractDateValueStream;
+import org.apache.solr.analytics.value.DoubleValue.AbstractDoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream.AbstractDoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue.AbstractFloatValue;
+import org.apache.solr.analytics.value.FloatValueStream.AbstractFloatValueStream;
+import org.apache.solr.analytics.value.IntValue.AbstractIntValue;
+import org.apache.solr.analytics.value.IntValueStream.AbstractIntValueStream;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.value.LongValueStream.AbstractLongValueStream;
+import org.apache.solr.analytics.value.StringValue.AbstractStringValue;
+import org.apache.solr.analytics.value.StringValueStream.AbstractStringValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A mapping function to replace an {@link AnalyticsValue} from an {@link AnalyticsValue} or an {@link AnalyticsValueStream}
+ * with a different {@link AnalyticsValue}.
+ * For each document, all values from the base parameter matching the comparison parameter will be replaced with the fill parameter.
+ * <p>
+ * The first parameter can be any type of analytics expression. If the parameter is multi-valued, then the return will be multi-valued. (Required)
+ * <br>
+ * The second parameter, which is the value to compare to the first parameter, must be an {@link AnalyticsValue}, aka single-valued. (Required)
+ * <br>
+ * The third parameter, which is the value to fill the first parameter with, must be an {@link AnalyticsValue}, aka single-valued. (Required)
+ * <p>
+ * The resulting Value or ValueStream will be typed with the closest super-type of the three parameters.
+ * (e.g. {@value #name}(double,int,float) will return a double)
+ */
+public class ReplaceFunction {
+  public static final String name = "replace";
+
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 3) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 3 paramaters, " + params.length + " found.");
+    }
+    if (!(params[1] instanceof AnalyticsValue && params[2] instanceof AnalyticsValue)) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires the comparator and fill parameters to be single-valued.");
+    }
+
+    AnalyticsValueStream baseExpr = params[0];
+    AnalyticsValue compExpr = (AnalyticsValue)params[1];
+    AnalyticsValue fillExpr = (AnalyticsValue)params[2];
+
+    if (baseExpr instanceof DateValue && compExpr instanceof DateValue && fillExpr instanceof DateValue) {
+      return new DateReplaceFunction((DateValue)baseExpr,(DateValue)compExpr,(DateValue)fillExpr);
+    }
+    if (baseExpr instanceof DateValueStream && compExpr instanceof DateValue && fillExpr instanceof DateValue) {
+      return new DateStreamReplaceFunction((DateValueStream)baseExpr,(DateValue)compExpr,(DateValue)fillExpr);
+    }
+    if (baseExpr instanceof BooleanValue && compExpr instanceof BooleanValue && fillExpr instanceof BooleanValue) {
+      return new BooleanReplaceFunction((BooleanValue)baseExpr,(BooleanValue)compExpr,(BooleanValue)fillExpr);
+    }
+    if (baseExpr instanceof BooleanValueStream && compExpr instanceof BooleanValue && fillExpr instanceof BooleanValue) {
+      return new BooleanStreamReplaceFunction((BooleanValueStream)baseExpr,(BooleanValue)compExpr,(BooleanValue)fillExpr);
+    }
+    if (baseExpr instanceof IntValue && compExpr instanceof IntValue && fillExpr instanceof IntValue) {
+      return new IntReplaceFunction((IntValue)baseExpr,(IntValue)compExpr,(IntValue)fillExpr);
+    }
+    if (baseExpr instanceof IntValueStream && compExpr instanceof IntValue && fillExpr instanceof IntValue) {
+      return new IntStreamReplaceFunction((IntValueStream)baseExpr,(IntValue)compExpr,(IntValue)fillExpr);
+    }
+    if (baseExpr instanceof LongValue && compExpr instanceof LongValue && fillExpr instanceof LongValue) {
+      return new LongReplaceFunction((LongValue)baseExpr,(LongValue)compExpr,(LongValue)fillExpr);
+    }
+    if (baseExpr instanceof LongValueStream && compExpr instanceof LongValue && fillExpr instanceof LongValue) {
+      return new LongStreamReplaceFunction((LongValueStream)baseExpr,(LongValue)compExpr,(LongValue)fillExpr);
+    }
+    if (baseExpr instanceof FloatValue && compExpr instanceof FloatValue && fillExpr instanceof FloatValue) {
+      return new FloatReplaceFunction((FloatValue)baseExpr,(FloatValue)compExpr,(FloatValue)fillExpr);
+    }
+    if (baseExpr instanceof FloatValueStream && compExpr instanceof FloatValue && fillExpr instanceof FloatValue) {
+      return new FloatStreamReplaceFunction((FloatValueStream)baseExpr,(FloatValue)compExpr,(FloatValue)fillExpr);
+    }
+    if (baseExpr instanceof DoubleValue && compExpr instanceof DoubleValue && fillExpr instanceof DoubleValue) {
+      return new DoubleReplaceFunction((DoubleValue)baseExpr,(DoubleValue)compExpr,(DoubleValue)fillExpr);
+    }
+    if (baseExpr instanceof DoubleValueStream && compExpr instanceof DoubleValue && fillExpr instanceof DoubleValue) {
+      return new DoubleStreamReplaceFunction((DoubleValueStream)baseExpr,(DoubleValue)compExpr,(DoubleValue)fillExpr);
+    }
+    if (baseExpr instanceof StringValue && compExpr instanceof StringValue && fillExpr instanceof StringValue) {
+      return new StringReplaceFunction((StringValue)baseExpr,(StringValue)compExpr,(StringValue)fillExpr);
+    }
+    if (baseExpr instanceof StringValueStream && compExpr instanceof StringValue && fillExpr instanceof StringValue) {
+      return new StringStreamReplaceFunction((StringValueStream)baseExpr,(StringValue)compExpr,(StringValue)fillExpr);
+    }
+    if (baseExpr instanceof AnalyticsValue) {
+      return new ValueReplaceFunction((AnalyticsValue)baseExpr,(AnalyticsValue)compExpr,(AnalyticsValue)fillExpr);
+    }
+    return new StreamReplaceFunction(baseExpr,compExpr,fillExpr); 
+    
+  });
+}
+class StreamReplaceFunction implements AnalyticsValueStream {
+  private final AnalyticsValueStream baseExpr;
+  private final AnalyticsValue compExpr;
+  private final AnalyticsValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StreamReplaceFunction(AnalyticsValueStream baseExpr, AnalyticsValue compExpr, AnalyticsValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    Object compValue = compExpr.getObject();
+    if (compExpr.exists()) {
+      Object fillValue = fillExpr.getObject();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamObjects(value -> {
+        if (value.equals(compValue)) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamObjects(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class ValueReplaceFunction extends AbstractAnalyticsValue {
+  private final AnalyticsValue baseExpr;
+  private final AnalyticsValue compExpr;
+  private final AnalyticsValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public ValueReplaceFunction(AnalyticsValue baseExpr, AnalyticsValue compExpr, AnalyticsValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public Object getObject() {
+    Object value = baseExpr.getObject();
+    exists = baseExpr.exists();
+    Object comp = compExpr.getObject();
+    if (value.equals(comp) && exists==compExpr.exists()) {
+      value = fillExpr.getObject();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanStreamReplaceFunction extends AbstractBooleanValueStream {
+  private final BooleanValueStream baseExpr;
+  private final BooleanValue compExpr;
+  private final BooleanValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanStreamReplaceFunction(BooleanValueStream baseExpr, BooleanValue compExpr, BooleanValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    boolean compValue = compExpr.getBoolean();
+    if (compExpr.exists()) {
+      boolean fillValue = fillExpr.getBoolean();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamBooleans(value -> {
+        if (value==compValue) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamBooleans(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanReplaceFunction extends AbstractBooleanValue {
+  private final BooleanValue baseExpr;
+  private final BooleanValue compExpr;
+  private final BooleanValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanReplaceFunction(BooleanValue baseExpr, BooleanValue compExpr, BooleanValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public boolean getBoolean() {
+    boolean value = baseExpr.getBoolean();
+    exists = baseExpr.exists();
+    boolean comp = compExpr.getBoolean();
+    if (value==comp && exists==compExpr.exists()) {
+      value = fillExpr.getBoolean();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntStreamReplaceFunction extends AbstractIntValueStream {
+  private final IntValueStream baseExpr;
+  private final IntValue compExpr;
+  private final IntValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntStreamReplaceFunction(IntValueStream baseExpr, IntValue compExpr, IntValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamInts(IntConsumer cons) {
+    int compValue = compExpr.getInt();
+    if (compExpr.exists()) {
+      int fillValue = fillExpr.getInt();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamInts(value -> {
+        if (value==compValue) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamInts(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntReplaceFunction extends AbstractIntValue {
+  private final IntValue baseExpr;
+  private final IntValue compExpr;
+  private final IntValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntReplaceFunction(IntValue baseExpr, IntValue compExpr, IntValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public int getInt() {
+    int value = baseExpr.getInt();
+    exists = baseExpr.exists();
+    int comp = compExpr.getInt();
+    if (value==comp && exists==compExpr.exists()) {
+      value = fillExpr.getInt();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongStreamReplaceFunction extends AbstractLongValueStream {
+  private final LongValueStream baseExpr;
+  private final LongValue compExpr;
+  private final LongValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongStreamReplaceFunction(LongValueStream baseExpr, LongValue compExpr, LongValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    long compValue = compExpr.getLong();
+    if (compExpr.exists()) {
+      long fillValue = fillExpr.getLong();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamLongs(value -> {
+        if (value==compValue) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamLongs(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongReplaceFunction extends AbstractLongValue {
+  private final LongValue baseExpr;
+  private final LongValue compExpr;
+  private final LongValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongReplaceFunction(LongValue baseExpr, LongValue compExpr, LongValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public long getLong() {
+    long value = baseExpr.getLong();
+    exists = baseExpr.exists();
+    long comp = compExpr.getLong();
+    if (value==comp && exists==compExpr.exists()) {
+      value = fillExpr.getLong();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatStreamReplaceFunction extends AbstractFloatValueStream {
+  private final FloatValueStream baseExpr;
+  private final FloatValue compExpr;
+  private final FloatValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatStreamReplaceFunction(FloatValueStream baseExpr, FloatValue compExpr, FloatValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    float compValue = compExpr.getFloat();
+    if (compExpr.exists()) {
+      float fillValue = fillExpr.getFloat();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamFloats(value -> {
+        if (value==compValue) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamFloats(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatReplaceFunction extends AbstractFloatValue {
+  private final FloatValue baseExpr;
+  private final FloatValue compExpr;
+  private final FloatValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatReplaceFunction(FloatValue baseExpr, FloatValue compExpr, FloatValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public float getFloat() {
+    float value = baseExpr.getFloat();
+    exists = baseExpr.exists();
+    float comp = compExpr.getFloat();
+    if (value==comp && exists==compExpr.exists()) {
+      value = fillExpr.getFloat();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DoubleStreamReplaceFunction extends AbstractDoubleValueStream {
+  private final DoubleValueStream baseExpr;
+  private final DoubleValue compExpr;
+  private final DoubleValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DoubleStreamReplaceFunction(DoubleValueStream baseExpr, DoubleValue compExpr, DoubleValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    double compValue = compExpr.getDouble();
+    if (compExpr.exists()) {
+      double fillValue = fillExpr.getDouble();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamDoubles(value -> {
+        if (value==compValue) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamDoubles(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DoubleReplaceFunction extends AbstractDoubleValue {
+  private final DoubleValue baseExpr;
+  private final DoubleValue compExpr;
+  private final DoubleValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DoubleReplaceFunction(DoubleValue baseExpr, DoubleValue compExpr, DoubleValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public double getDouble() {
+    double value = baseExpr.getDouble();
+    exists = baseExpr.exists();
+    double comp = compExpr.getDouble();
+    if (value==comp && exists==compExpr.exists()) {
+      value = fillExpr.getDouble();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DateStreamReplaceFunction extends AbstractDateValueStream {
+  private final DateValueStream baseExpr;
+  private final DateValue compExpr;
+  private final DateValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateStreamReplaceFunction(DateValueStream baseExpr, DateValue compExpr, DateValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    long compValue = compExpr.getLong();
+    if (compExpr.exists()) {
+      long fillValue = fillExpr.getLong();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamLongs(value -> {
+        if (value==compValue) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamLongs(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DateReplaceFunction extends AbstractDateValue {
+  private final DateValue baseExpr;
+  private final DateValue compExpr;
+  private final DateValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateReplaceFunction(DateValue baseExpr, DateValue compExpr, DateValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public long getLong() {
+    long value = baseExpr.getLong();
+    exists = baseExpr.exists();
+    long comp = compExpr.getLong();
+    if (value==comp && exists==compExpr.exists()) {
+      value = fillExpr.getLong();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringStreamReplaceFunction extends AbstractStringValueStream {
+  private final StringValueStream baseExpr;
+  private final StringValue compExpr;
+  private final StringValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StringStreamReplaceFunction(StringValueStream baseExpr, StringValue compExpr, StringValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    String compValue = compExpr.toString();
+    if (compExpr.exists()) {
+      String fillValue = fillExpr.toString();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamStrings(value -> {
+        if (value.equals(compValue)) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamStrings(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringReplaceFunction extends AbstractStringValue {
+  private final StringValue baseExpr;
+  private final StringValue compExpr;
+  private final StringValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StringReplaceFunction(StringValue baseExpr, StringValue compExpr, StringValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public String getString() {
+    String value = baseExpr.getString();
+    exists = baseExpr.exists();
+    String comp = compExpr.getString();
+    if (value.equals(comp) && exists==compExpr.exists()) {
+      value = fillExpr.getString();
+      exists = fillExpr.exists();
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
\ No newline at end of file


[12/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/StringCastFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/StringCastFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/StringCastFunction.java
new file mode 100644
index 0000000..5960c0e
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/StringCastFunction.java
@@ -0,0 +1,42 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A mapping function that casts any input to a {@link StringValue} or {@link StringValueStream}.
+ */
+public class StringCastFunction {
+  public static final String name = "string";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 1) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 1 paramater, " + params.length + " found.");
+    }
+    AnalyticsValueStream param = params[0];
+    if (param instanceof StringValueStream) {
+      return LambdaFunction.createStringLambdaFunction(name, a -> a, (StringValueStream)param);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a string-castable parameter.");
+    }
+  });
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/SubtractFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/SubtractFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/SubtractFunction.java
new file mode 100644
index 0000000..3a4e456
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/SubtractFunction.java
@@ -0,0 +1,51 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A subtraction mapping function.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If two numeric Values are passed in, a {@link DoubleValue} representing the first subtracted by the second is returned.
+ * <li>If a numeric ValueStream and a numeric Value are passed in, a {@link DoubleValueStream} representing the Value subtracted by
+ * each of the values of the ValueStream for a document is returned. 
+ * (Or the other way, since the Value and ValueStream can be used in either order)
+ * </ul>
+ */
+public class SubtractFunction {
+  public static final String name = "sub";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 2) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 2 paramaters, " + params.length + " found.");
+    }
+    AnalyticsValueStream param1 = params[0];
+    AnalyticsValueStream param2 = params[1];
+    if (param1 instanceof DoubleValueStream && param2 instanceof DoubleValueStream) {
+      return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> a-b, (DoubleValueStream)param1, (DoubleValueStream)param2);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires numeric parameters.");
+    }
+  });
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/TopFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/TopFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/TopFunction.java
new file mode 100644
index 0000000..801d1ea
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/TopFunction.java
@@ -0,0 +1,163 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValue;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValue;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A top mapping function, returning the highest value found.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If a single comparable ValueStream is passed in, a Value (of the same type) representing the maximum of the values for each document is returned.
+ * <li>If multiple comparable Values are passed in, a Value (of the same type) representing the maximum of all values is returned.
+ * </ul>
+ */
+public class TopFunction {
+  public static final String name = "top";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires paramaters.");
+    }
+    AnalyticsValueStream param = params[0];
+    if (param instanceof DateValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createDateLambdaFunction(name, (a,b) -> (a>b)? a:b, (DateValueStream)param);
+      }
+      DateValue[] castedParams = new DateValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof DateValue) {
+          castedParams[i] = (DateValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createDateLambdaFunction(name, (a,b) -> (a>b)? a:b, castedParams, false);
+      }
+    }
+    if (param instanceof IntValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createIntLambdaFunction(name, (a,b) -> (a>b)? a:b, (IntValueStream)param);
+      }
+      IntValue[] castedParams = new IntValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof IntValue) {
+          castedParams[i] = (IntValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createIntLambdaFunction(name, (a,b) -> (a>b)? a:b, castedParams, false);
+      }
+    }
+    if (param instanceof LongValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createLongLambdaFunction(name, (a,b) -> (a>b)? a:b, (LongValueStream)param);
+      }
+      LongValue[] castedParams = new LongValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof LongValue) {
+          castedParams[i] = (LongValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createLongLambdaFunction(name, (a,b) -> (a>b)? a:b, castedParams, false);
+      }
+    }
+    if (param instanceof FloatValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createFloatLambdaFunction(name, (a,b) -> (a>b)? a:b, (FloatValueStream)param);
+      }
+      FloatValue[] castedParams = new FloatValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof FloatValue) {
+          castedParams[i] = (FloatValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createFloatLambdaFunction(name, (a,b) -> (a>b)? a:b, castedParams, false);
+      }
+    }
+    if (param instanceof DoubleValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> (a>b)? a:b, (DoubleValueStream)param);
+      }
+      DoubleValue[] castedParams = new DoubleValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof DoubleValue) {
+          castedParams[i] = (DoubleValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> (a>b)? a:b, castedParams, false);
+      }
+    }
+    if (param instanceof StringValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createStringLambdaFunction(name, (a,b) -> (a.compareTo(b)>=0)? a:b, (StringValueStream)param);
+      }
+      StringValue[] castedParams = new StringValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof StringValue) {
+          castedParams[i] = (StringValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createStringLambdaFunction(name, (a,b) -> (a.compareTo(b)>=0)? a:b, castedParams, false);
+      }
+    }
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a comparable parameter. " + 
+          "Incorrect parameter: "+params[0].getExpressionStr());
+  });
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/package-info.java
new file mode 100644
index 0000000..3cf363b
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+ 
+/** 
+ * Mapping functions to use for analytics expressions.
+ */
+package org.apache.solr.analytics.function.mapping;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/package-info.java
new file mode 100644
index 0000000..e21dd9b
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+ 
+/** 
+ * Functions to use for analytics expressions.
+ */
+package org.apache.solr.analytics.function;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/CountFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/CountFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/CountFunction.java
new file mode 100644
index 0000000..a77f997
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/CountFunction.java
@@ -0,0 +1,87 @@
+/*
+ * 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.solr.analytics.function.reduction;
+
+import java.util.function.UnaryOperator;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.function.ReductionFunction;
+import org.apache.solr.analytics.function.reduction.data.CountCollector;
+import org.apache.solr.analytics.function.reduction.data.CountCollector.ExpressionCountCollector;
+import org.apache.solr.analytics.function.reduction.data.CountCollector.TotalCountCollector;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A reduction function which either counts the number of values that the parameter expression contains,
+ * or the number of documents returned if no parameter is given.
+ */
+public class CountFunction extends AbstractLongValue implements ReductionFunction {
+  private CountCollector collector;
+  public static final String name = "count";
+  private final String exprStr;
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length == 0) {
+      return new CountFunction();
+    }
+    if (params.length == 1) {
+      return new CountFunction(params[0]);
+    }
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function a max of 1 paramater, " + params.length + " given.");
+  });
+  
+  public CountFunction() {
+    this.collector = new TotalCountCollector();
+    this.exprStr = AnalyticsValueStream.createExpressionString(name);
+  }
+  
+  public CountFunction(AnalyticsValueStream param) {
+    this.collector = new ExpressionCountCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public long getLong() {
+    return collector.count();
+  }
+  @Override
+  public boolean exists() {
+    return true;
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (CountCollector)sync.apply(collector);
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/DocCountFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/DocCountFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/DocCountFunction.java
new file mode 100644
index 0000000..f009b59
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/DocCountFunction.java
@@ -0,0 +1,87 @@
+/*
+ * 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.solr.analytics.function.reduction;
+
+import java.util.function.UnaryOperator;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.function.ReductionFunction;
+import org.apache.solr.analytics.function.reduction.data.CountCollector;
+import org.apache.solr.analytics.function.reduction.data.CountCollector.ExpressionCountCollector;
+import org.apache.solr.analytics.function.reduction.data.CountCollector.TotalCountCollector;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A reduction function which either counts the number of Solr Documents for which the parameter expression exists,
+ * or the number of documents returned if no parameter is given.
+ */
+public class DocCountFunction extends AbstractLongValue implements ReductionFunction {
+  private CountCollector collector;
+  public static final String name = "docCount";
+  private final String exprStr;
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length == 0) {
+      return new DocCountFunction();
+    }
+    if (params.length == 1) {
+      return new DocCountFunction(params[0]);
+    }
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function accepts at most 1 paramater, " + params.length + " given.");
+  });
+  
+  public DocCountFunction() {
+    this.collector = new TotalCountCollector();
+    this.exprStr = AnalyticsValueStream.createExpressionString(name);
+  }
+  
+  public DocCountFunction(AnalyticsValueStream param) {
+    this.collector = new ExpressionCountCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public long getLong() {
+    return collector.docCount();
+  }
+  @Override
+  public boolean exists() {
+    return true;
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (CountCollector)sync.apply(collector);
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MaxFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MaxFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MaxFunction.java
new file mode 100644
index 0000000..f5ea12d
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MaxFunction.java
@@ -0,0 +1,298 @@
+/*
+ * 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.solr.analytics.function.reduction;
+
+import java.util.function.UnaryOperator;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.function.ReductionFunction;
+import org.apache.solr.analytics.function.reduction.data.MaxCollector.DoubleMaxCollector;
+import org.apache.solr.analytics.function.reduction.data.MaxCollector.FloatMaxCollector;
+import org.apache.solr.analytics.function.reduction.data.MaxCollector.IntMaxCollector;
+import org.apache.solr.analytics.function.reduction.data.MaxCollector.LongMaxCollector;
+import org.apache.solr.analytics.function.reduction.data.MaxCollector.StringMaxCollector;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DoubleValue.AbstractDoubleValue;
+import org.apache.solr.analytics.value.FloatValue.AbstractFloatValue;
+import org.apache.solr.analytics.value.IntValue.AbstractIntValue;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.value.StringValue.AbstractStringValue;
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A reduction function which returns the maximum value of the given expression.
+ */
+public class MaxFunction {
+  public static final String name = "max";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 1) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 1 paramater, " + params.length + " found.");
+    }
+    AnalyticsValueStream param = params[0];
+    if (param instanceof DateValueStream) {
+      return new DateMaxFunction((DateValueStream)param);
+    }
+    if (param instanceof IntValueStream) {
+      return new IntMaxFunction((IntValueStream)param);
+    }
+    if (param instanceof LongValueStream) {
+      return new LongMaxFunction((LongValueStream)param);
+    }
+    if (param instanceof FloatValueStream) {
+      return new FloatMaxFunction((FloatValueStream)param);
+    }
+    if (param instanceof DoubleValueStream) {
+      return new DoubleMaxFunction((DoubleValueStream)param);
+    }
+    if (param instanceof StringValueStream) {
+      return new StringMaxFunction((StringValueStream)param);
+    }
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a comparable parameter. " + 
+          "Incorrect parameter: "+params[0].getExpressionStr());
+  });
+}
+class IntMaxFunction extends AbstractIntValue implements ReductionFunction {
+  private IntMaxCollector collector;
+  public static final String name = MaxFunction.name;
+  private final String exprStr;
+  
+  public IntMaxFunction(IntValueStream param) {
+    this.collector = new IntMaxCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public int getInt() {
+    return collector.exists() ? collector.max() : 0;
+  }
+  @Override
+  public boolean exists() {
+    return collector.exists();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (IntMaxCollector)sync.apply(collector);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class LongMaxFunction extends AbstractLongValue implements ReductionFunction {
+  private LongMaxCollector collector;
+  public static final String name = MaxFunction.name;
+  private final String exprStr;
+  
+  public LongMaxFunction(LongValueStream param) {
+    this.collector = new LongMaxCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public long getLong() {
+    return collector.exists() ? collector.max() : 0;
+  }
+  @Override
+  public boolean exists() {
+    return collector.exists();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (LongMaxCollector)sync.apply(collector);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class FloatMaxFunction extends AbstractFloatValue implements ReductionFunction {
+  private FloatMaxCollector collector;
+  public static final String name = MaxFunction.name;
+  private final String exprStr;
+  
+  public FloatMaxFunction(FloatValueStream param) {
+    this.collector = new FloatMaxCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public float getFloat() {
+    return collector.exists() ? collector.max() : 0;
+  }
+  @Override
+  public boolean exists() {
+    return collector.exists();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (FloatMaxCollector)sync.apply(collector);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class DoubleMaxFunction extends AbstractDoubleValue implements ReductionFunction {
+  private DoubleMaxCollector collector;
+  public static final String name = MaxFunction.name;
+  private final String exprStr;
+  
+  public DoubleMaxFunction(DoubleValueStream param) {
+    this.collector = new DoubleMaxCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public double getDouble() {
+    return collector.exists() ? collector.max() : 0;
+  }
+  @Override
+  public boolean exists() {
+    return collector.exists();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (DoubleMaxCollector)sync.apply(collector);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class DateMaxFunction extends AbstractDateValue implements ReductionFunction {
+  private LongMaxCollector collector;
+  public static final String name = MaxFunction.name;
+  private final String exprStr;
+  
+  public DateMaxFunction(LongValueStream param) {
+    this.collector = new LongMaxCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public long getLong() {
+    return collector.exists() ? collector.max() : 0;
+  }
+  @Override
+  public boolean exists() {
+    return collector.exists();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (LongMaxCollector)sync.apply(collector);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class StringMaxFunction extends AbstractStringValue implements ReductionFunction {
+  private StringMaxCollector collector;
+  public static final String name = MaxFunction.name;
+  private final String exprStr;
+  
+  public StringMaxFunction(StringValueStream param) {
+    this.collector = new StringMaxCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public String getString() {
+    return collector.exists() ? collector.max() : null;
+  }
+  @Override
+  public boolean exists() {
+    return collector.exists();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (StringMaxCollector)sync.apply(collector);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MedianFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MedianFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MedianFunction.java
new file mode 100644
index 0000000..9a6fe40
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MedianFunction.java
@@ -0,0 +1,200 @@
+/*
+ * 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.solr.analytics.function.reduction;
+
+import java.util.function.UnaryOperator;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.function.ReductionFunction;
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.analytics.function.reduction.data.SortedListCollector;
+import org.apache.solr.analytics.function.reduction.data.SortedListCollector.SortedDoubleListCollector;
+import org.apache.solr.analytics.function.reduction.data.SortedListCollector.SortedFloatListCollector;
+import org.apache.solr.analytics.function.reduction.data.SortedListCollector.SortedIntListCollector;
+import org.apache.solr.analytics.function.reduction.data.SortedListCollector.SortedLongListCollector;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DoubleValue.AbstractDoubleValue;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A reduction function which returns the median value of the given expression.
+ */
+public class MedianFunction {
+  public static final String name = "median";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 1) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 1 paramater, " + params.length + " found.");
+    }
+    AnalyticsValueStream param = params[0];
+    if (param instanceof DateValueStream) {
+      return new DateMedianFunction((DateValueStream)param);
+    } else if (param instanceof IntValueStream) {
+      return new IntMedianFunction((IntValueStream)param);
+    } else if (param instanceof LongValueStream) {
+      return new LongMedianFunction((LongValueStream)param);
+    } else if (param instanceof FloatValueStream) {
+      return new FloatMedianFunction((FloatValueStream)param);
+    } else if (param instanceof DoubleValueStream) {
+      return new DoubleMedianFunction((DoubleValueStream)param);
+    }
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a date or numeric parameter.");
+  });
+}
+abstract class NumericMedianFunction<T extends Comparable<T>> extends AbstractDoubleValue implements ReductionFunction {
+  protected SortedListCollector<T> collector;
+  public static final String name = MedianFunction.name;
+  private final String exprStr;
+  
+  public NumericMedianFunction(DoubleValueStream param, SortedListCollector<T> collector) {
+    this.collector = collector;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  protected abstract double collectOrd(int ord);
+  
+  @Override
+  public double getDouble() {
+    int size = collector.size();
+    if (size == 0) {
+      return 0;
+    }
+    if (size % 2 == 0) {
+      return (collectOrd(size/2) + collectOrd(size/2 - 1))/2;
+    } else {
+      return collectOrd(size/2);
+    }
+  }
+  @Override
+  public boolean exists() {
+    return collector.size() > 0;
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (SortedListCollector<T>)sync.apply(collector);
+    collector.calcMedian();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class IntMedianFunction extends NumericMedianFunction<Integer> {
+  public IntMedianFunction(IntValueStream param) {
+    super((DoubleValueStream) param, new SortedIntListCollector(param));
+  }
+
+  @Override
+  protected double collectOrd(int ord) {
+    return collector.get(ord);
+  }
+}
+class LongMedianFunction extends NumericMedianFunction<Long> {
+  public LongMedianFunction(LongValueStream param) {
+    super((DoubleValueStream) param, new SortedLongListCollector(param));
+  }
+
+  @Override
+  protected double collectOrd(int ord) {
+    return collector.get(ord);
+  }
+}
+class FloatMedianFunction extends NumericMedianFunction<Float> {
+  public FloatMedianFunction(FloatValueStream param) {
+    super((DoubleValueStream) param, new SortedFloatListCollector(param));
+  }
+
+  @Override
+  protected double collectOrd(int ord) {
+    return collector.get(ord);
+  }
+}
+class DoubleMedianFunction extends NumericMedianFunction<Double> {
+  public DoubleMedianFunction(DoubleValueStream param) {
+    super(param, new SortedDoubleListCollector(param));
+  }
+
+  @Override
+  protected double collectOrd(int ord) {
+    return collector.get(ord);
+  }
+}
+class DateMedianFunction extends AbstractDateValue implements ReductionFunction {
+  private SortedLongListCollector collector;
+  public static final String name = MedianFunction.name;
+  private final String exprStr;
+  
+  public DateMedianFunction(DateValueStream param) {
+    this.collector = new SortedLongListCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public long getLong() {
+    int size = collector.size();
+    if (size == 0) {
+      return 0;
+    }
+    if (size % 2 == 0) {
+      return (collector.get(size/2) + collector.get(size/2 - 1))/2;
+    } else {
+      return collector.get(size/2);
+    }
+  }
+  @Override
+  public boolean exists() {
+    return collector.size() > 0;
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (SortedLongListCollector)sync.apply(collector);
+    collector.calcMedian();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MinFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MinFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MinFunction.java
new file mode 100644
index 0000000..92539d7
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MinFunction.java
@@ -0,0 +1,298 @@
+/*
+ * 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.solr.analytics.function.reduction;
+
+import java.util.function.UnaryOperator;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.function.ReductionFunction;
+import org.apache.solr.analytics.function.reduction.data.MinCollector.DoubleMinCollector;
+import org.apache.solr.analytics.function.reduction.data.MinCollector.FloatMinCollector;
+import org.apache.solr.analytics.function.reduction.data.MinCollector.IntMinCollector;
+import org.apache.solr.analytics.function.reduction.data.MinCollector.LongMinCollector;
+import org.apache.solr.analytics.function.reduction.data.MinCollector.StringMinCollector;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DoubleValue.AbstractDoubleValue;
+import org.apache.solr.analytics.value.FloatValue.AbstractFloatValue;
+import org.apache.solr.analytics.value.IntValue.AbstractIntValue;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.value.StringValue.AbstractStringValue;
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A reduction function which returns the minumum value of the given expression.
+ */
+public class MinFunction {
+  public static final String name = "min";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 1) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 1 paramater, " + params.length + " found.");
+    }
+    AnalyticsValueStream param = params[0];
+    if (param instanceof DateValueStream) {
+      return new DateMinFunction((DateValueStream)param);
+    }
+    if (param instanceof IntValueStream) {
+      return new IntMinFunction((IntValueStream)param);
+    }
+    if (param instanceof LongValueStream) {
+      return new LongMinFunction((LongValueStream)param);
+    }
+    if (param instanceof FloatValueStream) {
+      return new FloatMinFunction((FloatValueStream)param);
+    }
+    if (param instanceof DoubleValueStream) {
+      return new DoubleMinFunction((DoubleValueStream)param);
+    }
+    if (param instanceof StringValueStream) {
+      return new StringMinFunction((StringValueStream)param);
+    }
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a comparable parameter. " + 
+          "Incorrect parameter: "+params[0].getExpressionStr());
+  });
+}
+class IntMinFunction extends AbstractIntValue implements ReductionFunction {
+  private IntMinCollector collector;
+  public static final String name = MinFunction.name;
+  private final String exprStr;
+  
+  public IntMinFunction(IntValueStream param) {
+    this.collector = new IntMinCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public int getInt() {
+    return collector.exists() ? collector.min() : 0;
+  }
+  @Override
+  public boolean exists() {
+    return collector.exists();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (IntMinCollector)sync.apply(collector);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class LongMinFunction extends AbstractLongValue implements ReductionFunction {
+  private LongMinCollector collector;
+  public static final String name = MinFunction.name;
+  private final String exprStr;
+  
+  public LongMinFunction(LongValueStream param) {
+    this.collector = new LongMinCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public long getLong() {
+    return collector.exists() ? collector.min() : 0;
+  }
+  @Override
+  public boolean exists() {
+    return collector.exists();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (LongMinCollector)sync.apply(collector);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class FloatMinFunction extends AbstractFloatValue implements ReductionFunction {
+  private FloatMinCollector collector;
+  public static final String name = MinFunction.name;
+  private final String exprStr;
+  
+  public FloatMinFunction(FloatValueStream param) {
+    this.collector = new FloatMinCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public float getFloat() {
+    return collector.exists() ? collector.min() : 0;
+  }
+  @Override
+  public boolean exists() {
+    return collector.exists();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (FloatMinCollector)sync.apply(collector);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class DoubleMinFunction extends AbstractDoubleValue implements ReductionFunction {
+  private DoubleMinCollector collector;
+  public static final String name = MinFunction.name;
+  private final String exprStr;
+  
+  public DoubleMinFunction(DoubleValueStream param) {
+    this.collector = new DoubleMinCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public double getDouble() {
+    return collector.exists() ? collector.min() : 0;
+  }
+  @Override
+  public boolean exists() {
+    return collector.exists();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (DoubleMinCollector)sync.apply(collector);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class DateMinFunction extends AbstractDateValue implements ReductionFunction {
+  private LongMinCollector collector;
+  public static final String name = MinFunction.name;
+  private final String exprStr;
+  
+  public DateMinFunction(LongValueStream param) {
+    this.collector = new LongMinCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public long getLong() {
+    return collector.exists() ? collector.min() : 0;
+  }
+  @Override
+  public boolean exists() {
+    return collector.exists();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (LongMinCollector)sync.apply(collector);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class StringMinFunction extends AbstractStringValue implements ReductionFunction {
+  private StringMinCollector collector;
+  public static final String name = MinFunction.name;
+  private final String exprStr;
+  
+  public StringMinFunction(StringValueStream param) {
+    this.collector = new StringMinCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public String getString() {
+    return collector.exists() ? collector.min() : null;
+  }
+  @Override
+  public boolean exists() {
+    return collector.exists();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (StringMinCollector)sync.apply(collector);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MissingFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MissingFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MissingFunction.java
new file mode 100644
index 0000000..5fa7e64
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/MissingFunction.java
@@ -0,0 +1,76 @@
+/*
+ * 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.solr.analytics.function.reduction;
+
+import java.util.function.UnaryOperator;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.function.ReductionFunction;
+import org.apache.solr.analytics.function.reduction.data.CountCollector.ExpressionCountCollector;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A reduction function which returns the number of documents for which the given expression does not exist.
+ */
+public class MissingFunction extends AbstractLongValue implements ReductionFunction {
+  private ExpressionCountCollector collector;
+  public static final String name = "missing";
+  private final String exprStr;
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 1) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 1 paramater, " + params.length + " found.");
+    }
+    return new MissingFunction(params[0]);
+  });
+  
+  public MissingFunction(AnalyticsValueStream param) {
+    this.collector = new ExpressionCountCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public long getLong() {
+    return collector.missing();
+  }
+  @Override
+  public boolean exists() {
+    return true;
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (ExpressionCountCollector)sync.apply(collector);
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/OrdinalFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/OrdinalFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/OrdinalFunction.java
new file mode 100644
index 0000000..0b86cdf
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/OrdinalFunction.java
@@ -0,0 +1,386 @@
+/*
+ * 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.solr.analytics.function.reduction;
+
+import java.util.Locale;
+import java.util.function.UnaryOperator;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.function.ReductionFunction;
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.analytics.function.reduction.data.SortedListCollector.SortedDoubleListCollector;
+import org.apache.solr.analytics.function.reduction.data.SortedListCollector.SortedFloatListCollector;
+import org.apache.solr.analytics.function.reduction.data.SortedListCollector.SortedIntListCollector;
+import org.apache.solr.analytics.function.reduction.data.SortedListCollector.SortedLongListCollector;
+import org.apache.solr.analytics.function.reduction.data.SortedListCollector.SortedStringListCollector;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValue;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DoubleValue.AbstractDoubleValue;
+import org.apache.solr.analytics.value.FloatValue.AbstractFloatValue;
+import org.apache.solr.analytics.value.IntValue.AbstractIntValue;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.value.StringValue.AbstractStringValue;
+import org.apache.solr.analytics.value.constant.ConstantIntValue;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A reduction function which returns the given ordinal of the sorted values of the given expression.
+ */
+public class OrdinalFunction {
+  public static final String name = "ordinal";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 2) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 2 paramater, " + params.length + " found.");
+    }
+    AnalyticsValueStream percValue = params[0];
+    int ord = 0;
+    if (params[0] instanceof ConstantIntValue) {
+      ord = ((IntValue)percValue).getInt();
+      if (ord == 0) {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires the ordinal to be >= 1 or <= -1, 0 is not accepted.");
+      }
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a constant int value (the ordinal) as the first argument.");
+    }
+    AnalyticsValueStream param = params[1];
+    if (param instanceof DateValueStream) {
+      return new DateOrdinalFunction((DateValueStream)param, ord);
+    }else if (param instanceof IntValueStream) {
+      return new IntOrdinalFunction((IntValueStream)param, ord);
+    } else if (param instanceof LongValueStream) {
+      return new LongOrdinalFunction((LongValueStream)param, ord);
+    } else if (param instanceof FloatValueStream) {
+      return new FloatOrdinalFunction((FloatValueStream)param, ord);
+    } else if (param instanceof DoubleValueStream) {
+      return new DoubleOrdinalFunction((DoubleValueStream)param, ord);
+    } else if (param instanceof StringValueStream) {
+      return new StringOrdinalFunction((StringValueStream)param, ord);
+    } 
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a comparable parameter.");
+  });
+  
+  protected static String createOrdinalExpressionString(AnalyticsValueStream param, double perc) {
+    return String.format(Locale.ROOT, "%s(%s,%s)",
+                         name,
+                         (int)(perc*100),
+                         param.getExpressionStr());
+  }
+}
+class IntOrdinalFunction extends AbstractIntValue implements ReductionFunction {
+  private SortedIntListCollector collector;
+  private int ordinal;
+  public static final String name = OrdinalFunction.name;
+  private final String exprStr;
+  
+  public IntOrdinalFunction(IntValueStream param, int ordinal) {
+    this.collector = new SortedIntListCollector(param);
+    this.ordinal = ordinal;
+    this.exprStr = OrdinalFunction.createOrdinalExpressionString(param, ordinal);
+  }
+
+  @Override
+  public int getInt() {
+    int size = collector.size();
+    if (ordinal > 0) {
+      return ordinal <= size ? collector.get(ordinal - 1) : 0;
+    } else {
+      return (ordinal * -1) <= size ? collector.get(size + ordinal) : 0;
+    }
+    
+  }
+  @Override
+  public boolean exists() {
+    return (ordinal > 0 ? ordinal : (ordinal * -1)) <= collector.size();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (SortedIntListCollector)sync.apply(collector);
+    if (ordinal > 0) {
+      collector.calcOrdinal(ordinal);
+    } else {
+      collector.calcReverseOrdinal(ordinal*-1);
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class LongOrdinalFunction extends AbstractLongValue implements ReductionFunction {
+  private SortedLongListCollector collector;
+  private int ordinal;
+  public static final String name = OrdinalFunction.name;
+  private final String exprStr;
+  
+  public LongOrdinalFunction(LongValueStream param, int ordinal) {
+    this.collector = new SortedLongListCollector(param);
+    this.ordinal = ordinal;
+    this.exprStr = OrdinalFunction.createOrdinalExpressionString(param, ordinal);
+  }
+
+  @Override
+  public long getLong() {
+    int size = collector.size();
+    if (ordinal > 0) {
+      return ordinal <= size ? collector.get(ordinal - 1) : 0;
+    } else {
+      return (ordinal * -1) <= size ? collector.get(size + ordinal) : 0;
+    }
+    
+  }
+  @Override
+  public boolean exists() {
+    return (ordinal > 0 ? ordinal : (ordinal * -1)) <= collector.size();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (SortedLongListCollector)sync.apply(collector);
+    if (ordinal > 0) {
+      collector.calcOrdinal(ordinal);
+    } else {
+      collector.calcReverseOrdinal(ordinal*-1);
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class FloatOrdinalFunction extends AbstractFloatValue implements ReductionFunction {
+  private SortedFloatListCollector collector;
+  private int ordinal;
+  public static final String name = OrdinalFunction.name;
+  private final String exprStr;
+  
+  public FloatOrdinalFunction(FloatValueStream param, int ordinal) {
+    this.collector = new SortedFloatListCollector(param);
+    this.ordinal = ordinal;
+    this.exprStr = OrdinalFunction.createOrdinalExpressionString(param, ordinal);
+  }
+
+  @Override
+  public float getFloat() {
+    int size = collector.size();
+    if (ordinal > 0) {
+      return ordinal <= size ? collector.get(ordinal - 1) : 0;
+    } else {
+      return (ordinal * -1) <= size ? collector.get(size + ordinal) : 0;
+    }
+    
+  }
+  @Override
+  public boolean exists() {
+    return (ordinal > 0 ? ordinal : (ordinal * -1)) <= collector.size();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (SortedFloatListCollector)sync.apply(collector);
+    if (ordinal > 0) {
+      collector.calcOrdinal(ordinal);
+    } else {
+      collector.calcReverseOrdinal(ordinal*-1);
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class DoubleOrdinalFunction extends AbstractDoubleValue implements ReductionFunction {
+  private SortedDoubleListCollector collector;
+  private int ordinal;
+  public static final String name = OrdinalFunction.name;
+  private final String exprStr;
+  
+  public DoubleOrdinalFunction(DoubleValueStream param, int ordinal) {
+    this.collector = new SortedDoubleListCollector(param);
+    this.ordinal = ordinal;
+    this.exprStr = OrdinalFunction.createOrdinalExpressionString(param, ordinal);
+  }
+
+  @Override
+  public double getDouble() {
+    int size = collector.size();
+    if (ordinal > 0) {
+      return ordinal <= size ? collector.get(ordinal - 1) : 0;
+    } else {
+      return (ordinal * -1) <= size ? collector.get(size + ordinal) : 0;
+    }
+    
+  }
+  @Override
+  public boolean exists() {
+    return (ordinal > 0 ? ordinal : (ordinal * -1)) <= collector.size();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (SortedDoubleListCollector)sync.apply(collector);
+    if (ordinal > 0) {
+      collector.calcOrdinal(ordinal);
+    } else {
+      collector.calcReverseOrdinal(ordinal*-1);
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class DateOrdinalFunction extends AbstractDateValue implements ReductionFunction {
+  private SortedLongListCollector collector;
+  private int ordinal;
+  public static final String name = OrdinalFunction.name;
+  private final String exprStr;
+  
+  public DateOrdinalFunction(LongValueStream param, int ordinal) {
+    this.collector = new SortedLongListCollector(param);
+    this.ordinal = ordinal;
+    this.exprStr = OrdinalFunction.createOrdinalExpressionString(param, ordinal);
+  }
+
+  @Override
+  public long getLong() {
+    int size = collector.size();
+    if (ordinal > 0) {
+      return ordinal <= size ? collector.get(ordinal - 1) : 0;
+    } else {
+      return (ordinal * -1) <= size ? collector.get(size + ordinal) : 0;
+    }
+    
+  }
+  @Override
+  public boolean exists() {
+    return (ordinal > 0 ? ordinal : (ordinal * -1)) <= collector.size();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (SortedLongListCollector)sync.apply(collector);
+    collector.calcOrdinal(ordinal);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class StringOrdinalFunction extends AbstractStringValue implements ReductionFunction {
+  private SortedStringListCollector collector;
+  private int ordinal;
+  public static final String name = OrdinalFunction.name;
+  private final String exprStr;
+  
+  public StringOrdinalFunction(StringValueStream param, int ordinal) {
+    this.collector = new SortedStringListCollector(param);
+    this.ordinal = ordinal;
+    this.exprStr = OrdinalFunction.createOrdinalExpressionString(param, ordinal);
+  }
+
+  @Override
+  public String getString() {
+    int size = collector.size();
+    if (ordinal > 0) {
+      return ordinal <= size ? collector.get(ordinal - 1) : null;
+    } else {
+      return (ordinal * -1) <= size ? collector.get(size + ordinal) : null;
+    }
+    
+  }
+  @Override
+  public boolean exists() {
+    return (ordinal > 0 ? ordinal : (ordinal * -1)) <= collector.size();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (SortedStringListCollector)sync.apply(collector);
+    if (ordinal > 0) {
+      collector.calcOrdinal(ordinal);
+    } else {
+      collector.calcReverseOrdinal(ordinal*-1);
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/PercentileFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/PercentileFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/PercentileFunction.java
new file mode 100644
index 0000000..4277d90
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/PercentileFunction.java
@@ -0,0 +1,337 @@
+/*
+ * 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.solr.analytics.function.reduction;
+
+import java.util.Locale;
+import java.util.function.UnaryOperator;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.function.ReductionFunction;
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.analytics.function.reduction.data.SortedListCollector.SortedDoubleListCollector;
+import org.apache.solr.analytics.function.reduction.data.SortedListCollector.SortedFloatListCollector;
+import org.apache.solr.analytics.function.reduction.data.SortedListCollector.SortedIntListCollector;
+import org.apache.solr.analytics.function.reduction.data.SortedListCollector.SortedLongListCollector;
+import org.apache.solr.analytics.function.reduction.data.SortedListCollector.SortedStringListCollector;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValue;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DoubleValue.AbstractDoubleValue;
+import org.apache.solr.analytics.value.FloatValue.AbstractFloatValue;
+import org.apache.solr.analytics.value.IntValue.AbstractIntValue;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.value.StringValue.AbstractStringValue;
+import org.apache.solr.analytics.value.constant.ConstantIntValue;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A reduction function which returns the given percentile of the sorted values of the given expression.
+ */
+public class PercentileFunction {
+  public static final String name = "percentile";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 2) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 2 paramater, " + params.length + " found.");
+    }
+    AnalyticsValueStream percValue = params[0];
+    double perc = 0;
+    if (percValue instanceof ConstantIntValue) {
+      perc = ((IntValue)percValue).getInt();
+      if (perc < 0 || perc > 99) {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a percentile between 0 and 99, " + ((int)perc) + " found.");
+      }
+      perc /= 100;
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a constant int value (the percentile) as the first argument.");
+    }
+    AnalyticsValueStream param = params[1];
+    if (param instanceof DateValueStream) {
+      return new DatePercentileFunction((DateValueStream)param, perc);
+    }else if (param instanceof IntValueStream) {
+      return new IntPercentileFunction((IntValueStream)param, perc);
+    } else if (param instanceof LongValueStream) {
+      return new LongPercentileFunction((LongValueStream)param, perc);
+    } else if (param instanceof FloatValueStream) {
+      return new FloatPercentileFunction((FloatValueStream)param, perc);
+    } else if (param instanceof DoubleValueStream) {
+      return new DoublePercentileFunction((DoubleValueStream)param, perc);
+    } else if (param instanceof StringValueStream) {
+      return new StringPercentileFunction((StringValueStream)param, perc);
+    } 
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a comparable parameter.");
+  });
+  
+  protected static String createPercentileExpressionString(AnalyticsValueStream param, double perc) {
+    return String.format(Locale.ROOT, "%s(%s,%s)",
+                         name,
+                         (int)(perc*100),
+                         param.getExpressionStr());
+  }
+}
+class IntPercentileFunction extends AbstractIntValue implements ReductionFunction {
+  private SortedIntListCollector collector;
+  private double percentile;
+  public static final String name = PercentileFunction.name;
+  private final String exprStr;
+  
+  public IntPercentileFunction(IntValueStream param, double percentile) {
+    this.collector = new SortedIntListCollector(param);
+    this.percentile = percentile;
+    this.exprStr = PercentileFunction.createPercentileExpressionString(param, percentile);
+  }
+
+  @Override
+  public int getInt() {
+    int size = collector.size();
+    return size > 0 ? collector.get((int) Math.round(percentile * size - .5)) : 0;
+  }
+  @Override
+  public boolean exists() {
+    return collector.size() > 0;
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (SortedIntListCollector)sync.apply(collector);
+    collector.calcPercentile(percentile);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class LongPercentileFunction extends AbstractLongValue implements ReductionFunction {
+  private SortedLongListCollector collector;
+  private double percentile;
+  public static final String name = PercentileFunction.name;
+  private final String exprStr;
+  
+  public LongPercentileFunction(LongValueStream param, double percentile) {
+    this.collector = new SortedLongListCollector(param);
+    this.percentile = percentile;
+    this.exprStr = PercentileFunction.createPercentileExpressionString(param, percentile);
+  }
+
+  @Override
+  public long getLong() {
+    int size = collector.size();
+    return size > 0 ? collector.get((int) Math.round(percentile * size - .5)) : 0;
+  }
+  @Override
+  public boolean exists() {
+    return collector.size() > 0;
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (SortedLongListCollector)sync.apply(collector);
+    collector.calcPercentile(percentile);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class FloatPercentileFunction extends AbstractFloatValue implements ReductionFunction {
+  private SortedFloatListCollector collector;
+  private double percentile;
+  public static final String name = PercentileFunction.name;
+  private final String exprStr;
+  
+  public FloatPercentileFunction(FloatValueStream param, double percentile) {
+    this.collector = new SortedFloatListCollector(param);
+    this.percentile = percentile;
+    this.exprStr = PercentileFunction.createPercentileExpressionString(param, percentile);
+  }
+
+  @Override
+  public float getFloat() {
+    int size = collector.size();
+    return size > 0 ? collector.get((int) Math.round(percentile * size - .5)) : 0;
+  }
+  @Override
+  public boolean exists() {
+    return collector.size() > 0;
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (SortedFloatListCollector)sync.apply(collector);
+    collector.calcPercentile(percentile);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class DoublePercentileFunction extends AbstractDoubleValue implements ReductionFunction {
+  private SortedDoubleListCollector collector;
+  private double percentile;
+  public static final String name = PercentileFunction.name;
+  private final String exprStr;
+  
+  public DoublePercentileFunction(DoubleValueStream param, double percentile) {
+    this.collector = new SortedDoubleListCollector(param);
+    this.percentile = percentile;
+    this.exprStr = PercentileFunction.createPercentileExpressionString(param, percentile);
+  }
+
+  @Override
+  public double getDouble() {
+    int size = collector.size();
+    return size > 0 ? collector.get((int) Math.round(percentile * size - .5)) : 0;
+  }
+  @Override
+  public boolean exists() {
+    return collector.size() > 0;
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (SortedDoubleListCollector)sync.apply(collector);
+    collector.calcPercentile(percentile);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class DatePercentileFunction extends AbstractDateValue implements ReductionFunction {
+  private SortedLongListCollector collector;
+  private double percentile;
+  public static final String name = PercentileFunction.name;
+  private final String exprStr;
+  
+  public DatePercentileFunction(LongValueStream param, double percentile) {
+    this.collector = new SortedLongListCollector(param);
+    this.percentile = percentile;
+    this.exprStr = PercentileFunction.createPercentileExpressionString(param, percentile);
+  }
+
+  @Override
+  public long getLong() {
+    int size = collector.size();
+    return size > 0 ? collector.get((int) Math.round(percentile * size - .5)) : 0;
+  }
+  @Override
+  public boolean exists() {
+    return collector.size() > 0;
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (SortedLongListCollector)sync.apply(collector);
+    collector.calcPercentile(percentile);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
+class StringPercentileFunction extends AbstractStringValue implements ReductionFunction {
+  private SortedStringListCollector collector;
+  private double percentile;
+  public static final String name = PercentileFunction.name;
+  private final String exprStr;
+  
+  public StringPercentileFunction(StringValueStream param, double percentile) {
+    this.collector = new SortedStringListCollector(param);
+    this.percentile = percentile;
+    this.exprStr = PercentileFunction.createPercentileExpressionString(param, percentile);
+  }
+
+  @Override
+  public String getString() {
+    int size = collector.size();
+    return size > 0 ? collector.get((int) Math.round(percentile * size - .5)) : null;
+  }
+  @Override
+  public boolean exists() {
+    return collector.size() > 0;
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (SortedStringListCollector)sync.apply(collector);
+    collector.calcPercentile(percentile);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/SumFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/SumFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/SumFunction.java
new file mode 100644
index 0000000..7d4e939
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/reduction/SumFunction.java
@@ -0,0 +1,92 @@
+/*
+ * 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.solr.analytics.function.reduction;
+
+import java.io.Serializable;
+import java.util.function.UnaryOperator;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.function.ReductionFunction;
+import org.apache.solr.analytics.function.reduction.data.ReductionData;
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.analytics.function.reduction.data.SumCollector;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.DoubleValue.AbstractDoubleValue;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A reduction function which returns the sum of the sorted values of the given expression.
+ */
+public class SumFunction extends AbstractDoubleValue implements ReductionFunction {
+  private SumCollector collector;
+  public static final String name = "sum";
+  private final String exprStr;
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 1) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 1 paramater, " + params.length + " found.");
+    }
+    DoubleValueStream casted;
+    try {
+      casted = (DoubleValueStream) params[0];
+    }
+    catch (ClassCastException e) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires numeric parameter. " + 
+          "Incorrect parameter: "+params[0].getExpressionStr());
+    }
+    return new SumFunction(casted);
+  });
+  
+  public SumFunction(DoubleValueStream param) {
+    this.collector = new SumCollector(param);
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+  }
+
+  @Override
+  public double getDouble() {
+    return collector.sum();
+  }
+  @Override
+  public boolean exists() {
+    return collector.exists();
+  }
+
+  @Override
+  public void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync) {
+    collector = (SumCollector) sync.apply(collector);
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.REDUCTION;
+  }
+
+  protected static class SumData extends ReductionData implements Serializable {
+    private static final long serialVersionUID = 5920718235872898338L;
+    double sum;
+  }
+}
\ No newline at end of file


[09/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/StatsCollectorSupplierFactory.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/StatsCollectorSupplierFactory.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/StatsCollectorSupplierFactory.java
deleted file mode 100644
index e22362d..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/StatsCollectorSupplierFactory.java
+++ /dev/null
@@ -1,646 +0,0 @@
-/*
- * 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.solr.analytics.statistics;
-
-import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.function.Supplier;
-
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.queries.function.valuesource.BytesRefFieldSource;
-import org.apache.lucene.queries.function.valuesource.DoubleFieldSource;
-import org.apache.lucene.queries.function.valuesource.FloatFieldSource;
-import org.apache.lucene.queries.function.valuesource.IntFieldSource;
-import org.apache.lucene.queries.function.valuesource.LongFieldSource;
-import org.apache.solr.analytics.expression.ExpressionFactory;
-import org.apache.solr.analytics.request.ExpressionRequest;
-import org.apache.solr.analytics.util.AnalyticsParams;
-import org.apache.solr.analytics.util.valuesource.AbsoluteValueDoubleFunction;
-import org.apache.solr.analytics.util.valuesource.AddDoubleFunction;
-import org.apache.solr.analytics.util.valuesource.ConstDateSource;
-import org.apache.solr.analytics.util.valuesource.ConstDoubleSource;
-import org.apache.solr.analytics.util.valuesource.ConstStringSource;
-import org.apache.solr.analytics.util.valuesource.DateFieldSource;
-import org.apache.solr.analytics.util.valuesource.DateMathFunction;
-import org.apache.solr.analytics.util.valuesource.DivDoubleFunction;
-import org.apache.solr.analytics.util.valuesource.DualDoubleFunction;
-import org.apache.solr.analytics.util.valuesource.FilterFieldSource;
-import org.apache.solr.analytics.util.valuesource.LogDoubleFunction;
-import org.apache.solr.analytics.util.valuesource.MultiDateFunction;
-import org.apache.solr.analytics.util.valuesource.MultiDoubleFunction;
-import org.apache.solr.analytics.util.valuesource.MultiplyDoubleFunction;
-import org.apache.solr.analytics.util.valuesource.NegateDoubleFunction;
-import org.apache.solr.analytics.util.valuesource.PowDoubleFunction;
-import org.apache.solr.analytics.util.valuesource.ReverseStringFunction;
-import org.apache.solr.analytics.util.valuesource.SingleDoubleFunction;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.SolrException.ErrorCode;
-import org.apache.solr.schema.FieldType;
-import org.apache.solr.schema.IndexSchema;
-import org.apache.solr.schema.SchemaField;
-import org.apache.solr.schema.StrField;
-import org.apache.solr.schema.TrieDateField;
-import org.apache.solr.schema.TrieDoubleField;
-import org.apache.solr.schema.TrieFloatField;
-import org.apache.solr.schema.TrieIntField;
-import org.apache.solr.schema.TrieLongField;
-import org.apache.solr.search.function.ConcatStringFunction;
-import org.apache.solr.util.DateMathParser;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class StatsCollectorSupplierFactory {
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-  
-  // FunctionTypes
-  final static int NUMBER_TYPE = 0;
-  final static int DATE_TYPE = 1;
-  final static int STRING_TYPE = 2;
-  final static int FIELD_TYPE = 3;
-  final static int FILTER_TYPE = 4;
-  
-  /**
-   * Builds a Supplier that will generate identical arrays of new StatsCollectors.
-   * 
-   * @param schema The Schema being used.
-   * @param exRequests The expression requests to generate a StatsCollector[] from.
-   * @return A Supplier that will return an array of new StatsCollector.
-   */
-  @SuppressWarnings("unchecked")
-  public static Supplier<StatsCollector[]> create(IndexSchema schema, List<ExpressionRequest> exRequests ) {
-    final Map<String, Set<String>> collectorStats =  new TreeMap<>();
-    final Map<String, Set<Integer>> collectorPercs =  new TreeMap<>();
-    final Map<String, ValueSource> collectorSources =  new TreeMap<>();
-    
-    // Iterate through all expression request to make a list of ValueSource strings
-    // and statistics that need to be calculated on those ValueSources.
-    for (ExpressionRequest expRequest : exRequests) {
-      String statExpression = expRequest.getExpressionString();
-      Set<String> statistics = getStatistics(statExpression);
-      if (statistics == null) {
-        continue;
-      }
-      for (String statExp : statistics) {
-        String stat;
-        String operands;
-        try {
-          stat = statExp.substring(0, statExp.indexOf('(')).trim();
-          operands = statExp.substring(statExp.indexOf('(')+1, statExp.lastIndexOf(')')).trim();
-        } catch (Exception e) {
-          throw new SolrException(ErrorCode.BAD_REQUEST,"Unable to parse statistic: ["+statExpression+"]",e);
-        }
-        String[] arguments = ExpressionFactory.getArguments(operands);
-        String source = arguments[0];
-        if (stat.equals(AnalyticsParams.STAT_PERCENTILE)) {
-          // The statistic is a percentile, extra parsing is required
-          if (arguments.length<2) {
-            throw new SolrException(ErrorCode.BAD_REQUEST,"Too few arguments given for "+stat+"() in ["+statExp+"].");
-          } else if (arguments.length>2) {
-            throw new SolrException(ErrorCode.BAD_REQUEST,"Too many arguments given for "+stat+"() in ["+statExp+"].");
-          }
-          source = arguments[1];
-          Set<Integer> percs = collectorPercs.get(source);
-          if (percs == null) {
-            percs = new HashSet<>();
-            collectorPercs.put(source, percs);
-          }
-          try {
-            int perc = Integer.parseInt(arguments[0]);
-            if (perc>0 && perc<100) {
-              percs.add(perc);
-            } else {
-              throw new SolrException(ErrorCode.BAD_REQUEST,"The percentile in ["+statExp+"] is not between 0 and 100, exculsive.");
-            }
-          } catch (NumberFormatException e) {
-            throw new SolrException(ErrorCode.BAD_REQUEST,"\""+arguments[0]+"\" cannot be converted into a percentile.",e);
-          }
-        } else if (arguments.length>1) {
-          throw new SolrException(ErrorCode.BAD_REQUEST,"Too many arguments given for "+stat+"() in ["+statExp+"].");
-        } else if (arguments.length==0) {
-          throw new SolrException(ErrorCode.BAD_REQUEST,"No arguments given for "+stat+"() in ["+statExp+"].");
-        } 
-        // Only unique ValueSources will be made; therefore statistics must be accumulated for
-        // each ValueSource, even across different expression requests
-        Set<String> stats = collectorStats.get(source);
-        if (stats == null) {
-          stats = new HashSet<>();
-          collectorStats.put(source, stats);
-        }
-        if(AnalyticsParams.STAT_PERCENTILE.equals(stat)) {
-          stats.add(stat + "_"+ arguments[0]);
-        } else {
-          stats.add(stat);
-        }
-      }
-    }
-    String[] keys = collectorStats.keySet().toArray(new String[0]);
-    for (String sourceStr : keys) {
-      // Build one ValueSource for each unique value source string
-      ValueSource source = buildSourceTree(schema, sourceStr);
-      if (source == null) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"The statistic ["+sourceStr+"] could not be parsed.");
-      }
-      String builtString = source.toString();
-      collectorSources.put(builtString,source);
-      // Replace the user given string with the correctly built string
-      if (!builtString.equals(sourceStr)) {
-        Set<String> stats = collectorStats.remove(sourceStr);
-        if (stats!=null) {
-          collectorStats.put(builtString, stats);
-        }
-        Set<Integer> percs = collectorPercs.remove(sourceStr);
-        if (percs!=null) {
-          collectorPercs.put(builtString, percs);
-        }
-        for (ExpressionRequest er : exRequests) {
-          er.setExpressionString(er.getExpressionString().replace(sourceStr, builtString));
-        }
-      }
-    }
-    if (collectorSources.size()==0) {
-      return new Supplier<StatsCollector[]>() {
-        @Override
-        public StatsCollector[] get() {
-          return new StatsCollector[0];
-        }
-      };
-    }
-    
-    log.info("Stats objects: "+collectorStats.size()+" sr="+collectorSources.size()+" pr="+collectorPercs.size() );
-    
-    // All information is stored in final arrays so that nothing 
-    // has to be computed when the Supplier's get() method is called.
-    final Set<String>[] statsArr = collectorStats.values().toArray(new Set[0]);
-    final ValueSource[] sourceArr = collectorSources.values().toArray(new ValueSource[0]);
-    final boolean[] uniqueBools = new boolean[statsArr.length];
-    final boolean[] medianBools = new boolean[statsArr.length];
-    final boolean[] numericBools = new boolean[statsArr.length];
-    final boolean[] dateBools = new boolean[statsArr.length];
-    final double[][] percsArr = new double[statsArr.length][];
-    final String[][] percsNames = new String[statsArr.length][];
-    for (int count = 0; count < sourceArr.length; count++) {
-      uniqueBools[count] = statsArr[count].contains(AnalyticsParams.STAT_UNIQUE);
-      medianBools[count] = statsArr[count].contains(AnalyticsParams.STAT_MEDIAN);
-      numericBools[count] = statsArr[count].contains(AnalyticsParams.STAT_SUM)||statsArr[count].contains(AnalyticsParams.STAT_SUM_OF_SQUARES)||statsArr[count].contains(AnalyticsParams.STAT_MEAN)||statsArr[count].contains(AnalyticsParams.STAT_STANDARD_DEVIATION);
-      dateBools[count] = (sourceArr[count] instanceof DateFieldSource) | (sourceArr[count] instanceof MultiDateFunction) | (sourceArr[count] instanceof ConstDateSource);
-      Set<Integer> ps = collectorPercs.get(sourceArr[count].toString());
-      if (ps!=null) {
-        percsArr[count] = new double[ps.size()];
-        percsNames[count] = new String[ps.size()];
-        int percCount = 0;
-        for (int p : ps) {
-          percsArr[count][percCount] = p/100.0;
-          percsNames[count][percCount++] = AnalyticsParams.STAT_PERCENTILE+"_"+p;
-        }
-      }
-    }
-    // Making the Supplier
-    return new Supplier<StatsCollector[]>() {
-      public StatsCollector[] get() {
-        StatsCollector[] collectors = new StatsCollector[statsArr.length];
-        for (int count = 0; count < statsArr.length; count++) {
-          if(numericBools[count]){
-            StatsCollector sc = new NumericStatsCollector(sourceArr[count], statsArr[count]);
-            if(uniqueBools[count]) sc = new UniqueStatsCollector(sc);
-            if(medianBools[count]) sc = new MedianStatsCollector(sc);
-            if(percsArr[count]!=null) sc = new PercentileStatsCollector(sc,percsArr[count],percsNames[count]);
-            collectors[count]=sc;
-          } else if (dateBools[count]) {
-            StatsCollector sc = new MinMaxStatsCollector(sourceArr[count], statsArr[count]);
-            if(uniqueBools[count]) sc = new UniqueStatsCollector(sc);
-            if(medianBools[count]) sc = new DateMedianStatsCollector(sc);
-            if(percsArr[count]!=null) sc = new PercentileStatsCollector(sc,percsArr[count],percsNames[count]);
-           collectors[count]=sc;
-          } else {
-            StatsCollector sc = new MinMaxStatsCollector(sourceArr[count], statsArr[count]);
-            if(uniqueBools[count]) sc = new UniqueStatsCollector(sc);
-            if(medianBools[count]) sc = new MedianStatsCollector(sc);
-            if(percsArr[count]!=null) sc = new PercentileStatsCollector(sc,percsArr[count],percsNames[count]);
-            collectors[count]=sc;
-          }
-        }
-        return collectors;
-      }
-    };
-  }
-  
-  /**
-   * Finds the set of statistics that must be computed for the expression.
-   * @param expression The string representation of an expression
-   * @return The set of statistics (sum, mean, median, etc.) found in the expression
-   */
-  public static Set<String> getStatistics(String expression) {
-    HashSet<String> set = new HashSet<>();
-    int firstParen = expression.indexOf('(');
-    if (firstParen>0) {
-      String topOperation = expression.substring(0,firstParen).trim();
-      if (AnalyticsParams.ALL_STAT_SET.contains(topOperation)) {
-        set.add(expression);
-      } else if (!(topOperation.equals(AnalyticsParams.CONSTANT_NUMBER)||topOperation.equals(AnalyticsParams.CONSTANT_DATE)||topOperation.equals(AnalyticsParams.CONSTANT_STRING))) {
-        String operands = expression.substring(firstParen+1, expression.lastIndexOf(')')).trim();
-        String[] arguments = ExpressionFactory.getArguments(operands);
-        for (String argument : arguments) {
-          Set<String> more = getStatistics(argument);
-          if (more!=null) {
-            set.addAll(more);
-          }
-        }
-      }
-    }
-    if (set.size()==0) {
-      return null;
-    }
-    return set;
-  }
-  
-  /**
-   * Builds a Value Source from a given string
-   * 
-   * @param schema The schema being used.
-   * @param expression The string to be turned into an expression.
-   * @return The completed ValueSource
-   */
-  private static ValueSource buildSourceTree(IndexSchema schema, String expression) {
-    return buildSourceTree(schema,expression,FIELD_TYPE);
-  }
-  
-  /**
-   * Builds a Value Source from a given string and a given source type
-   * 
-   * @param schema The schema being used.
-   * @param expression The string to be turned into an expression.
-   * @param sourceType The type of source that must be returned.
-   * @return The completed ValueSource
-   */
-  private static ValueSource buildSourceTree(IndexSchema schema, String expression, int sourceType) {
-    int expressionType = getSourceType(expression);
-    if (sourceType != FIELD_TYPE && expressionType != FIELD_TYPE && 
-        expressionType != FILTER_TYPE && expressionType != sourceType) {
-      return null;
-    }
-    switch (expressionType) {
-    case NUMBER_TYPE : return buildNumericSource(schema, expression);
-    case DATE_TYPE : return buildDateSource(schema, expression);
-    case STRING_TYPE : return buildStringSource(schema, expression);
-    case FIELD_TYPE : return buildFieldSource(schema, expression, sourceType);
-    case FILTER_TYPE : return buildFilterSource(schema, expression.substring(expression.indexOf('(')+1,expression.lastIndexOf(')')), sourceType);
-    default : throw new SolrException(ErrorCode.BAD_REQUEST,expression+" is not a valid operation.");
-    }
-  }
-
-  /**
-   * Determines what type of value source the expression represents.
-   * 
-   * @param expression The expression representing the desired ValueSource
-   * @return NUMBER_TYPE, DATE_TYPE, STRING_TYPE or -1
-   */
-  private static int getSourceType(String expression) {
-    int paren = expression.indexOf('(');
-    if (paren<0) {
-      return FIELD_TYPE;
-    }
-    String operation = expression.substring(0,paren).trim();
-
-    if (AnalyticsParams.NUMERIC_OPERATION_SET.contains(operation)) {
-      return NUMBER_TYPE;
-    } else if (AnalyticsParams.DATE_OPERATION_SET.contains(operation)) {
-      return DATE_TYPE;
-    } else if (AnalyticsParams.STRING_OPERATION_SET.contains(operation)) {
-      return STRING_TYPE;
-    } else if (operation.equals(AnalyticsParams.FILTER)) {
-      return FILTER_TYPE;
-    }
-    throw new SolrException(ErrorCode.BAD_REQUEST,"The operation \""+operation+"\" in ["+expression+"] is not supported.");
-  }
-  
-  /**
-   *  Builds a value source for a given field, making sure that the field fits a given source type.
-   * @param schema the schema
-   * @param expressionString The name of the field to build a Field Source from.
-   * @param sourceType FIELD_TYPE for any type of field, NUMBER_TYPE for numeric fields, 
-   * DATE_TYPE for date fields and STRING_TYPE for string fields.
-   * @return a value source
-   */
-  private static ValueSource buildFieldSource(IndexSchema schema, String expressionString, int sourceType) {
-    SchemaField sf;
-    try {
-      sf = schema.getField(expressionString);
-    } catch (SolrException e) {
-      throw new SolrException(ErrorCode.BAD_REQUEST,"The field "+expressionString+" does not exist.",e);
-    }
-    FieldType type = sf.getType();
-    if ( type instanceof TrieIntField) {
-      if (sourceType!=NUMBER_TYPE&&sourceType!=FIELD_TYPE) {
-        return null;
-      }
-      return new IntFieldSource(expressionString) {
-        public String description() {
-          return field;
-        }
-      };
-    } else if (type instanceof TrieLongField) {
-      if (sourceType!=NUMBER_TYPE&&sourceType!=FIELD_TYPE) {
-        return null;
-      }
-      return new LongFieldSource(expressionString) {
-        public String description() {
-          return field;
-        }
-      };
-    } else if (type instanceof TrieFloatField) {
-      if (sourceType!=NUMBER_TYPE&&sourceType!=FIELD_TYPE) {
-        return null;
-      }
-      return new FloatFieldSource(expressionString) {
-        public String description() {
-          return field;
-        }
-      };
-    } else if (type instanceof TrieDoubleField) {
-      if (sourceType!=NUMBER_TYPE&&sourceType!=FIELD_TYPE) {
-        return null;
-      }
-      return new DoubleFieldSource(expressionString) {
-        public String description() {
-          return field;
-        }
-      };
-    } else if (type instanceof TrieDateField) {
-      if (sourceType!=DATE_TYPE&&sourceType!=FIELD_TYPE) {
-        return null;
-      }
-      return new DateFieldSource(expressionString) {
-        public String description() {
-          return field;
-        }
-      };
-    } else if (type instanceof StrField) {
-      if (sourceType!=STRING_TYPE&&sourceType!=FIELD_TYPE) {
-        return null;
-      }
-      return new BytesRefFieldSource(expressionString) {
-        public String description() {
-          return field;
-        }
-      };
-    }
-    throw new SolrException(ErrorCode.BAD_REQUEST, type.toString()+" is not a supported field type in Solr Analytics.");
-  }
-  
-  /**
-   * Builds a default is missing source that wraps a given source. A missing value is required for all 
-   * non-field value sources.
-   * @param schema the schema
-   * @param expressionString The name of the field to build a Field Source from.
-   * @param sourceType FIELD_TYPE for any type of field, NUMBER_TYPE for numeric fields, 
-   * DATE_TYPE for date fields and STRING_TYPE for string fields.
-   * @return a value source
-   */
-  @SuppressWarnings("deprecation")
-  private static ValueSource buildFilterSource(IndexSchema schema, String expressionString, int sourceType) {
-    String[] arguments = ExpressionFactory.getArguments(expressionString);
-    if (arguments.length!=2) {
-      throw new SolrException(ErrorCode.BAD_REQUEST,"Invalid arguments were given for \""+AnalyticsParams.FILTER+"\".");
-    }
-    ValueSource delegateSource = buildSourceTree(schema, arguments[0], sourceType);
-    if (delegateSource==null) {
-      return null;
-    }
-    Object defaultObject;
-
-    ValueSource src = delegateSource;
-    if (delegateSource instanceof FilterFieldSource) {
-      src = ((FilterFieldSource)delegateSource).getRootSource();
-    }
-    if ( src instanceof IntFieldSource) {
-      try {
-        defaultObject = new Integer(arguments[1]);
-      } catch (NumberFormatException e) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"The filter value "+arguments[1]+" cannot be converted into an integer.",e);
-      }
-    } else if ( src instanceof DateFieldSource || src instanceof MultiDateFunction) {
-      defaultObject = DateMathParser.parseMath(null, arguments[1]);
-    } else if ( src instanceof LongFieldSource ) {
-      try {
-        defaultObject = new Long(arguments[1]);
-      } catch (NumberFormatException e) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"The filter value "+arguments[1]+" cannot be converted into a long.",e);
-      }
-    } else if ( src instanceof FloatFieldSource ) {
-      try {
-        defaultObject = new Float(arguments[1]);
-      } catch (NumberFormatException e) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"The filter value "+arguments[1]+" cannot be converted into a float.",e);
-      }
-    } else if ( src instanceof DoubleFieldSource || src instanceof SingleDoubleFunction ||
-                src instanceof DualDoubleFunction|| src instanceof MultiDoubleFunction) {
-      try {
-        defaultObject = new Double(arguments[1]);
-      } catch (NumberFormatException e) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"The filter value "+arguments[1]+" cannot be converted into a double.",e);
-      }
-    } else {
-      defaultObject = arguments[1];
-    }
-    return new FilterFieldSource(delegateSource,defaultObject);
-  } 
-  
-  /**
-   * Recursively parses and breaks down the expression string to build a numeric ValueSource.
-   * 
-   * @param schema The schema to pull fields from.
-   * @param expressionString The expression string to build a ValueSource from.
-   * @return The value source represented by the given expressionString
-   */
-  private static ValueSource buildNumericSource(IndexSchema schema, String expressionString) {
-    int paren = expressionString.indexOf('(');
-    String[] arguments;
-    String operands;
-    if (paren<0) {
-      return buildFieldSource(schema,expressionString,NUMBER_TYPE);
-    } else {
-      try {
-        operands = expressionString.substring(paren+1, expressionString.lastIndexOf(')')).trim();
-      } catch (Exception e) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"Missing closing parenthesis in ["+expressionString+"]");
-      }
-      arguments = ExpressionFactory.getArguments(operands);
-    }
-    String operation = expressionString.substring(0, paren).trim();
-    if (operation.equals(AnalyticsParams.CONSTANT_NUMBER)) {
-      if (arguments.length!=1) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"The constant number declaration ["+expressionString+"] does not have exactly 1 argument.");
-      }
-      return new ConstDoubleSource(Double.parseDouble(arguments[0]));
-    } else if (operation.equals(AnalyticsParams.NEGATE)) {
-      if (arguments.length!=1) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"The negate operation ["+expressionString+"] does not have exactly 1 argument.");
-      }
-      ValueSource argSource = buildNumericSource(schema, arguments[0]);
-      if (argSource==null) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"The operation \""+AnalyticsParams.NEGATE+"\" requires a numeric field or operation as argument. \""+arguments[0]+"\" is not a numeric field or operation.");
-      }
-      return new NegateDoubleFunction(argSource);
-    }  else if (operation.equals(AnalyticsParams.ABSOLUTE_VALUE)) {
-      if (arguments.length!=1) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"The absolute value operation ["+expressionString+"] does not have exactly 1 argument.");
-      }
-      ValueSource argSource = buildNumericSource(schema, arguments[0]);
-      if (argSource==null) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"The operation \""+AnalyticsParams.NEGATE+"\" requires a numeric field or operation as argument. \""+arguments[0]+"\" is not a numeric field or operation.");
-      }
-      return new AbsoluteValueDoubleFunction(argSource);
-    } else if (operation.equals(AnalyticsParams.FILTER)) {
-      return buildFilterSource(schema, operands, NUMBER_TYPE);
-    }
-    List<ValueSource> subExpressions = new ArrayList<>();
-    for (String argument : arguments) {
-      ValueSource argSource = buildNumericSource(schema, argument);
-      if (argSource == null) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"The operation \""+operation+"\" requires numeric fields or operations as arguments. \""+argument+"\" is not a numeric field or operation.");
-      }
-      subExpressions.add(argSource);
-    }
-    if (operation.equals(AnalyticsParams.ADD)) {
-      return new AddDoubleFunction(subExpressions.toArray(new ValueSource[0]));
-    } else if (operation.equals(AnalyticsParams.MULTIPLY)) {
-      return new MultiplyDoubleFunction(subExpressions.toArray(new ValueSource[0]));
-    } else if (operation.equals(AnalyticsParams.DIVIDE)) {
-      if (subExpressions.size()!=2) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"The divide operation ["+expressionString+"] does not have exactly 2 arguments.");
-      }
-      return new DivDoubleFunction(subExpressions.get(0),subExpressions.get(1));
-    } else if (operation.equals(AnalyticsParams.POWER)) {
-      if (subExpressions.size()!=2) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"The power operation ["+expressionString+"] does not have exactly 2 arguments.");
-      }
-      return new PowDoubleFunction(subExpressions.get(0),subExpressions.get(1));
-    } else if (operation.equals(AnalyticsParams.LOG)) {
-      if (subExpressions.size()!=2) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"The log operation ["+expressionString+"] does not have exactly 2 arguments.");
-      }
-      return new LogDoubleFunction(subExpressions.get(0), subExpressions.get(1));
-    } 
-    if (AnalyticsParams.DATE_OPERATION_SET.contains(operation)||AnalyticsParams.STRING_OPERATION_SET.contains(operation)) {
-      return null;
-    }
-    throw new SolrException(ErrorCode.BAD_REQUEST,"The operation ["+expressionString+"] is not supported.");
-  }
-
-  
-  /**
-   * Recursively parses and breaks down the expression string to build a date ValueSource.
-   * 
-   * @param schema The schema to pull fields from.
-   * @param expressionString The expression string to build a ValueSource from.
-   * @return The value source represented by the given expressionString
-   */
-  @SuppressWarnings("deprecation")
-  private static ValueSource buildDateSource(IndexSchema schema, String expressionString) {
-    int paren = expressionString.indexOf('(');
-    String[] arguments;
-    if (paren<0) {
-      return buildFieldSource(schema, expressionString, DATE_TYPE);
-    } else {
-      arguments = ExpressionFactory.getArguments(expressionString.substring(paren+1, expressionString.lastIndexOf(')')).trim());
-    }
-    String operands = arguments[0];
-    String operation = expressionString.substring(0, paren).trim();
-    if (operation.equals(AnalyticsParams.CONSTANT_DATE)) {
-      if (arguments.length!=1) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"The constant date declaration ["+expressionString+"] does not have exactly 1 argument.");
-      }
-      return new ConstDateSource(DateMathParser.parseMath(null, operands));
-    } else if (operation.equals(AnalyticsParams.FILTER)) {
-      return buildFilterSource(schema, operands, DATE_TYPE);
-    }
-    if (operation.equals(AnalyticsParams.DATE_MATH)) {
-      List<ValueSource> subExpressions = new ArrayList<>();
-      boolean first = true;
-      for (String argument : arguments) {
-        ValueSource argSource;
-        if (first) {
-          first = false;
-          argSource = buildDateSource(schema, argument);
-          if (argSource == null) {
-            throw new SolrException(ErrorCode.BAD_REQUEST,"\""+AnalyticsParams.DATE_MATH+"\" requires the first argument be a date operation or field. ["+argument+"] is not a date operation or field.");
-          }
-        } else {
-          argSource = buildStringSource(schema, argument);
-          if (argSource == null) {
-            throw new SolrException(ErrorCode.BAD_REQUEST,"\""+AnalyticsParams.DATE_MATH+"\" requires that all arguments except the first be string operations. ["+argument+"] is not a string operation.");
-          }
-        }
-        subExpressions.add(argSource);
-      }
-      return new DateMathFunction(subExpressions.toArray(new ValueSource[0]));
-    }
-    if (AnalyticsParams.NUMERIC_OPERATION_SET.contains(operation)||AnalyticsParams.STRING_OPERATION_SET.contains(operation)) {
-      return null;
-    }
-    throw new SolrException(ErrorCode.BAD_REQUEST,"The operation ["+expressionString+"] is not supported.");
-  }
-
-  
-  /**
-   * Recursively parses and breaks down the expression string to build a string ValueSource.
-   * 
-   * @param schema The schema to pull fields from.
-   * @param expressionString The expression string to build a ValueSource from.
-   * @return The value source represented by the given expressionString
-   */
-  private static ValueSource buildStringSource(IndexSchema schema, String expressionString) {
-    int paren = expressionString.indexOf('(');
-    String[] arguments;
-    if (paren<0) {
-      return buildFieldSource(schema, expressionString, FIELD_TYPE);
-    } else {
-      arguments = ExpressionFactory.getArguments(expressionString.substring(paren+1, expressionString.lastIndexOf(')')).trim());
-    }
-    String operands = arguments[0];
-    String operation = expressionString.substring(0, paren).trim();
-    if (operation.equals(AnalyticsParams.CONSTANT_STRING)) {
-      operands = expressionString.substring(paren+1, expressionString.lastIndexOf(')'));
-      return new ConstStringSource(operands);
-    } else if (operation.equals(AnalyticsParams.FILTER)) {
-      return buildFilterSource(schema,operands,FIELD_TYPE);
-    } else if (operation.equals(AnalyticsParams.REVERSE)) {
-      if (arguments.length!=1) {
-        throw new SolrException(ErrorCode.BAD_REQUEST,"\""+AnalyticsParams.REVERSE+"\" requires exactly one argument. The number of arguments in "+expressionString+" is not 1.");
-      }
-      return new ReverseStringFunction(buildStringSource(schema, operands));
-    }
-    List<ValueSource> subExpressions = new ArrayList<>();
-    for (String argument : arguments) {
-      subExpressions.add(buildSourceTree(schema, argument));
-    }
-    if (operation.equals(AnalyticsParams.CONCATENATE)) {
-      return new ConcatStringFunction(subExpressions.toArray(new ValueSource[0]));
-    } 
-    if (AnalyticsParams.NUMERIC_OPERATION_SET.contains(operation)) {
-      return buildNumericSource(schema, expressionString);
-    } else if (AnalyticsParams.DATE_OPERATION_SET.contains(operation)) {
-      return buildDateSource(schema, expressionString);
-    }
-    throw new SolrException(ErrorCode.BAD_REQUEST,"The operation ["+expressionString+"] is not supported.");
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/UniqueStatsCollector.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/UniqueStatsCollector.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/UniqueStatsCollector.java
deleted file mode 100644
index 461b0f4..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/UniqueStatsCollector.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.solr.analytics.statistics;
-
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * <code>UniqueValueCounter</code> computes the number of unique values.
- */
-public class UniqueStatsCollector extends AbstractDelegatingStatsCollector{
-  private final Set<Object> uniqueValues = new HashSet<>();
-  
-  public UniqueStatsCollector(StatsCollector delegate) {
-    super(delegate);
-  }
-  
-  @Override
-  public void collect(int doc) throws IOException {
-    super.collect(doc);
-    if (value.exists) {
-      uniqueValues.add(value.toObject());
-    }
-  }
-
-  @Override
-  public Comparable getStat(String stat) {
-    if (stat.equals("unique")) {
-      return new Long(uniqueValues.size());
-    }
-    return delegate.getStat(stat);
-  }
-
-  @Override
-  public void compute() {
-    delegate.compute();
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/package-info.java
deleted file mode 100644
index 90fa12d..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/statistics/package-info.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.
- */
- 
-/** 
- * Statistics collectors reduce a list of Objects to a single value. Most implementations reduce a list to a statistic on that list.
- */
-package org.apache.solr.analytics.statistics;
-
-
-

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/AnalyticsShardRequestManager.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/AnalyticsShardRequestManager.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/AnalyticsShardRequestManager.java
new file mode 100644
index 0000000..f65e58f
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/AnalyticsShardRequestManager.java
@@ -0,0 +1,245 @@
+/*
+ * 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.solr.analytics.stream;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import org.apache.solr.analytics.AnalyticsRequestManager;
+import org.apache.solr.analytics.AnalyticsRequestParser;
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.impl.HttpSolrClient;
+import org.apache.solr.client.solrj.impl.CloudSolrClient.Builder;
+import org.apache.solr.client.solrj.request.QueryRequest;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.ZkCoreNodeProps;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.ExecutorUtil;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.common.util.SolrjNamedThreadFactory;
+import org.apache.solr.handler.AnalyticsHandler;
+import org.apache.solr.handler.component.AnalyticsComponent;
+import org.apache.solr.response.AnalyticsShardResponseWriter;
+
+/**
+ * This class manages the requesting of shard responses from all shards in the queried collection.
+ * 
+ * <p>
+ * Shard Requests are sent to the {@link AnalyticsHandler} instead of the {@link AnalyticsComponent},
+ * which is the entrance to the analytics component for all client requests.
+ */
+public class AnalyticsShardRequestManager {
+  private final SolrParams params;
+  protected transient CloudSolrClient cloudSolrClient;
+  protected transient List<String> replicaUrls;
+  
+  /**
+   * All shards responses, which are received in parallel, are funneled into the manager.
+   * So the manager must be transient.
+   */
+  private transient final AnalyticsRequestManager manager;
+
+  public AnalyticsShardRequestManager(SolrParams params, AnalyticsRequestManager manager) {
+    this.manager = manager;
+    this.params = loadParams(params, manager.analyticsRequest);
+  }
+
+  /**
+   * Send out shard requests to each shard in the given collection.
+   * 
+   * @param collection that is being queried
+   * @param zkHost of the solr cloud hosting the collection
+   * @throws IOException if an exception occurs while picking shards or sending requests
+   */
+  public void sendRequests(String collection, String zkHost) throws IOException {
+    this.replicaUrls = new ArrayList<>();
+    this.cloudSolrClient = new Builder()
+        .withZkHost(zkHost)
+        .build();
+    try {
+      this.cloudSolrClient.connect();
+      pickShards(collection);
+      streamFromShards();
+    } finally {
+      cloudSolrClient.close();
+    }
+  }
+  
+  /**
+   * Pick one replica from each shard to send the shard requests to.
+   * 
+   * @param collection that is being queried
+   * @throws IOException if an exception occurs while finding replicas
+   */
+  protected void pickShards(String collection) throws IOException {
+    try {
+
+      ZkStateReader zkStateReader = cloudSolrClient.getZkStateReader();
+      ClusterState clusterState = zkStateReader.getClusterState();
+      Set<String> liveNodes = clusterState.getLiveNodes();
+
+      Collection<Slice> slices = clusterState.getCollection(collection).getActiveSlices();
+
+      for(Slice slice : slices) {
+        Collection<Replica> replicas = slice.getReplicas();
+        List<Replica> shuffler = new ArrayList<>();
+        for(Replica replica : replicas) {
+          if(replica.getState() == Replica.State.ACTIVE && liveNodes.contains(replica.getNodeName()))
+          shuffler.add(replica);
+        }
+
+        Collections.shuffle(shuffler, new Random());
+        Replica rep = shuffler.get(0);
+        ZkCoreNodeProps zkProps = new ZkCoreNodeProps(rep);
+        String url = zkProps.getCoreUrl();
+        replicaUrls.add(url);
+      }
+    } catch (Exception e) {
+      throw new IOException(e);
+    }
+  }
+
+  /**
+   * Send a shard request to each chosen replica, streaming 
+   * the responses back to the {@link AnalyticsRequestManager}
+   * through the {@link AnalyticsShardResponseParser}.
+   * <p>
+   * A thread pool is used to send the requests simultaneously, 
+   * and therefore importing the results is also done in parallel.
+   * However the manager can only import one shard response at a time,
+   * so the {@link AnalyticsShardResponseParser} is blocked until each import is finished.
+   * 
+   * @throws IOException if an exception occurs while sending requests.
+   */
+  private void streamFromShards() throws IOException {
+    ExecutorService service = ExecutorUtil.newMDCAwareCachedThreadPool(new SolrjNamedThreadFactory("SolrAnalyticsStream"));
+    List<Future<SolrException>> futures = new ArrayList<>();
+    List<AnalyticsShardRequester> openers = new ArrayList<>();
+    for (String replicaUrl : replicaUrls) {
+      AnalyticsShardRequester opener = new AnalyticsShardRequester(replicaUrl);
+      openers.add(opener);
+      Future<SolrException> future = service.submit(opener);
+      futures.add(future);
+    }
+    try {
+      for (Future<SolrException> f : futures) {
+        SolrException e = f.get();
+        if (e != null) {
+          throw e;
+        }
+      }
+    } catch (InterruptedException e1) {
+      throw new RuntimeException(e1);
+    } catch (ExecutionException e1) {
+      throw new RuntimeException(e1);
+    } finally {
+      service.shutdown();
+      for (AnalyticsShardRequester opener : openers) {
+        opener.close();
+      }
+    }
+  }
+
+  /**
+   * Create a {@link SolrParams} for shard requests. The only parameters that are copied over from
+   * the original search request are "q" and "fq".
+   * 
+   * <p>
+   * The request is sent to the {@link AnalyticsHandler} and the output will be encoded in the analytics bit-stream
+   * format generated by the {@link AnalyticsShardResponseWriter}.
+   * 
+   * @param paramsIn of the original solr request
+   * @param analyticsRequest string representation
+   * @return shard request SolrParams
+   */
+  private static SolrParams loadParams(SolrParams paramsIn, String analyticsRequest) {
+    ModifiableSolrParams solrParams = new ModifiableSolrParams();
+
+    solrParams.add(CommonParams.QT, AnalyticsHandler.NAME);
+    solrParams.add(CommonParams.WT, AnalyticsShardResponseWriter.NAME);
+    solrParams.add(CommonParams.Q, paramsIn.get(CommonParams.Q));
+    solrParams.add(CommonParams.FQ, paramsIn.getParams(CommonParams.FQ));
+    solrParams.add(AnalyticsRequestParser.analyticsParamName, analyticsRequest);
+
+    return solrParams;
+  }
+
+  /**
+   * A class that opens a connection to a given solr instance, a selected replica of the queried collection,
+   * and sends a analytics request to the {@link AnalyticsHandler}. The results are processed by an
+   * {@link AnalyticsShardResponseParser} constructed with the {@link AnalyticsRequestManager} passed
+   * to the parent {@link AnalyticsShardRequestManager}.
+   */
+  protected class AnalyticsShardRequester implements Callable<SolrException> {
+    private String baseUrl;
+    HttpSolrClient client;
+
+    /**
+     * Create a requester for analytics shard data.
+     * 
+     * @param baseUrl of the replica to send the request to
+     */
+    public AnalyticsShardRequester(String baseUrl) {
+      this.baseUrl = baseUrl;
+      this.client = null;
+    }
+    
+    /**
+     * Send the analytics request to the shard.
+     */
+    @Override
+    public SolrException call() throws Exception {
+      client = new HttpSolrClient.Builder(baseUrl).build();
+      QueryRequest query = new QueryRequest( params );
+      query.setPath(AnalyticsHandler.NAME);
+      query.setResponseParser(new AnalyticsShardResponseParser(manager));
+      query.setMethod(SolrRequest.METHOD.POST);
+      NamedList<Object> exception = client.request(query);
+      if (exception.size() > 0) {
+        return (SolrException)exception.getVal(0);
+      }
+      return null;
+    }
+    
+    /**
+     * Close the connection to the solr instance.
+     * 
+     * @throws IOException if an error occurs while closing the connection
+     */
+    public void close() throws IOException {
+      if (client != null) {
+        client.close();
+      }
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/AnalyticsShardResponseParser.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/AnalyticsShardResponseParser.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/AnalyticsShardResponseParser.java
new file mode 100644
index 0000000..c7f4094
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/AnalyticsShardResponseParser.java
@@ -0,0 +1,89 @@
+/*
+ * 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.solr.analytics.stream;
+
+import org.apache.solr.analytics.AnalyticsRequestManager;
+import org.apache.solr.client.solrj.ResponseParser;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.handler.AnalyticsHandler;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.Reader;
+
+/**
+ * This parser initiates a merge of an Analytics Shard Response, sent from the {@link AnalyticsHandler}.
+ * 
+ * The input stream is immediately sent to the given {@link AnalyticsRequestManager} to merge.
+ */
+public class AnalyticsShardResponseParser extends ResponseParser {
+  public static final String BINARY_CONTENT_TYPE = "application/octet-stream";
+  public static final String STREAM = "application/octet-stream";
+  
+  private final AnalyticsRequestManager manager;
+
+  /**
+   * 
+   * @param manager the manager of the current Analytics Request, will manage the merging of shard data
+   */
+  public AnalyticsShardResponseParser(AnalyticsRequestManager manager) {
+    this.manager = manager;
+  }
+
+  @Override
+  public String getWriterType() {
+    return "analytics_shard_stream";
+  }
+
+  @Override
+  public NamedList<Object> processResponse(InputStream body, String encoding) {
+    DataInputStream input = new DataInputStream(body);
+    //check to see if the response is an exception
+    NamedList<Object> exception = new NamedList<>();
+    try {
+      if (input.readBoolean()) {
+        manager.importShardData(input);
+      } else {
+        exception.add("Exception", new ObjectInputStream(input).readObject());
+      }
+    } catch (IOException e) {
+      exception.add("Exception", new SolrException(ErrorCode.SERVER_ERROR, "Couldn't process analytics shard response", e));
+    } catch (ClassNotFoundException e1) {
+      throw new RuntimeException(e1);
+    }
+    return exception;
+  }
+
+  @Override
+  public String getContentType() {
+    return BINARY_CONTENT_TYPE;
+  }
+
+  @Override
+  public String getVersion() {
+    return "1";
+  }
+
+  @Override
+  public NamedList<Object> processResponse(Reader reader) {
+    throw new RuntimeException("Cannot handle character stream");
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/package-info.java
new file mode 100644
index 0000000..31563cd
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+ 
+/** 
+ * Classes to manage the I/O between shards.
+ */
+package org.apache.solr.analytics.stream;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/BooleanArrayReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/BooleanArrayReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/BooleanArrayReservation.java
new file mode 100644
index 0000000..b0ca38d
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/BooleanArrayReservation.java
@@ -0,0 +1,44 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.BooleanSupplier;
+import java.util.function.IntConsumer;
+import java.util.function.IntSupplier;
+
+import org.apache.solr.analytics.stream.reservation.read.BooleanDataArrayReader;
+import org.apache.solr.analytics.stream.reservation.write.BooleanDataArrayWriter;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+
+public class BooleanArrayReservation extends ReductionDataArrayReservation<BooleanConsumer, BooleanSupplier> {
+  
+  public BooleanArrayReservation(BooleanConsumer applier, IntConsumer sizeApplier, BooleanSupplier extractor, IntSupplier sizeExtractor) {
+    super(applier, sizeApplier, extractor, sizeExtractor);
+  }
+
+  @Override
+  public BooleanDataArrayReader createReadStream(DataInput input) {
+    return new BooleanDataArrayReader(input, applier, sizeApplier);
+  }
+
+  @Override
+  public BooleanDataArrayWriter createWriteStream(DataOutput output) {
+    return new BooleanDataArrayWriter(output, extractor, sizeExtractor);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/BooleanCheckedReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/BooleanCheckedReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/BooleanCheckedReservation.java
new file mode 100644
index 0000000..1c3d9ec
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/BooleanCheckedReservation.java
@@ -0,0 +1,42 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.BooleanSupplier;
+
+import org.apache.solr.analytics.stream.reservation.read.BooleanCheckedDataReader;
+import org.apache.solr.analytics.stream.reservation.write.BooleanCheckedDataWriter;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+
+public class BooleanCheckedReservation extends ReductionCheckedDataReservation<BooleanConsumer, BooleanSupplier> {
+  
+  public BooleanCheckedReservation(BooleanConsumer applier, BooleanSupplier extractor, BooleanSupplier exists) {
+    super(applier, extractor, exists);
+  }
+
+  @Override
+  public BooleanCheckedDataReader createReadStream(DataInput input) {
+    return new BooleanCheckedDataReader(input, applier);
+  }
+
+  @Override
+  public BooleanCheckedDataWriter createWriteStream(DataOutput output) {
+    return new BooleanCheckedDataWriter(output, extractor, exists);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/BooleanReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/BooleanReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/BooleanReservation.java
new file mode 100644
index 0000000..2e6b718
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/BooleanReservation.java
@@ -0,0 +1,42 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.BooleanSupplier;
+
+import org.apache.solr.analytics.stream.reservation.read.BooleanDataReader;
+import org.apache.solr.analytics.stream.reservation.write.BooleanDataWriter;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+
+public class BooleanReservation extends ReductionDataReservation<BooleanConsumer, BooleanSupplier> {
+  
+  public BooleanReservation(BooleanConsumer applier, BooleanSupplier extractor) {
+    super(applier, extractor);
+  }
+
+  @Override
+  public BooleanDataReader createReadStream(DataInput input) {
+    return new BooleanDataReader(input, applier);
+  }
+
+  @Override
+  public BooleanDataWriter createWriteStream(DataOutput output) {
+    return new BooleanDataWriter(output, extractor);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/DoubleArrayReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/DoubleArrayReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/DoubleArrayReservation.java
new file mode 100644
index 0000000..6acf657
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/DoubleArrayReservation.java
@@ -0,0 +1,44 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.DoubleConsumer;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntConsumer;
+import java.util.function.IntSupplier;
+
+import org.apache.solr.analytics.stream.reservation.read.DoubleDataArrayReader;
+import org.apache.solr.analytics.stream.reservation.write.DoubleDataArrayWriter;
+
+public class DoubleArrayReservation extends ReductionDataArrayReservation<DoubleConsumer, DoubleSupplier> {
+  
+  public DoubleArrayReservation(DoubleConsumer applier, IntConsumer sizeApplier, DoubleSupplier extractor, IntSupplier sizeExtractor) {
+    super(applier, sizeApplier, extractor, sizeExtractor);
+  }
+
+  @Override
+  public DoubleDataArrayReader createReadStream(DataInput input) {
+    return new DoubleDataArrayReader(input, applier, sizeApplier);
+  }
+
+  @Override
+  public DoubleDataArrayWriter createWriteStream(DataOutput output) {
+    return new DoubleDataArrayWriter(output, extractor, sizeExtractor);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/DoubleCheckedReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/DoubleCheckedReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/DoubleCheckedReservation.java
new file mode 100644
index 0000000..a1dd461
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/DoubleCheckedReservation.java
@@ -0,0 +1,43 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.BooleanSupplier;
+import java.util.function.DoubleConsumer;
+import java.util.function.DoubleSupplier;
+
+import org.apache.solr.analytics.stream.reservation.read.DoubleCheckedDataReader;
+import org.apache.solr.analytics.stream.reservation.write.DoubleCheckedDataWriter;
+
+public class DoubleCheckedReservation extends ReductionCheckedDataReservation<DoubleConsumer, DoubleSupplier> {
+  
+  public DoubleCheckedReservation(DoubleConsumer applier, DoubleSupplier extractor, BooleanSupplier exists) {
+    super(applier, extractor, exists);
+  }
+
+  @Override
+  public DoubleCheckedDataReader createReadStream(DataInput input) {
+    return new DoubleCheckedDataReader(input, applier);
+  }
+
+  @Override
+  public DoubleCheckedDataWriter createWriteStream(DataOutput output) {
+    return new DoubleCheckedDataWriter(output, extractor, exists);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/DoubleReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/DoubleReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/DoubleReservation.java
new file mode 100644
index 0000000..8ef3bef
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/DoubleReservation.java
@@ -0,0 +1,42 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.DoubleConsumer;
+import java.util.function.DoubleSupplier;
+
+import org.apache.solr.analytics.stream.reservation.read.DoubleDataReader;
+import org.apache.solr.analytics.stream.reservation.write.DoubleDataWriter;
+
+public class DoubleReservation extends ReductionDataReservation<DoubleConsumer, DoubleSupplier> {
+  
+  public DoubleReservation(DoubleConsumer applier, DoubleSupplier extractor) {
+    super(applier, extractor);
+  }
+
+  @Override
+  public DoubleDataReader createReadStream(DataInput input) {
+    return new DoubleDataReader(input, applier);
+  }
+
+  @Override
+  public DoubleDataWriter createWriteStream(DataOutput output) {
+    return new DoubleDataWriter(output, extractor);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/FloatArrayReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/FloatArrayReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/FloatArrayReservation.java
new file mode 100644
index 0000000..702106d
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/FloatArrayReservation.java
@@ -0,0 +1,44 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.IntConsumer;
+import java.util.function.IntSupplier;
+
+import org.apache.solr.analytics.stream.reservation.read.FloatDataArrayReader;
+import org.apache.solr.analytics.stream.reservation.write.FloatDataArrayWriter;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.util.function.FloatSupplier;
+
+public class FloatArrayReservation extends ReductionDataArrayReservation<FloatConsumer, FloatSupplier> {
+  
+  public FloatArrayReservation(FloatConsumer applier, IntConsumer sizeApplier, FloatSupplier extractor, IntSupplier sizeExtractor) {
+    super(applier, sizeApplier, extractor, sizeExtractor);
+  }
+
+  @Override
+  public FloatDataArrayReader createReadStream(DataInput input) {
+    return new FloatDataArrayReader(input, applier, sizeApplier);
+  }
+
+  @Override
+  public FloatDataArrayWriter createWriteStream(DataOutput output) {
+    return new FloatDataArrayWriter(output, extractor, sizeExtractor);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/FloatCheckedReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/FloatCheckedReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/FloatCheckedReservation.java
new file mode 100644
index 0000000..581772c
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/FloatCheckedReservation.java
@@ -0,0 +1,43 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.BooleanSupplier;
+
+import org.apache.solr.analytics.stream.reservation.read.FloatCheckedDataReader;
+import org.apache.solr.analytics.stream.reservation.write.FloatCheckedDataWriter;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.util.function.FloatSupplier;
+
+public class FloatCheckedReservation extends ReductionCheckedDataReservation<FloatConsumer, FloatSupplier> {
+  
+  public FloatCheckedReservation(FloatConsumer applier, FloatSupplier extractor, BooleanSupplier exists) {
+    super(applier, extractor, exists);
+  }
+
+  @Override
+  public FloatCheckedDataReader createReadStream(DataInput input) {
+    return new FloatCheckedDataReader(input, applier);
+  }
+
+  @Override
+  public FloatCheckedDataWriter createWriteStream(DataOutput output) {
+    return new FloatCheckedDataWriter(output, extractor, exists);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/FloatReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/FloatReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/FloatReservation.java
new file mode 100644
index 0000000..c0ea5f0
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/FloatReservation.java
@@ -0,0 +1,42 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+
+import org.apache.solr.analytics.stream.reservation.read.FloatDataReader;
+import org.apache.solr.analytics.stream.reservation.write.FloatDataWriter;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.util.function.FloatSupplier;
+
+public class FloatReservation extends ReductionDataReservation<FloatConsumer, FloatSupplier> {
+  
+  public FloatReservation(FloatConsumer applier, FloatSupplier extractor) {
+    super(applier, extractor);
+  }
+
+  @Override
+  public FloatDataReader createReadStream(DataInput input) {
+    return new FloatDataReader(input, applier);
+  }
+
+  @Override
+  public FloatDataWriter createWriteStream(DataOutput output) {
+    return new FloatDataWriter(output, extractor);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/IntArrayReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/IntArrayReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/IntArrayReservation.java
new file mode 100644
index 0000000..e3639a1
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/IntArrayReservation.java
@@ -0,0 +1,42 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.IntConsumer;
+import java.util.function.IntSupplier;
+
+import org.apache.solr.analytics.stream.reservation.read.IntDataArrayReader;
+import org.apache.solr.analytics.stream.reservation.write.IntDataArrayWriter;
+
+public class IntArrayReservation extends ReductionDataArrayReservation<IntConsumer, IntSupplier> {
+  
+  public IntArrayReservation(IntConsumer applier, IntConsumer sizeApplier, IntSupplier extractor, IntSupplier sizeExtractor) {
+    super(applier, sizeApplier, extractor, sizeExtractor);
+  }
+
+  @Override
+  public IntDataArrayReader createReadStream(DataInput input) {
+    return new IntDataArrayReader(input, applier, sizeApplier);
+  }
+
+  @Override
+  public IntDataArrayWriter createWriteStream(DataOutput output) {
+    return new IntDataArrayWriter(output, extractor, sizeExtractor);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/IntCheckedReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/IntCheckedReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/IntCheckedReservation.java
new file mode 100644
index 0000000..c0a7cf2
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/IntCheckedReservation.java
@@ -0,0 +1,43 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.BooleanSupplier;
+import java.util.function.IntConsumer;
+import java.util.function.IntSupplier;
+
+import org.apache.solr.analytics.stream.reservation.read.IntCheckedDataReader;
+import org.apache.solr.analytics.stream.reservation.write.IntCheckedDataWriter;
+
+public class IntCheckedReservation extends ReductionCheckedDataReservation<IntConsumer, IntSupplier> {
+  
+  public IntCheckedReservation(IntConsumer applier, IntSupplier extractor, BooleanSupplier exists) {
+    super(applier, extractor, exists);
+  }
+
+  @Override
+  public IntCheckedDataReader createReadStream(DataInput input) {
+    return new IntCheckedDataReader(input, applier);
+  }
+
+  @Override
+  public IntCheckedDataWriter createWriteStream(DataOutput output) {
+    return new IntCheckedDataWriter(output, extractor, exists);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/IntReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/IntReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/IntReservation.java
new file mode 100644
index 0000000..cb66b58
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/IntReservation.java
@@ -0,0 +1,42 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.IntConsumer;
+import java.util.function.IntSupplier;
+
+import org.apache.solr.analytics.stream.reservation.read.IntDataReader;
+import org.apache.solr.analytics.stream.reservation.write.IntDataWriter;
+
+public class IntReservation extends ReductionDataReservation<IntConsumer, IntSupplier> {
+  
+  public IntReservation(IntConsumer applier, IntSupplier extractor) {
+    super(applier, extractor);
+  }
+
+  @Override
+  public IntDataReader createReadStream(DataInput input) {
+    return new IntDataReader(input, applier);
+  }
+
+  @Override
+  public IntDataWriter createWriteStream(DataOutput output) {
+    return new IntDataWriter(output, extractor);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/LongArrayReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/LongArrayReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/LongArrayReservation.java
new file mode 100644
index 0000000..980bc2b
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/LongArrayReservation.java
@@ -0,0 +1,45 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.LongConsumer;
+import java.util.function.LongSupplier;
+
+import org.apache.solr.analytics.stream.reservation.read.LongDataArrayReader;
+import org.apache.solr.analytics.stream.reservation.write.LongDataArrayWriter;
+
+import java.util.function.IntConsumer;
+import java.util.function.IntSupplier;
+
+public class LongArrayReservation extends ReductionDataArrayReservation<LongConsumer, LongSupplier> {
+  
+  public LongArrayReservation(LongConsumer applier, IntConsumer sizeApplier, LongSupplier extractor, IntSupplier sizeExtractor) {
+    super(applier, sizeApplier, extractor, sizeExtractor);
+  }
+
+  @Override
+  public LongDataArrayReader createReadStream(DataInput input) {
+    return new LongDataArrayReader(input, applier, sizeApplier);
+  }
+
+  @Override
+  public LongDataArrayWriter createWriteStream(DataOutput output) {
+    return new LongDataArrayWriter(output, extractor, sizeExtractor);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/LongCheckedReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/LongCheckedReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/LongCheckedReservation.java
new file mode 100644
index 0000000..e1626e5
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/LongCheckedReservation.java
@@ -0,0 +1,43 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.BooleanSupplier;
+import java.util.function.LongConsumer;
+import java.util.function.LongSupplier;
+
+import org.apache.solr.analytics.stream.reservation.read.LongCheckedDataReader;
+import org.apache.solr.analytics.stream.reservation.write.LongCheckedDataWriter;
+
+public class LongCheckedReservation extends ReductionCheckedDataReservation<LongConsumer, LongSupplier> {
+  
+  public LongCheckedReservation(LongConsumer applier, LongSupplier extractor, BooleanSupplier exists) {
+    super(applier, extractor, exists);
+  }
+
+  @Override
+  public LongCheckedDataReader createReadStream(DataInput input) {
+    return new LongCheckedDataReader(input, applier);
+  }
+
+  @Override
+  public LongCheckedDataWriter createWriteStream(DataOutput output) {
+    return new LongCheckedDataWriter(output, extractor, exists);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/LongReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/LongReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/LongReservation.java
new file mode 100644
index 0000000..daf8ec3
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/LongReservation.java
@@ -0,0 +1,42 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.LongConsumer;
+import java.util.function.LongSupplier;
+
+import org.apache.solr.analytics.stream.reservation.read.LongDataReader;
+import org.apache.solr.analytics.stream.reservation.write.LongDataWriter;
+
+public class LongReservation extends ReductionDataReservation<LongConsumer, LongSupplier> {
+  
+  public LongReservation(LongConsumer applier, LongSupplier extractor) {
+    super(applier, extractor);
+  }
+
+  @Override
+  public LongDataReader createReadStream(DataInput input) {
+    return new LongDataReader(input, applier);
+  }
+
+  @Override
+  public LongDataWriter createWriteStream(DataOutput output) {
+    return new LongDataWriter(output, extractor);
+  }
+}
\ No newline at end of file


[02/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/expression/FunctionTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/expression/FunctionTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/expression/FunctionTest.java
new file mode 100644
index 0000000..dac7921
--- /dev/null
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/expression/FunctionTest.java
@@ -0,0 +1,221 @@
+/*
+ * 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.solr.analytics.expression;
+
+
+import org.apache.solr.analytics.AbstractAnalyticsStatsTest;
+import org.apache.solr.analytics.facet.AbstractAnalyticsFacetTest;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class FunctionTest extends AbstractAnalyticsStatsTest {
+  static String fileName = "functions.txt";
+
+  static public final int INT = 71;
+  static public final int LONG = 36;
+  static public final int FLOAT = 93;
+  static public final int DOUBLE = 49;
+  static public final int DATE = 12;
+  static public final int STRING = 28;
+  static public final int NUM_LOOPS = 100;
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    initCore("solrconfig-analytics.xml","schema-analytics.xml");
+    h.update("<delete><query>*:*</query></delete>");
+    
+    for (int j = 0; j < NUM_LOOPS; ++j) {
+      int i = j%INT+1;
+      long l = j%LONG+1;
+      float f = j%FLOAT+1;
+      double d = j%DOUBLE+1;
+      double d0 = j%DOUBLE;
+      String dt = (1800+j%DATE) + "-06-30T23:59:59Z";
+      String s = "str" + (j%STRING);
+
+      double add_if = (double)i+f;
+      double add_ldf = (double)l+d+f;
+      double mult_if = (double)i*f;
+      double mult_ldf = (double)l*d*f;
+      double div_if = (double)i/f;
+      double div_ld = (double)l/d;
+      double pow_if = Math.pow(i,f);
+      double pow_ld = Math.pow(l,d);
+      int neg_i = i*-1;
+      long neg_l = l*-1;
+      String dm_2y = (1802+j%DATE) + "-06-30T23:59:59Z";
+      String dm_2m = (1800+j%DATE) + "-08-30T23:59:59Z";
+      String concat_first = "this is the first"+s;
+      String concat_second = "this is the second"+s;
+      
+      assertU(adoc(AbstractAnalyticsFacetTest.filter("id", "1000" + j, "int_id", "" + i, "long_ld", "" + l, "float_fd", "" + f, 
+            "double_dd", "" + d,  "date_dtd", dt, "string_sd", s,
+            "add_if_dd", ""+add_if, "add_ldf_dd", ""+add_ldf, "mult_if_dd", ""+mult_if, "mult_ldf_dd", ""+mult_ldf,
+            "div_if_dd", ""+div_if, "div_ld_dd", ""+div_ld, "pow_if_dd", ""+pow_if, "pow_ld_dd", ""+pow_ld,
+            "neg_id", ""+neg_i, "neg_ld", ""+neg_l, "const_8_dd", "8", "const_10_dd", "10", "dm_2y_dtd", dm_2y, "dm_2m_dtd", dm_2m,
+            "const_00_dtd", "1800-06-30T23:59:59Z", "const_04_dtd", "1804-06-30T23:59:59Z", "const_first_sd", "this is the first", "const_second_sd", "this is the second",
+            "concat_first_sd", concat_first, "concat_second_sd", concat_second, "miss_dd", ""+d0 )));
+      
+      
+      if (usually()) {
+        assertU(commit()); // to have several segments
+      }
+    }
+    
+    assertU(commit()); 
+    
+    setResponse(h.query(request(fileToStringArr(FunctionTest.class, fileName))));
+  }
+      
+  @Test
+  public void addTest() throws Exception { 
+    double result = (Double)getStatResult("ar", "sum", VAL_TYPE.DOUBLE);
+    double calculated = (Double)getStatResult("ar", "sumc", VAL_TYPE.DOUBLE);
+    assertEquals(getRawResponse(), result, calculated, 0.0);
+    // TODO checfk why asserted 2times
+    assertEquals(getRawResponse(), result, calculated, 0.0);
+
+    result = (Double)getStatResult("ar", "mean", VAL_TYPE.DOUBLE);
+    calculated = (Double)getStatResult("ar", "meanc", VAL_TYPE.DOUBLE);
+    assertTrue(result==calculated);
+    assertEquals(getRawResponse(), result, calculated, 0.0);
+  }
+  
+  @Test
+  public void multiplyTest() throws Exception { 
+    double result = (Double)getStatResult("mr", "sum", VAL_TYPE.DOUBLE);
+    double calculated = (Double)getStatResult("mr", "sumc", VAL_TYPE.DOUBLE);
+    assertEquals(getRawResponse(),  result, calculated, 0.0);
+    
+    result = (Double)getStatResult("mr", "mean", VAL_TYPE.DOUBLE);
+    calculated = (Double)getStatResult("mr", "meanc", VAL_TYPE.DOUBLE);
+    assertEquals(getRawResponse(),  result, calculated, 0.0);
+  }
+  
+  @Test
+  public void divideTest() throws Exception { 
+    Double result = (Double)getStatResult("dr", "sum", VAL_TYPE.DOUBLE);
+    Double calculated = (Double)getStatResult("dr", "sumc", VAL_TYPE.DOUBLE);
+    assertEquals(getRawResponse(),  result, calculated, 0.0);
+    
+    result = (Double)getStatResult("dr", "mean", VAL_TYPE.DOUBLE);
+    calculated = (Double)getStatResult("dr", "meanc", VAL_TYPE.DOUBLE);
+    assertEquals(getRawResponse(),  result, calculated, 0.0);
+  }
+  
+  @Test
+  public void powerTest() throws Exception { 
+    double result = (Double)getStatResult("pr", "sum", VAL_TYPE.DOUBLE);
+    double calculated = (Double)getStatResult("pr", "sumc", VAL_TYPE.DOUBLE);
+    assertEquals(getRawResponse(), result, calculated, 0.0);
+    assertEquals(getRawResponse(),  result, calculated, 0.0);
+    
+    result = (Double)getStatResult("pr", "mean", VAL_TYPE.DOUBLE);
+    calculated = (Double)getStatResult("pr", "meanc", VAL_TYPE.DOUBLE);
+    assertEquals(getRawResponse(), result, calculated, 0.0);
+    assertEquals(getRawResponse(), result, calculated, 0.0);
+  }
+  
+  @Test
+  public void negateTest() throws Exception { 
+    double result = (Double)getStatResult("nr", "sum", VAL_TYPE.DOUBLE);
+    double calculated = (Double)getStatResult("nr", "sumc", VAL_TYPE.DOUBLE);
+    assertEquals(getRawResponse(),  result, calculated, 0.0);
+    
+    result = (Double)getStatResult("nr", "mean", VAL_TYPE.DOUBLE);
+    calculated = (Double)getStatResult("nr", "meanc", VAL_TYPE.DOUBLE);
+    assertEquals(getRawResponse(),  result, calculated, 0.0);
+  }
+
+  @Test 
+  public void absoluteValueTest() throws Exception {
+    double result = (Double)getStatResult("avr", "sum", VAL_TYPE.DOUBLE);
+    double calculated = (Double)getStatResult("avr", "sumc", VAL_TYPE.DOUBLE);
+    assertEquals(getRawResponse(),  result, calculated, 0.0);
+    
+    result = (Double)getStatResult("avr", "mean", VAL_TYPE.DOUBLE);
+    calculated = (Double)getStatResult("avr", "meanc", VAL_TYPE.DOUBLE);
+    assertEquals(getRawResponse(),  result, calculated, 0.0);
+  }
+  
+  @Test
+  public void constantNumberTest() throws Exception { 
+    double result = (Double)getStatResult("cnr", "sum", VAL_TYPE.DOUBLE);
+    double calculated = (Double)getStatResult("cnr", "sumc", VAL_TYPE.DOUBLE);
+    assertEquals(getRawResponse(), result, calculated, 0.0);
+    assertEquals(getRawResponse(), result, calculated, 0.0);
+    
+    result = (Double)getStatResult("cnr", "mean", VAL_TYPE.DOUBLE);
+    calculated = (Double)getStatResult("cnr", "meanc", VAL_TYPE.DOUBLE);
+    assertEquals(getRawResponse(), result, calculated, 0.0);
+    assertEquals(getRawResponse(),  result, calculated, 0.0);
+  }
+  
+  @Test
+  public void dateMathTest() throws Exception {
+    String result = (String)getStatResult("dmr", "median", VAL_TYPE.DATE);
+    String calculated = (String)getStatResult("dmr", "medianc", VAL_TYPE.DATE);
+    assertEquals(getRawResponse(), result, calculated);
+    
+    result = (String)getStatResult("dmr", "max", VAL_TYPE.DATE);
+    calculated = (String)getStatResult("dmr", "maxc", VAL_TYPE.DATE);
+    assertEquals(getRawResponse(), result, calculated);
+  }
+  
+  @Test
+  public void constantDateTest() throws Exception { 
+    String result = (String)getStatResult("cdr", "median", VAL_TYPE.DATE);
+    String calculated = (String)getStatResult("cdr", "medianc", VAL_TYPE.DATE);
+    assertEquals(getRawResponse(), result, calculated);
+    assertEquals(getRawResponse(), result, calculated);
+    
+    result = (String)getStatResult("cdr", "max", VAL_TYPE.DATE);
+    calculated = (String)getStatResult("cdr", "maxc", VAL_TYPE.DATE);
+    assertEquals(getRawResponse(), result, calculated);
+  }
+  
+  @Test
+  public void constantStringTest() throws Exception { 
+    String result = (String)getStatResult("csr", "min", VAL_TYPE.STRING);
+    String calculated = (String)getStatResult("csr", "minc", VAL_TYPE.STRING);
+    assertEquals(getRawResponse(), result, calculated);
+    
+    result = (String)getStatResult("csr", "max", VAL_TYPE.STRING);
+    calculated = (String)getStatResult("csr", "maxc", VAL_TYPE.STRING);
+    assertEquals(getRawResponse(), result, calculated);
+  }
+  
+  @Test
+  public void concatenateTest() throws Exception { 
+    String result = (String)getStatResult("cr", "min", VAL_TYPE.STRING);
+    String calculated = (String)getStatResult("cr", "minc", VAL_TYPE.STRING);
+    assertEquals(getRawResponse(), result, calculated);
+    
+    result = (String)getStatResult("cr", "max", VAL_TYPE.STRING);
+    calculated = (String)getStatResult("cr", "maxc", VAL_TYPE.STRING);
+    assertEquals(getRawResponse(), result, calculated);
+  }
+  
+  @Test
+  public void missingTest() throws Exception { 
+    double min = (Double)getStatResult("ms", "min", VAL_TYPE.DOUBLE);
+    double max = (Double)getStatResult("ms", "max", VAL_TYPE.DOUBLE);
+    assertEquals(getRawResponse(), 48.0d, max, 0.0);
+    assertEquals(getRawResponse(), 1.0d, min, 0.0);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/AbstractAnalyticsFacetCloudTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/AbstractAnalyticsFacetCloudTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/AbstractAnalyticsFacetCloudTest.java
new file mode 100644
index 0000000..99e5103
--- /dev/null
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/AbstractAnalyticsFacetCloudTest.java
@@ -0,0 +1,284 @@
+/*
+ * 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.solr.analytics.facet;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.lucene.util.IOUtils;
+import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
+import org.apache.solr.analytics.util.MedianCalculator;
+import org.apache.solr.analytics.util.OrdinalCalculator;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.request.QueryRequest;
+import org.apache.solr.client.solrj.request.UpdateRequest;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.cloud.AbstractDistribZkTestBase;
+import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.util.NamedList;
+import org.junit.AfterClass;
+
+public class AbstractAnalyticsFacetCloudTest extends SolrCloudTestCase {
+  protected static final HashMap<String,Object> defaults = new HashMap<>();
+  
+  protected static final String COLLECTIONORALIAS = "collection1";
+  protected static final int TIMEOUT = DEFAULT_TIMEOUT;
+  protected static final String id = "id";
+
+  public static void setupCluster() throws Exception {
+    configureCluster(2)
+        .addConfig("conf", configset("cloud-analytics"))
+        .configure();
+
+    CollectionAdminRequest.createCollection(COLLECTIONORALIAS, "conf", 2, 1).process(cluster.getSolrClient());
+    AbstractDistribZkTestBase.waitForRecoveriesToFinish(COLLECTIONORALIAS, cluster.getSolrClient().getZkStateReader(),
+        false, true, TIMEOUT);
+    
+    cleanIndex();
+  }
+
+  public static void cleanIndex() throws Exception {
+    new UpdateRequest()
+        .deleteByQuery("*:*")
+        .commit(cluster.getSolrClient(), COLLECTIONORALIAS);
+  }
+  
+  protected String latestType = "";
+  
+  @AfterClass
+  public static void afterClassAbstractAnalysis() {
+    defaults.clear();
+  }
+
+  protected NamedList<Object> queryCloudAnalytics(String[] testParams) throws SolrServerException, IOException, InterruptedException {
+    ModifiableSolrParams params = new ModifiableSolrParams();
+    params.set("q", "*:*");
+    params.set("indent", "true");
+    params.set("olap", "true");
+    params.set("rows", "0");
+    for (int i = 0; i + 1 < testParams.length;) {
+      params.add(testParams[i++], testParams[i++]);
+    }
+    cluster.waitForAllNodes(10000);
+    QueryRequest qreq = new QueryRequest(params);
+    QueryResponse resp = qreq.process(cluster.getSolrClient(), COLLECTIONORALIAS);
+    return resp.getResponse();
+  }
+  
+  @SuppressWarnings("unchecked")
+  protected <T> ArrayList<T> getValueList(NamedList<Object> response, String infoName, String facetType, String facetName, String exprName, boolean includeMissing) {
+    NamedList<NamedList<Object>> facetList = 
+        (NamedList<NamedList<Object>>)response.findRecursive(AnalyticsResponseHeadings.COMPLETED_OLD_HEADER,
+                                                             infoName,
+                                                             facetType,
+                                                             facetName);
+    
+    ArrayList<T> results = new ArrayList<>();
+    facetList.forEach( (name, expressions) -> {
+      if (!includeMissing && !name.equals("(MISSING)")) {
+        T result = (T)expressions.get(exprName);
+        if (result != null)
+          results.add(result);
+      }
+    });
+    return results;
+  }
+  
+  protected boolean responseContainsFacetValue(NamedList<Object> response, String infoName, String facetType, String facetName, String facetValue) {
+    return null != response.findRecursive(AnalyticsResponseHeadings.COMPLETED_OLD_HEADER,
+                                          infoName,
+                                          facetType,
+                                          facetName,
+                                          facetValue);
+  }
+
+
+  public static void increment(List<Long> list, int idx){
+    Long i = list.remove(idx);
+    list.add(idx, i+1);
+  }
+  
+  public static String[] filter(String...args){
+    List<String> l = new ArrayList<>();
+    for( int i=0; i <args.length; i+=2){
+      if( args[i+1].equals("0") || args[i+1].equals("0.0") || 
+          args[i+1].equals("1800-12-31T23:59:59Z") || args[i+1].equals("str0") ||
+          args[i+1].equals("this is the firststr0") || 
+          args[i+1].equals("this is the secondstr0") ){
+        continue;
+      }
+      l.add(args[i]);
+      l.add(args[i+1]);
+    }
+    return l.toArray(new String[0]);
+  }
+  
+  protected void setLatestType(String latestType) {
+    this.latestType = latestType;
+  }
+
+  @SuppressWarnings({ "unchecked", "rawtypes" })
+  public <T extends Number & Comparable<T>> ArrayList calculateNumberStat(ArrayList<ArrayList<T>> lists, String stat) {
+    ArrayList result;
+    if (stat.equals("median")) {
+      result = new ArrayList<Double>();
+      for (List<T> list : lists) {
+        result.add(MedianCalculator.getMedian(list));
+      }
+    } else if (stat.equals("mean")) {
+      result = new ArrayList<Double>();
+      for (List<T> list : lists) {
+        double d = 0;
+        for (T element : list) {
+          d += element.doubleValue();
+        }
+        result.add(d/list.size());
+      }
+    } else if (stat.equals("sum")) {
+      result = new ArrayList<Double>();
+      for (Collection<T> list : lists) {
+        double d = 0;
+        for (T element : list) {
+          d += element.doubleValue();
+        }
+        result.add(d);
+      }
+    } else if (stat.equals("sumOfSquares")) {
+      result = new ArrayList<Double>();
+      for (List<T> list : lists) {
+        double d = 0;
+        for (T element : list) {
+          d += element.doubleValue()*element.doubleValue();
+        }
+        result.add(d);
+      }
+    } else if (stat.equals("stddev")) {
+      result = new ArrayList<Double>();
+      for (List<T> list : lists) {
+        double sum = 0;
+        double sumSquares = 0;
+        for (T element : list) {
+          sum += element.doubleValue();
+          sumSquares += element.doubleValue()*element.doubleValue();
+        }
+        String res = Double.toString(Math.sqrt(sumSquares/list.size()-sum*sum/(list.size()*list.size())));
+        result.add(Double.parseDouble(res));
+      }
+    } else {
+      throw new IllegalArgumentException();
+    }
+    return result;
+  }
+
+  @SuppressWarnings({ "unchecked", "rawtypes" })
+  public <T extends Comparable<T>> ArrayList calculateStat(ArrayList<ArrayList<T>> lists, String stat) {
+    ArrayList result;
+    if (stat.contains("perc_")) {
+      result = new ArrayList<T>();
+      for (List<T> list : lists) {
+        if( list.size() == 0) continue;
+        int ord = (int) Math.ceil(Double.parseDouble(stat.substring(5))/100 * list.size()) - 1;
+        ArrayList<Integer> percs = new ArrayList<>(1);
+        percs.add(ord);
+        OrdinalCalculator.putOrdinalsInPosition(list, percs);
+        result.add(list.get(ord));
+      }
+    } else if (stat.equals("count")) {
+      result = new ArrayList<Long>();
+      for (List<T> list : lists) {
+        result.add((long)list.size());
+      }
+    } else if (stat.equals("missing")) {
+      result = new ArrayList<Long>();
+      for (ArrayList<T> list : lists) {
+        result.add(calculateMissing(list,latestType));
+      }
+    } else if (stat.equals("unique")) {
+      result = new ArrayList<Long>();
+      for (List<T> list : lists) {
+        HashSet<T> set = new HashSet<>();
+        set.addAll(list);
+        result.add((long)set.size());
+      }
+    } else if (stat.equals("max")) {
+      result = new ArrayList<T>();
+      for (List<T> list : lists) {
+        if( list.size() == 0) continue;
+        Collections.sort(list);
+        result.add(list.get(list.size()-1));
+      }
+    } else if (stat.equals("min")) {
+      result = new ArrayList<T>();
+      for (List<T> list : lists) {
+        if( list.size() == 0) continue;
+        Collections.sort((List<T>)list);
+        result.add(list.get(0));
+      }
+    } else {
+      result = null;
+    }
+    return result;
+  }
+
+  @SuppressWarnings("unchecked")
+  public <T extends Comparable<T>> Long calculateMissing(ArrayList<T> list, String type) {
+    T def = (T)defaults.get(type);
+    long miss = 0;
+    for (T element : list) {
+      if (element.compareTo(def)==0) {
+        miss++;
+      }
+    }
+    return Long.valueOf(miss);
+  }
+
+  public static ModifiableSolrParams fileToParams(Class<?> clazz, String fileName) throws FileNotFoundException {
+    InputStream in = clazz.getResourceAsStream(fileName);
+    if (in == null) throw new FileNotFoundException("Resource not found: " + fileName);
+    Scanner file = new Scanner(in, "UTF-8");
+    try { 
+      ModifiableSolrParams params = new ModifiableSolrParams();
+      params.set("q", "*:*");
+      params.set("indent", "true");
+      params.set("olap", "true");
+      params.set("rows", "0");
+      while (file.hasNextLine()) {
+        String line = file.nextLine();
+        line = line.trim();
+        if( StringUtils.isBlank(line) || line.startsWith("#")){
+          continue;
+        }
+        String[] param = line.split("=");
+        params.set(param[0], param[1]);
+      }
+      return params;
+    } finally {
+      IOUtils.closeWhileHandlingException(file, in);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/AbstractAnalyticsFacetTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/AbstractAnalyticsFacetTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/AbstractAnalyticsFacetTest.java
index 3660208..ff0e7da 100644
--- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/AbstractAnalyticsFacetTest.java
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/AbstractAnalyticsFacetTest.java
@@ -31,8 +31,9 @@ import java.util.Scanner;
 
 import org.apache.lucene.util.IOUtils;
 import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
 import org.apache.solr.analytics.util.MedianCalculator;
-import org.apache.solr.analytics.util.PercentileCalculator;
+import org.apache.solr.analytics.util.OrdinalCalculator;
 import org.apache.solr.request.SolrQueryRequest;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
@@ -92,9 +93,10 @@ public class AbstractAnalyticsFacetTest extends SolrTestCaseJ4 {
   }
   private NodeList getNodes(String n1, String n2, String n3, String element, String n4) throws XPathExpressionException {
     // Construct the XPath expression. The form better not change or all these will fail.
-    StringBuilder sb = new StringBuilder("/response/lst[@name='stats']/lst[@name='").append(n1).append("']");
+    StringBuilder sb = new StringBuilder("/response/lst[@name='"+AnalyticsResponseHeadings.COMPLETED_OLD_HEADER+"']/lst[@name='").append(n1).append("']");
     sb.append("/lst[@name='").append(n2).append("']");
     sb.append("/lst[@name='").append(n3).append("']");
+    sb.append("/lst[@name!='(MISSING)']");
     sb.append("//").append(element).append("[@name='").append(n4).append("']");
     return (NodeList)xPathFact.newXPath().compile(sb.toString()).evaluate(doc, XPathConstants.NODESET);
 
@@ -229,11 +231,14 @@ public class AbstractAnalyticsFacetTest extends SolrTestCaseJ4 {
   public <T extends Comparable<T>> ArrayList calculateStat(ArrayList<ArrayList<T>> lists, String stat) {
     ArrayList result;
     if (stat.contains("perc_")) {
-      double[] perc = new double[]{Double.parseDouble(stat.substring(5))/100};
       result = new ArrayList<T>();
       for (List<T> list : lists) {
         if( list.size() == 0) continue;
-        result.add(PercentileCalculator.getPercentiles(list, perc).get(0));
+        int ord = (int) Math.ceil(Double.parseDouble(stat.substring(5))/100 * list.size()) - 1;
+        ArrayList<Integer> percs = new ArrayList<>(1);
+        percs.add(ord);
+        OrdinalCalculator.putOrdinalsInPosition(list, percs);
+        result.add(list.get(ord));
       }
     } else if (stat.equals("count")) {
       result = new ArrayList<Long>();
@@ -294,7 +299,7 @@ public class AbstractAnalyticsFacetTest extends SolrTestCaseJ4 {
 
   
   public static String[] fileToStringArr(Class<?> clazz, String fileName) throws FileNotFoundException {
-    InputStream in = clazz.getResourceAsStream(fileName);
+    InputStream in = clazz.getResourceAsStream("/solr/analytics/" + fileName);
     if (in == null) throw new FileNotFoundException("Resource not found: " + fileName);
     Scanner file = new Scanner(in, "UTF-8");
     try { 
@@ -304,7 +309,14 @@ public class AbstractAnalyticsFacetTest extends SolrTestCaseJ4 {
         if (line.length()<2) {
           continue;
         }
+        int commentStart = line.indexOf("//");
+        if (commentStart >= 0) {
+          line = line.substring(0,commentStart);
+        }
         String[] param = line.split("=");
+        if (param.length != 2) {
+          continue;
+        }
         strList.add(param[0]);
         strList.add(param[1]);
       }
@@ -313,4 +325,19 @@ public class AbstractAnalyticsFacetTest extends SolrTestCaseJ4 {
       IOUtils.closeWhileHandlingException(file, in);
     }
   }
+  
+  protected void removeNodes(String xPath, List<Double> string) throws XPathExpressionException {
+    NodeList missingNodes = getNodes(xPath);
+    List<Double> result = new ArrayList<Double>();
+    for (int idx = 0; idx < missingNodes.getLength(); ++idx) {
+      result.add(Double.parseDouble(missingNodes.item(idx).getTextContent()));
+    }
+    string.removeAll(result);
+  }
+
+  protected NodeList getNodes(String xPath) throws XPathExpressionException {
+    StringBuilder sb = new StringBuilder(xPath);
+    return (NodeList) xPathFact.newXPath().compile(sb.toString()).evaluate(doc, XPathConstants.NODESET);
+  }
+  
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FacetSortingTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FacetSortingTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FacetSortingTest.java
new file mode 100644
index 0000000..ef09014
--- /dev/null
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FacetSortingTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.solr.analytics.facet;
+
+import org.apache.solr.analytics.AbstractAnalyticsStatsTest;
+import org.apache.solr.analytics.expression.ExpressionTest;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class FacetSortingTest extends AbstractAnalyticsStatsTest {
+  private static String fileName = "facetSorting.txt";
+
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    initCore("solrconfig-analytics.xml", "schema-analytics.xml");
+    h.update("<delete><query>*:*</query></delete>");
+
+    // The data set below is so generated that in bucket corresponding fieldFacet B, double_dd column has null values 
+    // and in bucket C corresponding to fieldFacet C has null values for column long_ld. 
+    // FieldFaceting occurs on string_sd field
+    assertU(adoc("id", "1001", "string_sd", "A", "double_dd", "" + 3, "long_ld", "" + 1));
+    assertU(adoc("id", "1002", "string_sd", "A", "double_dd", "" + 25, "long_ld", "" + 2));
+    assertU(adoc("id", "1003", "string_sd", "B", "long_ld", "" + 3));
+    assertU(adoc("id", "1004", "string_sd", "B", "long_ld", "" + 4));
+    assertU(adoc("id", "1005", "string_sd", "C",                       "double_dd", "" + 17));
+    
+    assertU(commit());
+    String response = h.query(request(fileToStringArr(ExpressionTest.class, fileName)));
+    setResponse(response);
+  }
+
+  @Test
+  public void addTest() throws Exception {
+    Double minResult = (Double) getStatResult("ar", "min", VAL_TYPE.DOUBLE);
+    Long maxResult = (Long) getStatResult("ar", "max", VAL_TYPE.LONG);
+    assertEquals(Double.valueOf(minResult), Double.valueOf(3.0));
+    assertEquals(Long.valueOf(maxResult),Long.valueOf(4));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetCloudTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetCloudTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetCloudTest.java
new file mode 100644
index 0000000..b3341b1
--- /dev/null
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/facet/FieldFacetCloudTest.java
@@ -0,0 +1,1214 @@
+/*
+ * 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.solr.analytics.facet;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.solr.client.solrj.request.UpdateRequest;
+import org.apache.solr.common.util.NamedList;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+
+public class FieldFacetCloudTest extends AbstractAnalyticsFacetCloudTest{
+  public static final int INT = 71;
+  public static final int LONG = 36;
+  public static final int LONGM = 50;
+  public static final int FLOAT = 73;
+  public static final int FLOATM = 84;
+  public static final int DOUBLE = 49;
+  public static final int DATE = 12;
+  public static final int DATEM = 30;
+  public static final int STRING = 29;
+  public static final int STRINGM = 41;
+  public static final int NUM_LOOPS = 100;
+  
+  //INT
+  private static ArrayList<ArrayList<Integer>> intDateTestStart; 
+  private static ArrayList<Long> intDateTestMissing; 
+  private static ArrayList<ArrayList<Integer>> intStringTestStart; 
+  private static ArrayList<Long> intStringTestMissing; 
+  
+  //LONG
+  private static ArrayList<ArrayList<Long>> longDateTestStart; 
+  private static ArrayList<Long> longDateTestMissing; 
+  private static ArrayList<ArrayList<Long>> longStringTestStart; 
+  private static ArrayList<Long> longStringTestMissing; 
+  
+  //FLOAT
+  private static ArrayList<ArrayList<Float>> floatDateTestStart; 
+  private static ArrayList<Long> floatDateTestMissing; 
+  private static ArrayList<ArrayList<Float>> floatStringTestStart; 
+  private static ArrayList<Long> floatStringTestMissing; 
+  
+  //DOUBLE
+  private static ArrayList<ArrayList<Double>> doubleDateTestStart; 
+  private static ArrayList<Long> doubleDateTestMissing; 
+  private static ArrayList<ArrayList<Double>> doubleStringTestStart; 
+  private static ArrayList<Long> doubleStringTestMissing; 
+  
+  //DATE
+  private static ArrayList<ArrayList<String>> dateIntTestStart; 
+  private static ArrayList<Long> dateIntTestMissing; 
+  private static ArrayList<ArrayList<String>> dateLongTestStart; 
+  private static ArrayList<Long> dateLongTestMissing; 
+  
+  //String
+  private static ArrayList<ArrayList<String>> stringIntTestStart; 
+  private static ArrayList<Long> stringIntTestMissing; 
+  private static ArrayList<ArrayList<String>> stringLongTestStart; 
+  private static ArrayList<Long> stringLongTestMissing; 
+  
+  //Multi-Valued
+  private static ArrayList<ArrayList<Integer>> multiLongTestStart; 
+  private static ArrayList<Long> multiLongTestMissing; 
+  private static ArrayList<ArrayList<Integer>> multiStringTestStart; 
+  private static ArrayList<Long> multiStringTestMissing; 
+  private static ArrayList<ArrayList<Integer>> multiDateTestStart; 
+  private static ArrayList<Long> multiDateTestMissing; 
+  
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    setupCluster();
+
+    //INT
+    intDateTestStart = new ArrayList<>();
+    intDateTestMissing = new ArrayList<>();
+    intStringTestStart = new ArrayList<>();
+    intStringTestMissing = new ArrayList<>();
+    
+    //LONG
+    longDateTestStart = new ArrayList<>();
+    longDateTestMissing = new ArrayList<>();
+    longStringTestStart = new ArrayList<>();
+    longStringTestMissing = new ArrayList<>();
+    
+    //FLOAT
+    floatDateTestStart = new ArrayList<>();
+    floatDateTestMissing = new ArrayList<>();
+    floatStringTestStart = new ArrayList<>();
+    floatStringTestMissing = new ArrayList<>();
+    
+    //DOUBLE
+    doubleDateTestStart = new ArrayList<>();
+    doubleDateTestMissing = new ArrayList<>();
+    doubleStringTestStart = new ArrayList<>();
+    doubleStringTestMissing = new ArrayList<>();
+    
+    //DATE
+    dateIntTestStart = new ArrayList<>();
+    dateIntTestMissing = new ArrayList<>();
+    dateLongTestStart = new ArrayList<>();
+    dateLongTestMissing = new ArrayList<>();
+    
+    //String
+    stringIntTestStart = new ArrayList<>();
+    stringIntTestMissing = new ArrayList<>();
+    stringLongTestStart = new ArrayList<>();
+    stringLongTestMissing = new ArrayList<>();
+    
+    //Multi-Valued
+    multiLongTestStart = new ArrayList<>();
+    multiLongTestMissing = new ArrayList<>();
+    multiStringTestStart = new ArrayList<>();
+    multiStringTestMissing = new ArrayList<>();
+    multiDateTestStart = new ArrayList<>();
+    multiDateTestMissing = new ArrayList<>();
+
+    UpdateRequest req = new UpdateRequest();
+    for (int j = 0; j < NUM_LOOPS; ++j) {
+      int i = j%INT;
+      long l = j%LONG;
+      long lm = j%LONGM;
+      float f = j%FLOAT;
+      double d = j%DOUBLE;
+      int dt = j%DATE;
+      int dtm = j%DATEM;
+      int s = j%STRING;
+      int sm = j%STRINGM;
+
+      List<String> fields = new ArrayList<>();
+      fields.add("id"); fields.add("1000"+j);
+      
+      if (dt != 0) {
+      }
+      if( i != 0 ) {
+        fields.add("int_id"); fields.add("" + i);
+      }
+      if( l != 0l ) {
+        fields.add("long_ld"); fields.add("" + l);
+        fields.add("long_ldm"); fields.add("" + l);
+      }
+      if( lm != 0l ) {
+        fields.add("long_ldm"); fields.add("" + lm);
+      }
+      if( f != 0.0f ) {
+        fields.add("float_fd"); fields.add("" + f);
+      }
+      if( d != 0.0d ) {
+        fields.add("double_dd"); fields.add("" + d);
+      }
+      if( dt != 0 ) {
+        fields.add("date_dtd"); fields.add((1800+dt) + "-12-31T23:59:59Z");
+        fields.add("date_dtdm"); fields.add((1800+dt) + "-12-31T23:59:59Z");
+      }
+      if ( dtm != 0 ) {
+        fields.add("date_dtdm"); fields.add((1800+dtm) + "-12-31T23:59:59Z");
+      }
+      if ( s != 0 ) {
+        fields.add("string_sd"); fields.add("str" + s);
+        fields.add("string_sdm"); fields.add("str" + s);
+      }
+      if ( sm != 0 ) {
+        fields.add("string_sdm"); fields.add("str" + sm);
+      }
+      if ( dtm != 0 ) {
+        fields.add("date_dtdm"); fields.add((1800+dtm) + "-12-31T23:59:59Z");
+      }
+      req.add(fields.toArray(new String[0]));
+      
+      if( dt != 0 ){
+        //Dates
+        if (j-DATE<0) {
+          ArrayList<Integer> list1 = new ArrayList<>();
+          if( i != 0 ){
+            list1.add(i);
+            intDateTestMissing.add(0l);
+          } else {
+            intDateTestMissing.add(1l);
+          }
+          intDateTestStart.add(list1);
+          ArrayList<Long> list2 = new ArrayList<>();
+          if( l != 0l ){
+            list2.add(l);
+            longDateTestMissing.add(0l);
+          } else {
+            longDateTestMissing.add(1l);
+          }
+          longDateTestStart.add(list2);
+          ArrayList<Float> list3 = new ArrayList<>();
+          if ( f != 0.0f ){
+            list3.add(f);
+            floatDateTestMissing.add(0l);
+          } else {
+            floatDateTestMissing.add(1l);
+            
+          }
+          floatDateTestStart.add(list3);
+          ArrayList<Double> list4 = new ArrayList<>();
+          if( d != 0.0d ){
+            list4.add(d);
+            doubleDateTestMissing.add(0l);
+          } else {
+            doubleDateTestMissing.add(1l);
+          }
+          doubleDateTestStart.add(list4);
+          ArrayList<Integer> list5 = new ArrayList<>();
+          if( i != 0 ){
+            list5.add(i);
+            multiDateTestMissing.add(0l);
+          } else {
+            multiDateTestMissing.add(1l);
+            
+          }
+          multiDateTestStart.add(list5);
+        } else {
+          if( i != 0 ) intDateTestStart.get(dt-1).add(i); else increment(intDateTestMissing,dt-1);
+          if( l != 0l ) longDateTestStart.get(dt-1).add(l); else increment(longDateTestMissing,dt-1);
+          if( f != 0.0f ) floatDateTestStart.get(dt-1).add(f); else increment(floatDateTestMissing,dt-1);
+          if( d != 0.0d ) doubleDateTestStart.get(dt-1).add(d); else increment(doubleDateTestMissing,dt-1);
+          if( i != 0 ) multiDateTestStart.get(dt-1).add(i); else increment(multiDateTestMissing,dt-1);
+        }
+      }
+      
+      if (j-DATEM<0 && dtm!=dt && dtm!=0) {
+        ArrayList<Integer> list1 = new ArrayList<>();
+        if( i != 0 ){
+          list1.add(i);
+          multiDateTestMissing.add(0l);
+        } else {
+          multiDateTestMissing.add(1l);
+        }
+        multiDateTestStart.add(list1);
+      } else if (dtm!=dt && dtm!=0) {
+        if( i != 0 ) multiDateTestStart.get(dtm-1).add(i);
+      }
+      
+      if( s != 0 ){
+        //Strings
+        if (j-STRING<0) {
+          ArrayList<Integer> list1 = new ArrayList<>();
+          if( i != 0 ){
+            list1.add(i);
+            intStringTestMissing.add(0l);
+          } else {
+            intStringTestMissing.add(1l);
+          }
+          intStringTestStart.add(list1);
+          ArrayList<Long> list2 = new ArrayList<>();
+          if( l != 0l ){
+            list2.add(l);
+            longStringTestMissing.add(0l);
+          } else {
+            longStringTestMissing.add(1l);
+          }
+          longStringTestStart.add(list2);
+          ArrayList<Float> list3 = new ArrayList<>();
+          if( f != 0.0f ){
+            list3.add(f);
+            floatStringTestMissing.add(0l);
+          } else {
+            floatStringTestMissing.add(1l);
+          }
+          floatStringTestStart.add(list3);
+          ArrayList<Double> list4 = new ArrayList<>();
+          if( d != 0.0d ){
+            list4.add(d);
+            doubleStringTestMissing.add(0l);
+          } else {
+            doubleStringTestMissing.add(1l);
+          }
+          doubleStringTestStart.add(list4);
+          ArrayList<Integer> list5 = new ArrayList<>();
+          if( i != 0 ){
+            list5.add(i);
+            multiStringTestMissing.add(0l);
+          } else {
+            multiStringTestMissing.add(1l);
+          }
+          multiStringTestStart.add(list5);
+        } else {
+          if( i != 0 ) intStringTestStart.get(s-1).add(i); else increment(intStringTestMissing,s-1);
+          if( l != 0l ) longStringTestStart.get(s-1).add(l); else increment(longStringTestMissing,s-1);
+          if( f != 0.0f ) floatStringTestStart.get(s-1).add(f); else increment(floatStringTestMissing,s-1);
+          if( d != 0.0d ) doubleStringTestStart.get(s-1).add(d); else increment(doubleStringTestMissing,s-1);
+          if( i != 0 ) multiStringTestStart.get(s-1).add(i); else increment(multiStringTestMissing,s-1);
+        }
+      }
+      
+      //Strings
+      if( sm != 0 ){
+        if (j-STRINGM<0&&sm!=s) {
+          ArrayList<Integer> list1 = new ArrayList<>();
+          if( i != 0 ){
+            list1.add(i);
+            multiStringTestMissing.add(0l);
+          } else {
+            multiStringTestMissing.add(1l);
+          }
+          multiStringTestStart.add(list1);
+        } else if (sm!=s) {
+          if( i != 0 ) multiStringTestStart.get(sm-1).add(i); else increment(multiStringTestMissing,sm-1);
+        }
+      }
+      
+      //Int
+      if( i != 0 ){
+        if (j-INT<0) {
+          ArrayList<String> list1 = new ArrayList<>();
+          if( dt != 0 ){
+            list1.add((1800+dt) + "-12-31T23:59:59Z");
+            dateIntTestMissing.add(0l);
+          } else {
+            dateIntTestMissing.add(1l);
+          }
+          dateIntTestStart.add(list1);
+          ArrayList<String> list2 = new ArrayList<>();
+          if( s != 0 ){
+            list2.add("str"+s);
+            stringIntTestMissing.add(0l);
+          } else {
+            stringIntTestMissing.add(1l);
+          }
+          stringIntTestStart.add(list2);
+        } else {
+          if( dt != 0 ) dateIntTestStart.get(i-1).add((1800+dt) + "-12-31T23:59:59Z"); else increment(dateIntTestMissing,i-1);
+          if( s != 0 ) stringIntTestStart.get(i-1).add("str"+s); else increment(stringIntTestMissing,i-1);
+        }
+      }
+      
+      //Long
+      if( l != 0 ){
+        if (j-LONG<0) {
+          ArrayList<String> list1 = new ArrayList<>();
+          if( dt != 0 ){
+            list1.add((1800+dt) + "-12-31T23:59:59Z");
+            dateLongTestMissing.add(0l);
+          } else {
+            dateLongTestMissing.add(1l);
+          }
+          dateLongTestStart.add(list1);
+          ArrayList<String> list2 = new ArrayList<>();
+          if( s != 0 ){
+            list2.add("str"+s);
+            stringLongTestMissing.add(0l);
+          } else {
+            stringLongTestMissing.add(1l);
+          }
+          stringLongTestStart.add(list2);
+          ArrayList<Integer> list3 = new ArrayList<>();
+          if( i != 0 ){
+            list3.add(i);
+            multiLongTestMissing.add(0l);
+          } else {
+            multiLongTestMissing.add(1l);
+          }
+          multiLongTestStart.add(list3);
+        } else {
+          if( dt != 0 ) dateLongTestStart.get((int)l-1).add((1800+dt) + "-12-31T23:59:59Z"); else increment(dateLongTestMissing,(int)l-1);
+          if( s != 0 ) stringLongTestStart.get((int)l-1).add("str"+s); else increment(stringLongTestMissing,(int)l-1);
+          if( i != 0 ) multiLongTestStart.get((int)l-1).add(i); else increment(multiLongTestMissing,(int)l-1);
+        }
+      }
+      
+      //Long
+      if( lm != 0 ){
+        if (j-LONGM<0&&lm!=l) {
+          ArrayList<Integer> list1 = new ArrayList<>();
+          if( i != 0 ){
+            list1.add(i);
+            multiLongTestMissing.add(0l);
+          } else {
+            multiLongTestMissing.add(1l);
+          }
+          multiLongTestStart.add(list1);
+        } else if (lm!=l) {
+          if( i != 0 ) multiLongTestStart.get((int)lm-1).add(i); else increment( multiLongTestMissing,(int)lm-1);
+        }
+      }
+      
+    }
+
+    req.commit(cluster.getSolrClient(), COLLECTIONORALIAS);
+    
+  }
+  
+  @SuppressWarnings("unchecked")
+  @Test
+  public void sumTest() throws Exception { 
+    String[] params = new String[] {
+        "o.sum.s.int", "sum(int_id)",
+        "o.sum.s.long", "sum(long_ld)",
+        "o.sum.s.float", "sum(float_fd)",
+        "o.sum.s.double", "sum(double_dd)",
+        "o.sum.ff", "string_sd",
+        "o.sum.ff", "date_dtd"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+
+    //Int Date
+    Collection<Double> intDate = getValueList(response, "sum", "fieldFacets", "date_dtd", "int", false);
+    ArrayList<Double> intDateTest = calculateNumberStat(intDateTestStart, "sum");
+    assertEquals(responseStr.toString(),intDate,intDateTest);
+    //Int String
+    Collection<Double> intString = getValueList(response, "sum", "fieldFacets", "string_sd", "int", false);
+    ArrayList<Double> intStringTest = calculateNumberStat(intStringTestStart, "sum");
+    assertEquals(responseStr,intString,intStringTest);
+
+    //Long Date
+    Collection<Double> longDate = getValueList(response, "sum", "fieldFacets", "date_dtd", "long", false);
+    ArrayList<Double> longDateTest = calculateNumberStat(longDateTestStart, "sum");
+    assertEquals(responseStr,longDate,longDateTest);
+    //Long String
+    Collection<Double> longString = getValueList(response, "sum", "fieldFacets", "string_sd", "long", false);
+    ArrayList<Double> longStringTest = calculateNumberStat(longStringTestStart, "sum");
+    assertEquals(responseStr,longString,longStringTest);
+
+    //Float Date
+    Collection<Double> floatDate = getValueList(response, "sum", "fieldFacets", "date_dtd", "float", false);
+    ArrayList<Double> floatDateTest = calculateNumberStat(floatDateTestStart, "sum");
+    assertEquals(responseStr,floatDate,floatDateTest);
+    //Float String
+    Collection<Double> floatString = getValueList(response, "sum", "fieldFacets", "string_sd", "float", false);
+    ArrayList<Double> floatStringTest = calculateNumberStat(floatStringTestStart, "sum");
+    assertEquals(responseStr,floatString,floatStringTest);
+
+    //Double Date
+    Collection<Double> doubleDate = getValueList(response, "sum", "fieldFacets", "date_dtd", "double", false);
+    ArrayList<Double> doubleDateTest = calculateNumberStat(doubleDateTestStart, "sum");
+    assertEquals(responseStr,doubleDate,doubleDateTest);
+    //Double String
+    Collection<Double> doubleString = getValueList(response, "sum", "fieldFacets", "string_sd", "double", false);
+    ArrayList<Double> doubleStringTest = calculateNumberStat(doubleStringTestStart, "sum");
+    assertEquals(responseStr,doubleString,doubleStringTest);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Test
+  public void meanTest() throws Exception { 
+    String[] params = new String[] {
+        "o.mean.s.int", "mean(int_id)",
+        "o.mean.s.long", "mean(long_ld)",
+        "o.mean.s.float", "mean(float_fd)",
+        "o.mean.s.double", "mean(double_dd)",
+        "o.mean.ff", "string_sd",
+        "o.mean.ff", "date_dtd"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int Date
+    Collection<Double> intDate = getValueList(response, "mean", "fieldFacets", "date_dtd", "int", false);
+    ArrayList<Double> intDateTest = calculateNumberStat(intDateTestStart, "mean");
+    assertEquals(responseStr,intDate,intDateTest);
+    //Int String
+    Collection<Double> intString = getValueList(response, "mean", "fieldFacets", "string_sd", "int", false);
+    ArrayList<Double> intStringTest = calculateNumberStat(intStringTestStart, "mean");
+    assertEquals(responseStr,intString,intStringTest);
+
+    //Long Date
+    Collection<Double> longDate = getValueList(response, "mean", "fieldFacets", "date_dtd", "long", false);
+    ArrayList<Double> longDateTest = calculateNumberStat(longDateTestStart, "mean");
+    assertEquals(responseStr,longDate,longDateTest);
+    //Long String
+    Collection<Double> longString = getValueList(response, "mean", "fieldFacets", "string_sd", "long", false);
+    ArrayList<Double> longStringTest = calculateNumberStat(longStringTestStart, "mean");
+    assertEquals(responseStr,longString,longStringTest);
+
+    //Float Date
+    Collection<Double> floatDate = getValueList(response, "mean", "fieldFacets", "date_dtd", "float", false);
+    ArrayList<Double> floatDateTest = calculateNumberStat(floatDateTestStart, "mean");
+    assertEquals(responseStr,floatDate,floatDateTest);
+    //Float String
+    Collection<Double> floatString = getValueList(response, "mean", "fieldFacets", "string_sd", "float", false);
+    ArrayList<Double> floatStringTest = calculateNumberStat(floatStringTestStart, "mean");
+    assertEquals(responseStr,floatString,floatStringTest);
+
+    //Double Date
+    Collection<Double> doubleDate = getValueList(response, "mean", "fieldFacets", "date_dtd", "double", false);
+    ArrayList<Double> doubleDateTest = calculateNumberStat(doubleDateTestStart, "mean");
+    assertEquals(responseStr,doubleDate,doubleDateTest);
+    //Double String
+    Collection<Double> doubleString = getValueList(response, "mean", "fieldFacets", "string_sd", "double", false);
+    ArrayList<Double> doubleStringTest = calculateNumberStat(doubleStringTestStart, "mean");
+    assertEquals(responseStr,doubleString,doubleStringTest);
+  }
+  
+  @SuppressWarnings("unchecked")
+  @Test
+  public void stddevFacetAscTest() throws Exception { 
+    String[] params = new String[] {
+        "o.stddev.s.int", "stddev(int_id)",
+        "o.stddev.s.long", "stddev(long_ld)",
+        "o.stddev.s.float", "stddev(float_fd)",
+        "o.stddev.s.double", "stddev(double_dd)",
+        "o.stddev.ff", "string_sd",
+        "o.stddev.ff", "date_dtd"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    
+    //Int Date
+    ArrayList<Double> intDate = getValueList(response, "stddev", "fieldFacets", "date_dtd", "int", false);
+    ArrayList<Double> intDateTest = calculateNumberStat(intDateTestStart, "stddev");
+    checkStddevs(response, intDate, intDateTest);
+    //Int String
+    ArrayList<Double> intString = getValueList(response, "stddev", "fieldFacets", "string_sd", "int", false);
+    ArrayList<Double> intStringTest = calculateNumberStat(intStringTestStart, "stddev");
+    checkStddevs(response, intString, intStringTest);
+
+    //Long Date
+    ArrayList<Double> longDate = getValueList(response, "stddev", "fieldFacets", "date_dtd", "long", false);
+    ArrayList<Double> longDateTest = calculateNumberStat(longDateTestStart, "stddev");
+    checkStddevs(response, longDate, longDateTest);
+    //Long String
+    ArrayList<Double> longString = getValueList(response, "stddev", "fieldFacets", "string_sd", "long", false);
+    ArrayList<Double> longStringTest = calculateNumberStat(longStringTestStart, "stddev");
+    checkStddevs(response, longString, longStringTest);
+
+    //Float Date
+    ArrayList<Double> floatDate = getValueList(response, "stddev", "fieldFacets", "date_dtd", "float", false);
+    ArrayList<Double> floatDateTest = calculateNumberStat(floatDateTestStart, "stddev");
+    checkStddevs(response, floatDate, floatDateTest);
+    //Float String
+    ArrayList<Double> floatString = getValueList(response, "stddev", "fieldFacets", "string_sd", "float", false);
+    ArrayList<Double> floatStringTest = calculateNumberStat(floatStringTestStart, "stddev");
+    checkStddevs(response, floatString, floatStringTest);
+
+    //Double Date
+    ArrayList<Double> doubleDate = getValueList(response, "stddev", "fieldFacets", "date_dtd", "double", false);
+    ArrayList<Double> doubleDateTest = calculateNumberStat(doubleDateTestStart, "stddev");
+    checkStddevs(response, doubleDate, doubleDateTest);
+    //Double String
+    ArrayList<Double> doubleString = getValueList(response, "stddev", "fieldFacets", "string_sd", "double", false);
+    ArrayList<Double> doubleStringTest = calculateNumberStat(doubleStringTestStart, "stddev");
+    checkStddevs(response, doubleString, doubleStringTest);
+  }
+  
+  @SuppressWarnings("unchecked")
+  @Test
+  public void medianFacetAscTest() throws Exception { 
+    String[] params = new String[] {
+        "o.median.s.int", "median(int_id)",
+        "o.median.s.long", "median(long_ld)",
+        "o.median.s.float", "median(float_fd)",
+        "o.median.s.double", "median(double_dd)",
+        "o.median.ff", "string_sd",
+        "o.median.ff", "date_dtd"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int Date
+    Collection<Double> intDate = getValueList(response, "median","fieldFacets", "date_dtd", "int", false);
+    ArrayList<Double> intDateTest = calculateNumberStat(intDateTestStart, "median");
+    assertEquals(responseStr,intDate,intDateTest);
+    //Int String
+    Collection<Double> intString = getValueList(response, "median", "fieldFacets", "string_sd", "int", false);
+    ArrayList<Double> intStringTest = calculateNumberStat(intStringTestStart, "median");
+    assertEquals(responseStr,intString,intStringTest);
+
+    //Long Date
+    Collection<Double> longDate = getValueList(response, "median", "fieldFacets", "date_dtd", "long", false);
+    ArrayList<Double> longDateTest = calculateNumberStat(longDateTestStart, "median");
+    assertEquals(responseStr,longDate,longDateTest);
+    //Long String
+    Collection<Double> longString = getValueList(response, "median", "fieldFacets", "string_sd", "long", false);
+    ArrayList<Double> longStringTest = calculateNumberStat(longStringTestStart, "median");
+    assertEquals(responseStr,longString,longStringTest);
+
+    //Float Date
+    Collection<Double> floatDate = getValueList(response, "median", "fieldFacets", "date_dtd", "float", false);
+    ArrayList<Double> floatDateTest = calculateNumberStat(floatDateTestStart, "median");
+    assertEquals(responseStr,floatDate,floatDateTest);
+    //Float String
+    Collection<Double> floatString = getValueList(response, "median", "fieldFacets", "string_sd", "float", false);
+    ArrayList<Double> floatStringTest = calculateNumberStat(floatStringTestStart, "median");
+    assertEquals(responseStr,floatString,floatStringTest);
+
+    //Double Date
+    Collection<Double> doubleDate = getValueList(response, "median", "fieldFacets", "date_dtd", "double", false);
+    ArrayList<Double> doubleDateTest = calculateNumberStat(doubleDateTestStart, "median");
+    assertEquals(responseStr,doubleDate,doubleDateTest);
+    //Double String
+    Collection<Double> doubleString = getValueList(response, "median", "fieldFacets", "string_sd", "double", false);
+    ArrayList<Double> doubleStringTest = calculateNumberStat(doubleStringTestStart, "median");
+    assertEquals(responseStr,doubleString,doubleStringTest);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Test
+  public void perc20Test() throws Exception { 
+    String[] params = new String[] {
+        "o.percentile_20n.s.int", "percentile(20,int_id)",
+        "o.percentile_20n.s.long", "percentile(20,long_ld)",
+        "o.percentile_20n.s.float", "percentile(20,float_fd)",
+        "o.percentile_20n.s.double", "percentile(20,double_dd)",
+        "o.percentile_20n.ff", "string_sd",
+        "o.percentile_20n.ff", "date_dtd",
+
+        "o.percentile_20.s.str", "percentile(20,string_sd)",
+        "o.percentile_20.s.date", "string(percentile(20,date_dtd))",
+        "o.percentile_20.ff", "int_id",
+        "o.percentile_20.ff", "long_ld"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int Date
+    Collection<Integer> intDate = getValueList(response, "percentile_20n", "fieldFacets", "date_dtd", "int", false);
+    ArrayList<Integer> intDateTest = (ArrayList<Integer>)calculateStat(intDateTestStart, "perc_20");
+    assertEquals(responseStr,intDate,intDateTest);
+    //Int String
+    Collection<Integer> intString = getValueList(response, "percentile_20n", "fieldFacets", "string_sd", "int", false);
+    ArrayList<Integer> intStringTest = (ArrayList<Integer>)calculateStat(intStringTestStart, "perc_20");
+    assertEquals(responseStr,intString,intStringTest);
+
+    //Long Date
+    Collection<Long> longDate = getValueList(response, "percentile_20n", "fieldFacets", "date_dtd", "long", false);
+    ArrayList<Long> longDateTest = (ArrayList<Long>)calculateStat(longDateTestStart, "perc_20");
+    assertEquals(responseStr,longDate,longDateTest);
+    //Long String
+    Collection<Long> longString = getValueList(response, "percentile_20n", "fieldFacets", "string_sd", "long", false);
+    ArrayList<Long> longStringTest = (ArrayList<Long>)calculateStat(longStringTestStart, "perc_20");
+    assertEquals(responseStr,longString,longStringTest);
+
+    //Float Date
+    Collection<Float> floatDate = getValueList(response, "percentile_20n", "fieldFacets", "date_dtd", "float", false);
+    ArrayList<Float> floatDateTest = (ArrayList<Float>)calculateStat(floatDateTestStart, "perc_20");
+    assertEquals(responseStr,floatDate,floatDateTest);
+    //Float String
+    Collection<Float> floatString = getValueList(response, "percentile_20n", "fieldFacets", "string_sd", "float", false);
+    ArrayList<Float> floatStringTest = (ArrayList<Float>)calculateStat(floatStringTestStart, "perc_20");
+    assertEquals(responseStr,floatString,floatStringTest);
+
+    //Double Date
+    Collection<Double> doubleDate = getValueList(response, "percentile_20n", "fieldFacets", "date_dtd", "double", false);
+    ArrayList<Double> doubleDateTest = (ArrayList<Double>)calculateStat(doubleDateTestStart, "perc_20");
+    assertEquals(responseStr,doubleDate,doubleDateTest);
+    //Double String
+    Collection<Double> doubleString = getValueList(response, "percentile_20n", "fieldFacets", "string_sd", "double", false);
+    ArrayList<Double> doubleStringTest = (ArrayList<Double>)calculateStat(doubleStringTestStart, "perc_20");
+    assertEquals(responseStr,doubleString,doubleStringTest);
+
+    //Date Int
+    Collection<String> dateInt = getValueList(response, "percentile_20", "fieldFacets", "int_id", "date", false);
+    ArrayList<String> dateIntTest = (ArrayList<String>)calculateStat(dateIntTestStart, "perc_20");
+    assertEquals(responseStr,dateInt,dateIntTest);
+    //Date Long
+    Collection<String> dateString = getValueList(response, "percentile_20", "fieldFacets", "long_ld", "date", false);
+    ArrayList<String> dateLongTest = (ArrayList<String>)calculateStat(dateLongTestStart, "perc_20");
+    assertEquals(responseStr,dateString,dateLongTest);
+
+    //String Int
+    Collection<String> stringInt = getValueList(response, "percentile_20", "fieldFacets", "int_id", "str", false);
+    ArrayList<String> stringIntTest = (ArrayList<String>)calculateStat(stringIntTestStart, "perc_20");
+    assertEquals(responseStr,stringInt,stringIntTest);
+    //String Long
+    Collection<String> stringLong = getValueList(response, "percentile_20", "fieldFacets", "long_ld", "str", false);
+    ArrayList<String> stringLongTest = (ArrayList<String>)calculateStat(stringLongTestStart, "perc_20");
+    assertEquals(responseStr,stringLong,stringLongTest);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Test
+  public void perc60Test() throws Exception { 
+    String[] params = new String[] {
+        "o.percentile_60n.s.int", "percentile(60,int_id)",
+        "o.percentile_60n.s.long", "percentile(60,long_ld)",
+        "o.percentile_60n.s.float", "percentile(60,float_fd)",
+        "o.percentile_60n.s.double", "percentile(60,double_dd)",
+        "o.percentile_60n.ff", "string_sd",
+        "o.percentile_60n.ff", "date_dtd",
+
+        "o.percentile_60.s.str", "percentile(60,string_sd)",
+        "o.percentile_60.s.date", "string(percentile(60,date_dtd))",
+        "o.percentile_60.ff", "int_id",
+        "o.percentile_60.ff", "long_ld"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int Date
+    Collection<Integer> intDate = getValueList(response, "percentile_60n", "fieldFacets", "date_dtd", "int", false);
+    ArrayList<Integer> intDateTest = (ArrayList<Integer>)calculateStat(intDateTestStart, "perc_60");
+    assertEquals(responseStr,intDate,intDateTest);
+    //Int String
+    Collection<Integer> intString = getValueList(response, "percentile_60n", "fieldFacets", "string_sd", "int", false);
+    ArrayList<Integer> intStringTest = (ArrayList<Integer>)calculateStat(intStringTestStart, "perc_60");
+    assertEquals(responseStr,intString,intStringTest);
+
+    //Long Date
+    Collection<Long> longDate = getValueList(response, "percentile_60n", "fieldFacets", "date_dtd", "long", false);
+    ArrayList<Long> longDateTest = (ArrayList<Long>)calculateStat(longDateTestStart, "perc_60");
+    assertEquals(responseStr,longDate,longDateTest);
+    //Long String
+    Collection<Long> longString = getValueList(response, "percentile_60n", "fieldFacets", "string_sd", "long", false);
+    ArrayList<Long> longStringTest = (ArrayList<Long>)calculateStat(longStringTestStart, "perc_60");
+    assertEquals(responseStr,longString,longStringTest);
+
+    //Float Date
+    Collection<Float> floatDate = getValueList(response, "percentile_60n", "fieldFacets", "date_dtd", "float", false);
+    ArrayList<Float> floatDateTest = (ArrayList<Float>)calculateStat(floatDateTestStart, "perc_60");
+    assertEquals(responseStr,floatDate,floatDateTest);
+    //Float String
+    Collection<Float> floatString = getValueList(response, "percentile_60n", "fieldFacets", "string_sd", "float", false);
+    ArrayList<Float> floatStringTest = (ArrayList<Float>)calculateStat(floatStringTestStart, "perc_60");
+    assertEquals(responseStr,floatString,floatStringTest);
+
+    //Double Date
+    Collection<Double> doubleDate = getValueList(response, "percentile_60n", "fieldFacets", "date_dtd", "double", false);
+    ArrayList<Double> doubleDateTest = (ArrayList<Double>)calculateStat(doubleDateTestStart, "perc_60");
+    assertEquals(responseStr,doubleDate,doubleDateTest);
+    //Double String
+    Collection<Double> doubleString = getValueList(response, "percentile_60n", "fieldFacets", "string_sd", "double", false);
+    ArrayList<Double> doubleStringTest = (ArrayList<Double>)calculateStat(doubleStringTestStart, "perc_60");
+    assertEquals(responseStr,doubleString,doubleStringTest);
+
+    //Date Int
+    Collection<String> dateInt = getValueList(response, "percentile_60", "fieldFacets", "int_id", "date", false);
+    ArrayList<String> dateIntTest = (ArrayList<String>)calculateStat(dateIntTestStart, "perc_60");
+    assertEquals(responseStr,dateInt,dateIntTest);
+    //Date Long
+    Collection<String> dateString = getValueList(response, "percentile_60", "fieldFacets", "long_ld", "date", false);
+    ArrayList<String> dateLongTest = (ArrayList<String>)calculateStat(dateLongTestStart, "perc_60");
+    assertEquals(responseStr,dateString,dateLongTest);
+
+    //String Int
+    Collection<String> stringInt = getValueList(response, "percentile_60", "fieldFacets", "int_id", "str", false);
+    ArrayList<String> stringIntTest = (ArrayList<String>)calculateStat(stringIntTestStart, "perc_60");
+    assertEquals(responseStr,stringInt,stringIntTest);
+    //String Long
+    Collection<String> stringLong = getValueList(response, "percentile_60", "fieldFacets", "long_ld", "str", false);
+    ArrayList<String> stringLongTest = (ArrayList<String>)calculateStat(stringLongTestStart, "perc_60");
+    assertEquals(responseStr,stringLong,stringLongTest);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Test
+  public void minTest() throws Exception { 
+    String[] params = new String[] {
+        "o.minn.s.int", "min(int_id)",
+        "o.minn.s.long", "min(long_ld)",
+        "o.minn.s.float", "min(float_fd)",
+        "o.minn.s.double", "min(double_dd)",
+        "o.minn.ff", "string_sd",
+        "o.minn.ff", "date_dtd",
+
+        "o.min.s.str", "min(string_sd)",
+        "o.min.s.date", "string(min(date_dtd))",
+        "o.min.ff", "int_id",
+        "o.min.ff", "long_ld"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int Date
+    Collection<Integer> intDate = getValueList(response, "minn", "fieldFacets", "date_dtd", "int", false);
+    ArrayList<Integer> intDateTest = (ArrayList<Integer>)calculateStat(intDateTestStart, "min");
+    assertEquals(responseStr,intDate,intDateTest);
+    //Int String
+    Collection<Integer> intString = getValueList(response, "minn", "fieldFacets", "string_sd", "int", false);
+    ArrayList<Integer> intStringTest = (ArrayList<Integer>)calculateStat(intStringTestStart, "min");
+    assertEquals(responseStr,intString,intStringTest);
+
+    //Long Date
+    Collection<Long> longDate = getValueList(response, "minn", "fieldFacets", "date_dtd", "long", false);
+    ArrayList<Long> longDateTest = (ArrayList<Long>)calculateStat(longDateTestStart, "min");
+    assertEquals(responseStr,longDate,longDateTest);
+    //Long String
+    Collection<Long> longString = getValueList(response, "minn", "fieldFacets", "string_sd", "long", false);
+    ArrayList<Long> longStringTest = (ArrayList<Long>)calculateStat(longStringTestStart, "min");
+    assertEquals(responseStr,longString,longStringTest);
+
+    //Float Date
+    Collection<Float> floatDate = getValueList(response, "minn", "fieldFacets", "date_dtd", "float", false);
+    ArrayList<Float> floatDateTest = (ArrayList<Float>)calculateStat(floatDateTestStart, "min");
+    assertEquals(responseStr,floatDate,floatDateTest);
+    //Float String
+    Collection<Float> floatString = getValueList(response, "minn", "fieldFacets", "string_sd", "float", false);
+    ArrayList<Float> floatStringTest = (ArrayList<Float>)calculateStat(floatStringTestStart, "min");
+    assertEquals(responseStr,floatString,floatStringTest);
+
+    //Double Date
+    Collection<Double> doubleDate = getValueList(response, "minn", "fieldFacets", "date_dtd", "double", false);
+    ArrayList<Double> doubleDateTest = (ArrayList<Double>)calculateStat(doubleDateTestStart, "min");
+    assertEquals(responseStr,doubleDate,doubleDateTest);
+    //Double String
+    Collection<Double> doubleString = getValueList(response, "minn", "fieldFacets", "string_sd", "double", false);
+    ArrayList<Double> doubleStringTest = (ArrayList<Double>)calculateStat(doubleStringTestStart, "min");
+    assertEquals(responseStr,doubleString,doubleStringTest);
+
+    //Date Int
+    Collection<String> dateInt = getValueList(response, "min", "fieldFacets", "int_id", "date", false);
+    ArrayList<String> dateIntTest = (ArrayList<String>)calculateStat(dateIntTestStart, "min");
+    assertEquals(responseStr,dateInt,dateIntTest);
+    //Date Long
+    Collection<String> dateString = getValueList(response, "min", "fieldFacets", "long_ld", "date", false);
+    ArrayList<String> dateLongTest = (ArrayList<String>)calculateStat(dateLongTestStart, "min");
+    assertEquals(responseStr,dateString,dateLongTest);
+
+    //String Int
+    Collection<String> stringInt = getValueList(response, "min", "fieldFacets", "int_id", "str", false);
+    ArrayList<String> stringIntTest = (ArrayList<String>)calculateStat(stringIntTestStart, "min");
+    assertEquals(responseStr,stringInt,stringIntTest);
+    //String Long
+    Collection<String> stringLong = getValueList(response, "min", "fieldFacets", "long_ld", "str", false);
+    ArrayList<String> stringLongTest = (ArrayList<String>)calculateStat(stringLongTestStart, "min");
+    assertEquals(responseStr,stringLong,stringLongTest);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Test
+  public void maxTest() throws Exception { 
+    String[] params = new String[] {
+        "o.maxn.s.int", "max(int_id)",
+        "o.maxn.s.long", "max(long_ld)",
+        "o.maxn.s.float", "max(float_fd)",
+        "o.maxn.s.double", "max(double_dd)",
+        "o.maxn.ff", "string_sd",
+        "o.maxn.ff", "date_dtd",
+
+        "o.max.s.str", "max(string_sd)",
+        "o.max.s.date", "string(max(date_dtd))",
+        "o.max.ff", "int_id",
+        "o.max.ff", "long_ld"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int Date
+    Collection<Integer> intDate = getValueList(response, "maxn", "fieldFacets", "date_dtd", "int", false);
+    ArrayList<Integer> intDateTest = (ArrayList<Integer>)calculateStat(intDateTestStart, "max");
+    //assertEquals(responseStr,intDate,intDateTest);
+    
+    //Int String
+    Collection<Integer> intString = getValueList(response, "maxn", "fieldFacets", "string_sd", "int", false);
+    ArrayList<Integer> intStringTest = (ArrayList<Integer>)calculateStat(intStringTestStart, "max");
+    assertEquals(responseStr,intString,intStringTest);
+
+    //Long Date
+    Collection<Long> longDate = getValueList(response, "maxn", "fieldFacets", "date_dtd", "long", false);
+    ArrayList<Long> longDateTest = (ArrayList<Long>)calculateStat(longDateTestStart, "max");
+    assertEquals(responseStr,longDate,longDateTest);
+    
+    //Long String
+    Collection<Long> longString = getValueList(response, "maxn", "fieldFacets", "string_sd", "long", false);
+    ArrayList<Long> longStringTest = (ArrayList<Long>)calculateStat(longStringTestStart, "max");
+    assertEquals(responseStr,longString,longStringTest);
+
+    //Float Date
+    Collection<Float> floatDate = getValueList(response, "maxn", "fieldFacets", "date_dtd", "float", false);
+    ArrayList<Float> floatDateTest = (ArrayList<Float>)calculateStat(floatDateTestStart, "max");
+    assertEquals(responseStr,floatDate,floatDateTest);
+    
+    //Float String
+    Collection<Float> floatString = getValueList(response, "maxn", "fieldFacets", "string_sd", "float", false);
+    ArrayList<Float> floatStringTest = (ArrayList<Float>)calculateStat(floatStringTestStart, "max");
+    assertEquals(responseStr,floatString,floatStringTest);
+
+    //Double Date
+    Collection<Double> doubleDate = getValueList(response, "maxn", "fieldFacets", "date_dtd", "double", false);
+    ArrayList<Double> doubleDateTest = (ArrayList<Double>)calculateStat(doubleDateTestStart, "max");
+    assertEquals(responseStr,doubleDate,doubleDateTest);
+    
+    //Double String
+    Collection<Double> doubleString = getValueList(response, "maxn", "fieldFacets", "string_sd", "double", false);
+    ArrayList<Double> doubleStringTest = (ArrayList<Double>)calculateStat(doubleStringTestStart, "max");
+    assertEquals(responseStr,doubleString,doubleStringTest);
+    
+    //String Int
+    Collection<String> stringInt = getValueList(response, "max", "fieldFacets", "int_id", "str", false);
+    ArrayList<String> stringIntTest = (ArrayList<String>)calculateStat(stringIntTestStart, "max");
+    assertEquals(responseStr,stringInt,stringIntTest);
+    
+    //String Long
+    Collection<String> stringLong = getValueList(response, "max", "fieldFacets", "long_ld", "str", false);
+    ArrayList<String> stringLongTest = (ArrayList<String>)calculateStat(stringLongTestStart, "max");
+    assertEquals(responseStr,stringLong,stringLongTest);
+
+    //Date Int
+    Collection<String> dateInt = getValueList(response, "max", "fieldFacets", "int_id", "date", false);
+    ArrayList<String> dateIntTest = (ArrayList<String>)calculateStat(dateIntTestStart, "max");
+    assertEquals(responseStr,dateInt,dateIntTest);
+    
+    //Date Long
+    Collection<String> dateString = getValueList(response, "max", "fieldFacets", "long_ld", "date", false);
+    ArrayList<String> dateLongTest = (ArrayList<String>)calculateStat(dateLongTestStart, "max");
+    assertEquals(responseStr,dateString,dateLongTest);
+
+  }
+
+  @SuppressWarnings("unchecked")
+  @Test
+  public void uniqueTest() throws Exception { 
+    String[] params = new String[] {
+        "o.uniquen.s.int", "unique(int_id)",
+        "o.uniquen.s.long", "unique(long_ld)",
+        "o.uniquen.s.float", "unique(float_fd)",
+        "o.uniquen.s.double", "unique(double_dd)",
+        "o.uniquen.ff", "string_sd",
+        "o.uniquen.ff", "date_dtd",
+
+        "o.unique.s.str", "unique(string_sd)",
+        "o.unique.s.date", "unique(date_dtd)",
+        "o.unique.ff", "int_id",
+        "o.unique.ff", "long_ld"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int Date
+    Collection<Long> intDate = getValueList(response, "uniquen", "fieldFacets", "date_dtd", "int", false);
+    ArrayList<Long> intDateTest = (ArrayList<Long>)calculateStat(intDateTestStart, "unique");
+    assertEquals(responseStr,intDate,intDateTest);
+    //Int String
+    Collection<Long> intString = getValueList(response, "uniquen", "fieldFacets", "string_sd", "int", false);
+    ArrayList<Long> intStringTest = (ArrayList<Long>)calculateStat(intStringTestStart, "unique");
+    assertEquals(responseStr,intString,intStringTest);
+
+    //Long Date
+    Collection<Long> longDate = getValueList(response, "uniquen", "fieldFacets", "date_dtd", "long", false);
+    ArrayList<Long> longDateTest = (ArrayList<Long>)calculateStat(longDateTestStart, "unique");
+    assertEquals(responseStr,longDate,longDateTest);
+    //Long String
+    Collection<Long> longString = getValueList(response, "uniquen", "fieldFacets", "string_sd", "long", false);
+    ArrayList<Long> longStringTest = (ArrayList<Long>)calculateStat(longStringTestStart, "unique");
+    assertEquals(responseStr,longString,longStringTest);
+
+    //Float Date
+    Collection<Long> floatDate = getValueList(response, "uniquen", "fieldFacets", "date_dtd", "float", false);
+    ArrayList<Long> floatDateTest = (ArrayList<Long>)calculateStat(floatDateTestStart, "unique");
+    assertEquals(responseStr,floatDate,floatDateTest);
+    //Float String
+    Collection<Long> floatString = getValueList(response, "uniquen", "fieldFacets", "string_sd", "float", false);
+    ArrayList<Long> floatStringTest = (ArrayList<Long>)calculateStat(floatStringTestStart, "unique");
+    assertEquals(responseStr,floatString,floatStringTest);
+
+    //Double Date
+    Collection<Long> doubleDate = getValueList(response, "uniquen", "fieldFacets", "date_dtd", "double", false);
+    ArrayList<Long> doubleDateTest = (ArrayList<Long>)calculateStat(doubleDateTestStart, "unique");
+    assertEquals(responseStr,doubleDate,doubleDateTest);
+    //Double String
+    Collection<Long> doubleString = getValueList(response, "uniquen", "fieldFacets", "string_sd", "double", false);
+    ArrayList<Long> doubleStringTest = (ArrayList<Long>)calculateStat(doubleStringTestStart, "unique");
+    assertEquals(responseStr,doubleString,doubleStringTest);
+
+    //Date Int
+    Collection<Long> dateInt = getValueList(response, "unique", "fieldFacets", "int_id", "date", false);
+    ArrayList<Long> dateIntTest = (ArrayList<Long>)calculateStat(dateIntTestStart, "unique");
+    assertEquals(responseStr,dateInt,dateIntTest);
+    //Date Long
+    Collection<Long> dateString = getValueList(response, "unique", "fieldFacets", "long_ld", "date", false);
+    ArrayList<Long> dateLongTest = (ArrayList<Long>)calculateStat(dateLongTestStart, "unique");
+    assertEquals(responseStr,dateString,dateLongTest);
+
+    //String Int
+    Collection<Long> stringInt = getValueList(response, "unique", "fieldFacets", "int_id", "str", false);
+    ArrayList<Long> stringIntTest = (ArrayList<Long>)calculateStat(stringIntTestStart, "unique");
+    assertEquals(responseStr,stringInt,stringIntTest);
+    //String Long
+    Collection<Long> stringLong = getValueList(response, "unique", "fieldFacets", "long_ld", "str", false);
+    ArrayList<Long> stringLongTest = (ArrayList<Long>)calculateStat(stringLongTestStart, "unique");
+    assertEquals(responseStr,stringLong,stringLongTest);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Test
+  public void countTest() throws Exception { 
+    String[] params = new String[] {
+        "o.countn.s.int", "count(int_id)",
+        "o.countn.s.long", "count(long_ld)",
+        "o.countn.s.float", "count(float_fd)",
+        "o.countn.s.double", "count(double_dd)",
+        "o.countn.ff", "string_sd",
+        "o.countn.ff", "date_dtd",
+
+        "o.count.s.str", "count(string_sd)",
+        "o.count.s.date", "count(date_dtd)",
+        "o.count.ff", "int_id",
+        "o.count.ff", "long_ld"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int Date
+    Collection<Long> intDate = getValueList(response, "countn", "fieldFacets", "date_dtd", "int", false);
+    ArrayList<Long> intDateTest = (ArrayList<Long>)calculateStat(intDateTestStart, "count");
+    assertEquals(responseStr,intDate,intDateTest);
+    
+    //Int String
+    Collection<Long> intString = getValueList(response, "countn", "fieldFacets", "string_sd", "int", false);
+    ArrayList<Long> intStringTest = (ArrayList<Long>)calculateStat(intStringTestStart, "count");
+    assertEquals(responseStr,intString,intStringTest);
+
+    //Long Date
+    Collection<Long> longDate = getValueList(response, "countn", "fieldFacets", "date_dtd", "long", false);
+    ArrayList<Long> longDateTest = (ArrayList<Long>)calculateStat(longDateTestStart, "count");
+    assertEquals(responseStr,longDate,longDateTest);
+    
+    //Long String
+    Collection<Long> longString = getValueList(response, "countn", "fieldFacets", "string_sd", "long", false);
+    ArrayList<Long> longStringTest = (ArrayList<Long>)calculateStat(longStringTestStart, "count");
+    assertEquals(responseStr,longString,longStringTest);
+
+    //Float Date
+    Collection<Long> floatDate = getValueList(response, "countn", "fieldFacets", "date_dtd", "float", false);
+    ArrayList<Long> floatDateTest = (ArrayList<Long>)calculateStat(floatDateTestStart, "count");
+    assertEquals(responseStr,floatDate,floatDateTest);
+    
+    //Float String
+    Collection<Long> floatString = getValueList(response, "countn", "fieldFacets", "string_sd", "float", false);
+    ArrayList<Long> floatStringTest = (ArrayList<Long>)calculateStat(floatStringTestStart, "count");
+    assertEquals(responseStr,floatString,floatStringTest);
+
+    //Double Date
+    Collection<Long> doubleDate = getValueList(response, "countn", "fieldFacets", "date_dtd", "double", false);
+    ArrayList<Long> doubleDateTest = (ArrayList<Long>)calculateStat(doubleDateTestStart, "count");
+    assertEquals(responseStr,doubleDate,doubleDateTest);
+    
+    //Double String
+    Collection<Long> doubleString = getValueList(response, "countn", "fieldFacets", "string_sd", "double", false);
+    ArrayList<Long> doubleStringTest = (ArrayList<Long>)calculateStat(doubleStringTestStart, "count");
+    assertEquals(responseStr,doubleString,doubleStringTest);
+
+    //Date Int
+    Collection<Long> dateInt = getValueList(response, "count", "fieldFacets", "int_id", "date", false);
+    ArrayList<Long> dateIntTest = (ArrayList<Long>)calculateStat(dateIntTestStart, "count");
+    assertEquals(responseStr,dateIntTest,dateInt);
+    
+    //Date Long
+    Collection<Long> dateLong = getValueList(response, "count", "fieldFacets", "long_ld", "date", false);
+    ArrayList<Long> dateLongTest = (ArrayList<Long>)calculateStat(dateLongTestStart, "count");
+    assertEquals(responseStr,dateLong,dateLongTest);
+
+    //String Int
+    Collection<Long> stringInt = getValueList(response, "count", "fieldFacets", "int_id", "str", false);
+    ArrayList<Long> stringIntTest = (ArrayList<Long>)calculateStat(stringIntTestStart, "count");
+    assertEquals(responseStr,stringInt,stringIntTest);
+    
+    //String Long
+    Collection<Long> stringLong = getValueList(response, "count", "fieldFacets", "long_ld", "str", false);
+    ArrayList<Long> stringLongTest = (ArrayList<Long>)calculateStat(stringLongTestStart, "count");
+    assertEquals(responseStr,stringLong,stringLongTest);
+  }
+
+  @SuppressWarnings("unchecked")
+  @Test
+  public void missingTest() throws Exception { 
+    String[] params = new String[] {
+        "o.missingn.s.int", "missing(int_id)",
+        "o.missingn.s.long", "missing(long_ld)",
+        "o.missingn.s.float", "missing(float_fd)",
+        "o.missingn.s.double", "missing(double_dd)",
+        "o.missingn.ff", "string_sd",
+        "o.missingn.ff", "date_dtd",
+
+        "o.missing.s.str", "missing(string_sd)",
+        "o.missing.s.date", "missing(date_dtd)",
+        "o.missing.ff", "int_id",
+        "o.missing.ff", "long_ld"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int Date
+    Collection<Long> intDate = getValueList(response, "missingn", "fieldFacets", "date_dtd", "int", false);
+    setLatestType("int");
+    assertEquals(responseStr,intDateTestMissing,intDate);
+    
+    //Int String
+    Collection<Long> intString = getValueList(response, "missingn", "fieldFacets", "string_sd", "int", false);
+    assertEquals(responseStr,intStringTestMissing,intString);
+
+    //Long Date
+    Collection<Long> longDate = getValueList(response, "missingn", "fieldFacets", "date_dtd", "long", false);
+    setLatestType("long");
+    assertEquals(responseStr,longDateTestMissing,longDate);
+    
+    //Long String
+    Collection<Long> longString = getValueList(response, "missingn", "fieldFacets", "string_sd", "long", false);
+    assertEquals(responseStr,longStringTestMissing,longString);
+
+    //Float Date
+    Collection<Long> floatDate = getValueList(response, "missingn", "fieldFacets", "date_dtd", "float", false);
+    setLatestType("float");
+    assertEquals(responseStr,floatDateTestMissing,floatDate);
+    
+    //Float String
+    Collection<Long> floatString = getValueList(response, "missingn", "fieldFacets", "string_sd", "float", false);
+    assertEquals(responseStr,floatStringTestMissing,floatString);
+
+    //Double Date
+    Collection<Long> doubleDate = getValueList(response, "missingn", "fieldFacets", "date_dtd", "double", false);
+    setLatestType("double");
+    assertEquals(responseStr,doubleDateTestMissing,doubleDate);
+    
+    //Double String
+    Collection<Long> doubleString = getValueList(response, "missingn", "fieldFacets", "string_sd", "double", false);
+    assertEquals(responseStr,doubleStringTestMissing,doubleString);
+
+    //Date Int
+    Collection<Long> dateInt = getValueList(response, "missing", "fieldFacets", "int_id", "date", false);
+    setLatestType("date");
+    assertEquals(responseStr,dateIntTestMissing,dateInt);
+    
+    //Date Long
+    Collection<Long> dateLong = getValueList(response, "missing", "fieldFacets", "long_ld", "date", false);
+    assertEquals(responseStr,dateLongTestMissing,dateLong);
+
+    //String Int
+    Collection<Long> stringInt = getValueList(response, "missing", "fieldFacets", "int_id", "str", false);
+    setLatestType("string");
+    assertEquals(responseStr,stringIntTestMissing,stringInt);
+    
+    //String Long
+    Collection<Long> stringLong = getValueList(response, "missing", "fieldFacets", "long_ld", "str", false);
+    assertEquals(responseStr,stringLongTestMissing,stringLong);
+  }
+  
+  @SuppressWarnings("unchecked")
+  @Test
+  public void multiValueTest() throws Exception { 
+    String[] params = new String[] {
+        "o.multivalued.s.mean", "mean(int_id)",
+        "o.multivalued.ff", "long_ldm",
+        "o.multivalued.ff", "string_sdm",
+        "o.multivalued.ff", "date_dtdm"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Long
+    Collection<Double> lon = getValueList(response, "multivalued", "fieldFacets", "long_ldm", "mean", false);
+    ArrayList<Double> longTest = calculateNumberStat(multiLongTestStart, "mean");
+    assertEquals(responseStr,lon,longTest);
+    //Date
+    Collection<Double> date = getValueList(response, "multivalued", "fieldFacets", "date_dtdm", "mean", false);
+    ArrayList<Double> dateTest = calculateNumberStat(multiDateTestStart, "mean");
+    assertEquals(responseStr,date,dateTest);
+    //String
+    Collection<Double> string = getValueList(response, "multivalued", "fieldFacets", "string_sdm", "mean", false);
+    ArrayList<Double> stringTest = calculateNumberStat(multiStringTestStart, "mean");
+    assertEquals(responseStr,string,stringTest);
+  }
+  
+  @SuppressWarnings("unchecked")
+  @Test
+  public void missingFacetTest() throws Exception { 
+    String[] params = new String[] {
+        "o.func.facet_show_missing(a)", "fillmissing(a,\"(MISSING)\")",
+        "o.missingf.s.mean", "mean(int_id)",
+        "o.missingf.ff", "date_dtd",
+        "o.missingf.ff", "string_sd",
+        "o.missingf.ff.string_sd.sm", "true",
+        "o.missingf.ff", "date_dtdm",
+        "o.missingf.ff.date_dtdm.sm", "true"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //int MultiDate
+    assertTrue(responseStr, responseContainsFacetValue(response, "missingf", "fieldFacets", "date_dtdm", "(MISSING)"));
+    ArrayList<Double> string = getValueList(response, "missingf", "fieldFacets", "date_dtdm", "mean", false);
+    ArrayList<Double> stringTest = calculateNumberStat(multiDateTestStart, "mean");
+    assertEquals(responseStr, string,stringTest);
+    
+    //Int String
+    assertTrue(responseStr, responseContainsFacetValue(response, "missingf", "fieldFacets", "string_sd", "(MISSING)"));
+    assertTrue(responseStr, !responseContainsFacetValue(response, "missingf", "fieldFacets", "string_sd", "str0"));
+    List<Double> intString = getValueList(response, "missingf", "fieldFacets", "string_sd", "mean", false);
+    ArrayList<Double> intStringTest = calculateNumberStat(intStringTestStart, "mean");
+    assertEquals(responseStr, intString,intStringTest);
+    
+    //Int Date
+    Collection<Double> intDate = getValueList(response, "missingf", "fieldFacets", "date_dtd", "mean", false);
+    ArrayList<ArrayList<Double>> intDateMissingTestStart = (ArrayList<ArrayList<Double>>) intDateTestStart.clone();
+    ArrayList<Double> intDateTest = calculateNumberStat(intDateMissingTestStart, "mean");
+    assertEquals(responseStr,intDate,intDateTest);
+  }
+
+  private void checkStddevs(NamedList<Object> response, ArrayList<Double> list1, ArrayList<Double> list2) {
+    Collections.sort(list1);
+    Collections.sort(list2);
+    for (int i = 0; i<list2.size(); i++) {
+      if ((Math.abs(list1.get(i)-list2.get(i))<.00000000001) == false) {
+        Assert.assertEquals(response.toString(), list1.get(i), list2.get(i), 0.00000000001);
+      }
+    }
+  }
+
+  public static void assertEquals(String mes, Object actual, Object expected) {
+    Collections.sort((List<Comparable>) actual);
+    Collections.sort((List<Comparable>)  expected);
+    Assert.assertEquals(mes, actual, expected);
+  }
+}


[18/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/FieldFacetAccumulator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/FieldFacetAccumulator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/FieldFacetAccumulator.java
deleted file mode 100644
index fb0884b..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/FieldFacetAccumulator.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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.solr.analytics.accumulator.facet;
-
-import java.io.IOException;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.NumericDocValues;
-import org.apache.lucene.index.SortedDocValues;
-import org.apache.lucene.index.SortedSetDocValues;
-import org.apache.lucene.util.BytesRef;
-import org.apache.solr.analytics.accumulator.FacetingAccumulator;
-import org.apache.solr.analytics.accumulator.ValueAccumulator;
-import org.apache.solr.analytics.util.AnalyticsParsers.NumericParser;
-import org.apache.solr.analytics.util.AnalyticsParsers.Parser;
-import org.apache.solr.analytics.util.AnalyticsParsers;
-import org.apache.solr.common.util.NamedList;
-import org.apache.solr.schema.DateValueFieldType;
-import org.apache.solr.schema.SchemaField;
-import org.apache.solr.search.SolrIndexSearcher;
-
-/**
- * An Accumulator that manages the faceting for fieldFacets.
- * Collects the field facet values.
- */
-public class FieldFacetAccumulator extends ValueAccumulator {
-  protected final Parser parser;
-  protected final FacetValueAccumulator parent;
-  protected final String name;
-  protected final SolrIndexSearcher searcher;
-  protected final SchemaField schemaField;
-  protected final boolean multiValued;
-  protected final boolean numField;
-  protected final boolean dateField;
-  protected SortedSetDocValues setValues;
-  protected SortedDocValues sortValues;
-  protected NumericDocValues numValues;
-  
-  public FieldFacetAccumulator(SolrIndexSearcher searcher, FacetValueAccumulator parent, SchemaField schemaField) throws IOException {  
-    if( !schemaField.hasDocValues() ){
-      throw new IOException("Field '"+schemaField.getName()+"' does not have docValues and therefore cannot be faceted over.");
-    }
-    this.searcher = searcher;
-    this.schemaField = schemaField;
-    this.name = schemaField.getName();
-    this.multiValued = schemaField.multiValued();
-    this.numField = schemaField.getType().getNumberType()!=null;
-    this.dateField = schemaField.getType() instanceof DateValueFieldType;
-    this.parent = parent;  
-    this.parser = AnalyticsParsers.getParser(schemaField.getType().getClass());
-  }
-
-  public static FieldFacetAccumulator create(SolrIndexSearcher searcher, FacetValueAccumulator parent, SchemaField facetField) throws IOException{
-    return new FieldFacetAccumulator(searcher,parent,facetField);
-  }
-
-  /**
-   * Move to the next set of documents to add to the field facet.
-   */
-  @Override
-  protected void doSetNextReader(LeafReaderContext context) throws IOException {
-    if (multiValued) {
-      setValues = context.reader().getSortedSetDocValues(name);
-    } else {
-      if (numField) {
-        numValues = context.reader().getNumericDocValues(name);
-      } else {
-        sortValues = context.reader().getSortedDocValues(name);
-      }
-    }
-  }
-
-  /**
-   * Tell the FacetingAccumulator to collect the doc with the 
-   * given fieldFacet and value(s).
-   */
-  @Override
-  public void collect(int doc) throws IOException {
-    if (multiValued) {
-      boolean exists = false;
-      if (setValues!=null) {
-        if (doc > setValues.docID()) {
-          setValues.advance(doc);
-        }
-        if (doc == setValues.docID()) {
-          int term;
-          while ((term = (int)setValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) {
-            exists = true;
-            final BytesRef value = setValues.lookupOrd(term);
-            parent.collectField(doc, name, parser.parse(value) );
-          }
-        }
-      }
-      if (!exists) {
-        parent.collectField(doc, name, FacetingAccumulator.MISSING_VALUE );
-      }
-    } else {
-      if(numField){
-        if(numValues != null) {
-          int valuesDocID = numValues.docID();
-          if (valuesDocID < doc) {
-            valuesDocID = numValues.advance(doc);
-          }
-          if (valuesDocID == doc) {
-            parent.collectField(doc, name, ((NumericParser)parser).parseNum(numValues.longValue()));
-          } else {
-            parent.collectField(doc, name, FacetingAccumulator.MISSING_VALUE );
-          }
-        } else {
-          parent.collectField(doc, name, FacetingAccumulator.MISSING_VALUE );
-        }
-      } else {
-        if(sortValues != null) {
-          if (doc > sortValues.docID()) {
-            sortValues.advance(doc);
-          }
-          if (doc == sortValues.docID()) {
-            parent.collectField(doc, name, parser.parse(sortValues.lookupOrd(sortValues.ordValue())) );
-          } else {
-            parent.collectField(doc, name, FacetingAccumulator.MISSING_VALUE );
-          }
-        } else {
-          parent.collectField(doc, name, FacetingAccumulator.MISSING_VALUE );
-        }
-      }
-    }
-  }
-
-  @Override
-  public void compute() {}
- 
-  @Override
-  public NamedList<?> export() { return null; }
-
-  @Override
-  public boolean needsScores() {
-    return true; // TODO: is this true?
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/QueryFacetAccumulator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/QueryFacetAccumulator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/QueryFacetAccumulator.java
deleted file mode 100644
index 8b92ba0..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/QueryFacetAccumulator.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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.solr.analytics.accumulator.facet;
-
-import java.io.IOException;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.solr.analytics.accumulator.ValueAccumulator;
-import org.apache.solr.analytics.statistics.StatsCollector;
-import org.apache.solr.common.util.NamedList;
-
-/**
- * An Accumulator that manages a certain query of a given query facet.
- */
-public class QueryFacetAccumulator extends ValueAccumulator {
-  protected final FacetValueAccumulator parent;
-  protected final String facetName;
-  protected final String facetValue;
-
-  public QueryFacetAccumulator(FacetValueAccumulator parent, String facetName, String facetValue) {
-    this.parent = parent;
-    this.facetName = facetName;
-    this.facetValue = facetValue;
-  }
-
-  /**
-   * Tell the FacetingAccumulator to collect the doc with the 
-   * given queryFacet and query.
-   */
-  @Override
-  public void collect(int doc) throws IOException {
-    parent.collectQuery(doc, facetName, facetValue);
-  }
-
-  /**
-   * Update the readers of the queryFacet {@link StatsCollector}s in FacetingAccumulator
-   */
-  @Override
-  protected void doSetNextReader(LeafReaderContext context) throws IOException {
-    parent.setQueryStatsCollectorReaders(context);
-  }
-
-  @Override
-  public void compute() {
-    // NOP
-  }
-
-  @Override
-  public NamedList<?> export() {
-    // NOP
-    return null;
-  }
-
-  @Override
-  public boolean needsScores() {
-    return true; // TODO: is this true?
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/RangeFacetAccumulator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/RangeFacetAccumulator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/RangeFacetAccumulator.java
deleted file mode 100644
index 59cf428..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/RangeFacetAccumulator.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.solr.analytics.accumulator.facet;
-
-import java.io.IOException;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.solr.analytics.statistics.StatsCollector;
-
-/**
- * An Accumulator that manages a certain range of a given range facet.
- */
-public class RangeFacetAccumulator extends QueryFacetAccumulator {
-  public RangeFacetAccumulator(FacetValueAccumulator parent, String facetName, String facetValue) {
-    super(parent, facetName, facetValue);
-  }
-
-  /**
-   * Tell the FacetingAccumulator to collect the doc with the 
-   * given rangeFacet and range.
-   */
-  @Override
-  public void collect(int doc) throws IOException {
-    parent.collectRange(doc, facetName, facetValue);
-  }
-
-  /**
-   * Update the readers of the rangeFacet {@link StatsCollector}s in FacetingAccumulator
-   */
-  @Override
-  protected void doSetNextReader(LeafReaderContext context) throws IOException {
-    parent.setRangeStatsCollectorReaders(context);
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/package-info.java
deleted file mode 100644
index 3daf103..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/package-info.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.
- */
- 
-/** 
- * Accumulators for accumulating over differnt types of facets
-
- */
-package org.apache.solr.analytics.accumulator.facet;
-
-

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/package-info.java
deleted file mode 100644
index 0abe00a..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/package-info.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.
- */
- 
-/** 
- * Accumulators accumulate values over different types of strucuture (eg result, facet, etc..)
- */
-package org.apache.solr.analytics.accumulator;
-
-

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/BaseExpression.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/BaseExpression.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/BaseExpression.java
deleted file mode 100644
index 2f0326b..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/BaseExpression.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.solr.analytics.expression;
-
-import java.util.Date;
-
-import org.apache.solr.analytics.statistics.StatsCollector;
-
-
-/**
- * <code>BaseExpression</code> returns the value returned by the {@link StatsCollector} for the specified stat.
- */
-public class BaseExpression extends Expression {
-  protected final StatsCollector statsCollector;
-  protected final String stat;
-  
-  public BaseExpression(StatsCollector statsCollector, String stat) {
-    this.statsCollector = statsCollector;
-    this.stat = stat;
-  }
-  
-  public Comparable getValue() {
-    if(statsCollector.getStatsList().contains(stat)) {
-      return statsCollector.getStat(stat);
-    }
-    return null;
-  }
-}
-/**
- * <code>ConstantStringExpression</code> returns the specified constant double.
- */
-class ConstantNumberExpression extends Expression {
-  protected final Double constant;
-  
-  public ConstantNumberExpression(double d) {
-    constant = new Double(d);
-  }
-  
-  public Comparable getValue() {
-    return constant;
-  }
-}
-/**
- * <code>ConstantStringExpression</code> returns the specified constant date.
- */
-class ConstantDateExpression extends Expression {
-  protected final Date constant;
-  
-  public ConstantDateExpression(Date date) {
-    constant = date;
-  }
-  
-  public ConstantDateExpression(Long date) {
-    constant = new Date(date);
-  }
-  
-  public Comparable getValue() {
-    return constant;
-  }
-}
-/**
- * <code>ConstantStringExpression</code> returns the specified constant string.
- */
-class ConstantStringExpression extends Expression {
-  protected final String constant;
-  
-  public ConstantStringExpression(String str) {
-    constant = str;
-  }
-  
-  public Comparable getValue() {
-    return constant;
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/DualDelegateExpression.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/DualDelegateExpression.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/DualDelegateExpression.java
deleted file mode 100644
index f906b47..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/DualDelegateExpression.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.solr.analytics.expression;
-
-/**
- * Abstraction of an expression that applies a function to two delegate expressions.
- */
-public abstract class DualDelegateExpression extends Expression {
-  protected Expression a;
-  protected Expression b;
-  public DualDelegateExpression(Expression a, Expression b) {
-    this.a = a;
-    this.b = b;
-  }
-}
-/**
- * <code>DivideExpression</code> returns the quotient of 'a' and 'b'.
- */
-class DivideExpression extends DualDelegateExpression {
-  
-  /**
-   * @param a numerator
-   * @param b divisor
-   */
-  public DivideExpression(Expression a, Expression b) {
-    super(a,b);
-  }
-
-  @Override
-  public Comparable getValue() {
-    Comparable aComp = a.getValue();
-    Comparable bComp = b.getValue();
-    if (aComp==null || bComp==null) {
-      return null;
-    }
-    double div = ((Number)aComp).doubleValue();
-    div = div / ((Number)bComp).doubleValue();
-    return new Double(div);
-  }
-}
-/**
- * <code>PowerExpression</code> returns 'a' to the power of 'b'.
- */
-class PowerExpression extends DualDelegateExpression {
-
-  /**
-   * @param a base
-   * @param b exponent
-   */
-  public PowerExpression(Expression a, Expression b) {
-    super(a,b);
-  }
-
-  @Override
-  public Comparable getValue() {
-    Comparable aComp = a.getValue();
-    Comparable bComp = b.getValue();
-    if (aComp==null || bComp==null) {
-      return null;
-    }
-    return new Double(Math.pow(((Number)aComp).doubleValue(),((Number)bComp).doubleValue()));
-  }
-}
-/**
- * <code>LogExpression</code> returns the log of the delegate's value given a base number.
- */
-class LogExpression extends DualDelegateExpression {
-  /**
-   * @param a number
-   * @param b base
-   */
-  public LogExpression(Expression a, Expression b) {
-    super(a,b);
-  }
-
-  @Override
-  public Comparable getValue() {
-    Comparable aComp = a.getValue();
-    Comparable bComp = b.getValue();
-    if (aComp==null || bComp==null) {
-      return null;
-    }
-    return Math.log(((Number)aComp).doubleValue())/Math.log(((Number)bComp).doubleValue());
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/Expression.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/Expression.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/Expression.java
deleted file mode 100644
index ba26d9a..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/Expression.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.solr.analytics.expression;
-
-import java.util.Comparator;
-
-import org.apache.solr.analytics.request.FieldFacetRequest.FacetSortDirection;
-
-/**
- * Expressions map either zero, one, two or many inputs to a single value. 
- * They can be defined recursively to compute complex math.
- */
-public abstract class Expression {
-  public abstract Comparable getValue();
-
-  public Comparator<Expression> comparator(final FacetSortDirection direction) {
-    return (a, b) -> {
-      if( direction == FacetSortDirection.ASCENDING ){
-        return a.getValue().compareTo(b.getValue());
-      } else {
-        return b.getValue().compareTo(a.getValue());
-      }
-    };
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/ExpressionFactory.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/ExpressionFactory.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/ExpressionFactory.java
deleted file mode 100644
index 1f2d0e0..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/ExpressionFactory.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * 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.solr.analytics.expression;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.solr.analytics.statistics.StatsCollector;
-import org.apache.solr.analytics.util.AnalyticsParams;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.SolrException.ErrorCode;
-import org.apache.solr.util.DateMathParser;
-
-public class ExpressionFactory {
-
-  /**
-   * Creates a single expression that contains delegate expressions and/or 
-   * a StatsCollector.
-   * StatsCollectors are given as input and not created within the method so that
-   * expressions can share the same StatsCollectors, minimizing computation.
-   * 
-   * @param expression String representation of the desired expression
-   * @param statsCollectors List of StatsCollectors to build the expression with. 
-   * @return the expression
-   */
-  @SuppressWarnings("deprecation")
-  public static Expression create(String expression, StatsCollector[] statsCollectors) {
-    int paren = expression.indexOf('(');
-    if (paren<=0) {
-      throw new SolrException(ErrorCode.BAD_REQUEST, "The expression ["+expression+"] has no arguments and is not supported.");
-    }
-    String topOperation = expression.substring(0,paren).trim();
-    String operands;
-    try {
-      operands = expression.substring(paren+1, expression.lastIndexOf(')')).trim();
-    } catch (Exception e) {
-      throw new SolrException(ErrorCode.BAD_REQUEST,"Missing closing parenthesis in ["+expression+"]",e);
-    }
-    
-    // Builds a statistic, constant or recursively builds an expression tree
-    
-    // Statistic 
-    if (AnalyticsParams.ALL_STAT_SET.contains(topOperation)) {
-      if (topOperation.equals(AnalyticsParams.STAT_PERCENTILE)) {
-        operands = expression.substring(expression.indexOf(',')+1, expression.lastIndexOf(')')).trim();
-        topOperation = topOperation+"_"+expression.substring(expression.indexOf('(')+1, expression.indexOf(',')).trim();
-      }
-      StatsCollector collector = null;
-      // Finds the desired counter and builds an expression around it and the desired statistic.
-      for (StatsCollector c : statsCollectors) {
-        if (c.valueSourceString().equals(operands)) { 
-          collector = c;
-          break;
-        }
-      }
-      if (collector == null) {
-        throw new SolrException(ErrorCode.BAD_REQUEST, "ValueSource ["+operands+"] in Expression ["+expression+"] not found.");
-      }
-      return new BaseExpression(collector, topOperation);
-    }
-    // Constant
-    if (topOperation.equals(AnalyticsParams.CONSTANT_NUMBER)) {
-      try {
-        return new ConstantNumberExpression(Double.parseDouble(operands));
-      } catch (NumberFormatException e) {
-        throw new SolrException(ErrorCode.BAD_REQUEST, "The constant "+operands+" cannot be converted into a number.",e);
-      }
-    } else if (topOperation.equals(AnalyticsParams.CONSTANT_DATE)) {
-      return new ConstantDateExpression(DateMathParser.parseMath(null, operands));
-    } else if (topOperation.equals(AnalyticsParams.CONSTANT_STRING)) {
-      operands = expression.substring(paren+1, expression.lastIndexOf(')'));
-      return new ConstantStringExpression(operands);
-    }
-    
-    // Complex Delegating Expressions
-    String[] arguments = getArguments(operands);
-    Expression[] expArgs = new Expression[arguments.length];
-    for (int count = 0; count < arguments.length; count++) {
-      // Recursively builds delegate expressions
-      expArgs[count] = create(arguments[count], statsCollectors);
-    }
-    
-    // Single Delegate Expressions
-    if (expArgs.length==1) {
-      // Numeric Expression
-      if (topOperation.equals(AnalyticsParams.NEGATE)) {
-        return new NegateExpression(expArgs[0]);
-      }
-      if (topOperation.equals(AnalyticsParams.ABSOLUTE_VALUE)) {
-        return new AbsoluteValueExpression(expArgs[0]);
-      }
-      // String Expression
-      else if (topOperation.equals(AnalyticsParams.REVERSE)) {
-        return new ReverseExpression(expArgs[0]);
-      }
-      throw new SolrException(ErrorCode.BAD_REQUEST, topOperation+" does not have the correct number of arguments.");
-    }  else {
-      // Multi Delegate Expressions
-      // Numeric Expression
-      if (topOperation.equals(AnalyticsParams.ADD)) {
-        return new AddExpression(expArgs);
-      } else if (topOperation.equals(AnalyticsParams.MULTIPLY)) {
-        return new MultiplyExpression(expArgs);
-      }
-      // Date Expression
-      else if (topOperation.equals(AnalyticsParams.DATE_MATH)) {
-        return new DateMathExpression(expArgs);
-      } 
-      // String Expression
-      else if (topOperation.equals(AnalyticsParams.CONCATENATE)) {
-        return new ConcatenateExpression(expArgs);
-      } 
-      // Dual Delegate Expressions
-      else if (expArgs.length==2 && (topOperation.equals(AnalyticsParams.DIVIDE) || topOperation.equals(AnalyticsParams.POWER) 
-          || topOperation.equals(AnalyticsParams.LOG))) {
-        // Numeric Expression
-        if (topOperation.equals(AnalyticsParams.DIVIDE)) {
-          return new DivideExpression(expArgs[0], expArgs[1]);
-        } else if (topOperation.equals(AnalyticsParams.POWER)) {
-          return new PowerExpression(expArgs[0], expArgs[1]);
-        } else if (topOperation.equals(AnalyticsParams.LOG)) {
-          return new LogExpression(expArgs[0], expArgs[1]);
-        }
-        return null;
-      }
-      throw new SolrException(ErrorCode.BAD_REQUEST, topOperation+" does not have the correct number of arguments or is unsupported.");
-    }
-    
-  }
-  
-  /**
-   * Splits up an Expression's arguments.
-   * 
-   * @param expression Current expression string
-   * @return List The list of arguments
-   */
-  public static String[] getArguments(String expression) {
-    String[] strings = new String[1];
-    int stack = 0;
-    int start = 0;
-    List<String> arguments = new ArrayList<>();
-    char[] chars = expression.toCharArray();
-    for (int count = 0; count < expression.length(); count++) {
-      char c = chars[count];
-      if (c==',' && stack == 0) {
-        arguments.add(expression.substring(start, count).replace("\\(","(").replace("\\)",")").replace("\\,",",").trim());
-        start = count+1;
-      } else if (c == '(') {
-        stack ++;
-      } else if (c == ')') {
-        stack --;
-      } else if (c == '\\') {
-        ; // Do nothing.
-      }
-    }
-    if (stack==0) {
-      arguments.add(expression.substring(start).trim());
-    }
-    return arguments.toArray(strings);
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/MultiDelegateExpression.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/MultiDelegateExpression.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/MultiDelegateExpression.java
deleted file mode 100644
index 4ea66fa..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/MultiDelegateExpression.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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.solr.analytics.expression;
-
-import java.text.ParseException;
-import java.util.Date;
-
-import org.apache.solr.util.DateMathParser;
-
-/**
- * Abstraction of an expression that applies a function to an array of delegate expressions.
- */
-public abstract class MultiDelegateExpression extends Expression {
-  protected final Expression[] delegates;
-  
-  public MultiDelegateExpression(Expression[] delegates) {
-    this.delegates = delegates;
-  }
-}
-/**
- * <code>AddExpression</code> returns the sum of its components' values.
- */
-class AddExpression extends MultiDelegateExpression {
-  public AddExpression(Expression[] delegates) {
-    super(delegates);
-  }
-
-  @Override
-  public Comparable getValue() {
-    double sum = 0;
-    for (Expression delegate : delegates) {
-      Comparable dComp = delegate.getValue();
-      if (dComp==null) {
-        return null;
-      } else if (dComp.getClass().equals(Date.class)) {
-        dComp = new Long(((Date)dComp).getTime());
-      }
-      sum += ((Number)dComp).doubleValue();
-    }
-    return new Double(sum);
-  }
-}
-/**
- * <code>MultiplyExpression</code> returns the product of its delegates' values.
- */
-class MultiplyExpression extends MultiDelegateExpression {
-  public MultiplyExpression(Expression[] delegates) {
-    super(delegates);
-  }
-
-  @Override
-  public Comparable getValue() {
-    double prod = 1;
-    for (Expression delegate : delegates) {
-      Comparable dComp = delegate.getValue();
-      if (dComp==null) {
-        return null;
-      }
-      prod *= ((Number)dComp).doubleValue();
-    }
-    return new Double(prod);
-  }
-}
-/**
- * <code>DateMathExpression</code> returns the start date modified by the DateMath operations
- */
-class DateMathExpression extends MultiDelegateExpression {
-  /**
-   * @param delegates A list of Expressions. The first element in the list
-   * should be a numeric Expression which represents the starting date. 
-   * The rest of the field should be string Expression objects which contain
-   * the DateMath operations to perform on the start date.
-   */
-  public DateMathExpression(Expression[] delegates) {
-    super(delegates);
-  }
-
-  @Override
-  public Comparable getValue() {
-    DateMathParser parser = new DateMathParser();
-    parser.setNow((Date)delegates[0].getValue());
-    try {
-      for (int count = 1; count<delegates.length; count++) {
-        Comparable dComp = delegates[count].getValue();
-        if (dComp==null) {
-          return null;
-        }
-        parser.setNow(parser.parseMath((String)dComp));
-      }
-      return parser.getNow();
-    } catch (ParseException e) {
-      e.printStackTrace();
-      return parser.getNow();
-    }
-  }
-}
-/**
- * <code>ConcatenateExpression</code> returns the concatenation of its delegates' values in the order given.
- */
-class ConcatenateExpression extends MultiDelegateExpression {
-  public ConcatenateExpression(Expression[] delegates) {
-    super(delegates);
-  }
-
-  @Override
-  public Comparable getValue() {
-    StringBuilder builder = new StringBuilder();
-    for (Expression delegate : delegates) {
-      Comparable dComp = delegate.getValue();
-      if (dComp==null) {
-        return null;
-      }
-      builder.append(dComp.toString());
-    }
-    return builder.toString();
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/SingleDelegateExpression.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/SingleDelegateExpression.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/SingleDelegateExpression.java
deleted file mode 100644
index 8d94ece..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/SingleDelegateExpression.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.solr.analytics.expression;
-
-import java.util.Date;
-
-/**
- * Abstraction of an expression that applies a function to one delegate expression.
- */
-public abstract class SingleDelegateExpression extends Expression {
-  protected Expression delegate;
-  
-  public SingleDelegateExpression(Expression delegate) {
-    this.delegate = delegate;
-  }
-}
-/**
- * <code>NegateExpression</code> returns the negation of the delegate's value.
- */
-class NegateExpression extends SingleDelegateExpression {
-  public NegateExpression(Expression delegate) {
-    super(delegate);
-  }
-
-  @Override
-  public Comparable getValue() {
-    Comparable nComp = delegate.getValue();
-    if (nComp==null) {
-      return null;
-    } else if (nComp.getClass().equals(Date.class)) {
-      nComp = new Long(((Date)nComp).getTime());
-    }
-    return new Double(((Number)nComp).doubleValue()*-1);
-  }
-}
-/**
- * <code>AbsoluteValueExpression</code> returns the negation of the delegate's value.
- */
-class AbsoluteValueExpression extends SingleDelegateExpression {
-  public AbsoluteValueExpression(Expression delegate) {
-    super(delegate);
-  }
-
-  @Override
-  public Comparable getValue() {
-    Comparable nComp = delegate.getValue();
-    if (nComp==null) {
-      return null;
-    }
-    double d = ((Number)nComp).doubleValue();
-    if (d<0) {
-      return new Double(d*-1);
-    } else {
-      return new Double(d);
-    }
-  }
-}
-/**
- * <code>StringExpression</code> returns the reverse of the delegate's string value.
- */
-class ReverseExpression extends SingleDelegateExpression {
-  public ReverseExpression(Expression delegate) {
-    super(delegate);
-  }
-
-  @Override
-  public Comparable getValue() {
-    Comparable rComp = delegate.getValue();
-    if (rComp==null) {
-      return null;
-    }
-    return new StringBuilder(rComp.toString()).reverse().toString();
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/package-info.java
deleted file mode 100644
index 8c6f70e..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/package-info.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * 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.
- */
- 
-/** 
- * Expressions map either zero, one, two or many inputs to a single value. They can be defined recursively to compute complex math.
- */
-package org.apache.solr.analytics.expression;
-
-

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AbstractSolrQueryFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AbstractSolrQueryFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AbstractSolrQueryFacet.java
new file mode 100644
index 0000000..d06cfa3
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AbstractSolrQueryFacet.java
@@ -0,0 +1,104 @@
+/*
+ * 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.solr.analytics.facet;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.SimpleCollector;
+import org.apache.solr.analytics.AnalyticsDriver;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.search.Filter;
+import org.apache.solr.search.SolrIndexSearcher;
+
+/**
+ * Solr Query Facets are AnalyticsFacets that are calculated after the document streaming phase has occurred in the {@link AnalyticsDriver}
+ * (during which StreamingFacets and overall expressions are calculated). {@link AbstractSolrQueryFacet}s should not be confused with {@link QueryFacet}s, 
+ * which are a specific sub-type.
+ * 
+ * <p>
+ * The filtering for these facets is done through issuing additional Solr queries, and collecting on the resulting documents.
+ * Unlike streaming facets, which have an unspecified amount of facet values (facet buckets), the amount of facet values is determined by the user and
+ * a Solr query is issued for each requested facet value.
+ */
+public abstract class AbstractSolrQueryFacet extends AnalyticsFacet {
+  
+  protected AbstractSolrQueryFacet(String name) {
+    super(name);
+  }
+
+  /**
+   * Returns the set of {@link FacetValueQueryExecuter}s, one for each facet value, through the given consumer.
+   * 
+   * Each of these executors will be executed after the streaming phase in the {@link AnalyticsDriver}.
+   * 
+   * @param filter the overall filter representing the documents being used for the analytics request
+   * @param queryRequest the queryRequest 
+   * @param consumer the consumer of each facet value's executer
+   */
+  public abstract void createFacetValueExecuters(final Filter filter, SolrQueryRequest queryRequest, Consumer<FacetValueQueryExecuter> consumer);
+  
+  /**
+   * This executer is in charge of issuing the Solr query for a facet value and collecting results as the query is processed.
+   */
+  public class FacetValueQueryExecuter extends SimpleCollector {
+    private final ReductionDataCollection collection;
+    private final Query query;
+    
+    /**
+     * Create an executer to collect the given reduction data from the given Solr query.
+     * 
+     * @param collection The reduction data to collect while querying
+     * @param query The query used to filter for the facet value
+     */
+    public FacetValueQueryExecuter(ReductionDataCollection collection, Query query) {
+      this.collection = collection;
+      this.query = query;
+    }
+
+    @Override
+    public boolean needsScores() {
+      return false;
+    }
+
+    @Override
+    public void doSetNextReader(LeafReaderContext context) throws IOException {
+      collectionManager.doSetNextReader(context);
+    }
+
+    @Override
+    public void collect(int doc) throws IOException {
+      collectionManager.collect(doc);
+      collectionManager.apply();
+    }
+
+    /**
+     * Start the collection for this facet value.
+     * 
+     * @param searcher the solr searcher
+     * @throws IOException if an exception occurs during the querying
+     */
+    public void execute(SolrIndexSearcher searcher) throws IOException {
+      collectionManager.clearLastingCollectTargets();
+      collectionManager.addLastingCollectTarget(collection);
+      searcher.search(query, this);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AnalyticsFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AnalyticsFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AnalyticsFacet.java
new file mode 100644
index 0000000..d9c0f8c
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AnalyticsFacet.java
@@ -0,0 +1,166 @@
+/*
+ * 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.solr.analytics.facet;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.apache.solr.analytics.function.ExpressionCalculator;
+import org.apache.solr.analytics.function.ReductionCollectionManager;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.analytics.function.reduction.data.ReductionData;
+import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
+import org.apache.solr.common.util.NamedList;
+
+/**
+ * An abstract Facet to break up Analytics data over.
+ */
+public abstract class AnalyticsFacet {
+  protected final Map<String,ReductionDataCollection> reductionData;
+  protected ReductionCollectionManager collectionManager;
+  protected ExpressionCalculator expressionCalculator;
+  
+  protected final String name;
+  
+  public AnalyticsFacet(String name) {
+    this.reductionData = new LinkedHashMap<>();
+    this.name = name;
+  }
+  
+  /**
+   * Set the {@link ReductionCollectionManager} that manages the collection of the expressions
+   * calculated with this facet.
+   * 
+   * @param collectionManager The manager for relevant expressions
+   */
+  public void setReductionCollectionManager(ReductionCollectionManager collectionManager) {
+    this.collectionManager = collectionManager;
+  }
+  
+  /**
+   * Set the {@link ExpressionCalculator} that calculates the collection of the expressions
+   * requested for this facet.
+   * 
+   * @param expressionCalculator The calculator for relevant expressions
+   */
+  public void setExpressionCalculator(ExpressionCalculator expressionCalculator) {
+    this.expressionCalculator = expressionCalculator;
+  }
+
+  /**
+   * Import the shard data from a bit-stream, exported by the {@link #exportShardData} method 
+   * in the each of the collection's shards.
+   * 
+   * @param input The bit-stream to import the data from
+   * @throws IOException if an exception occurs while reading from the {@link DataInput}
+   */
+  public void importShardData(DataInput input) throws IOException {
+    int size = input.readInt();
+    for (int i = 0; i < size; ++i) {
+      importFacetValue(input, input.readUTF());
+    }
+  }
+  /**
+   * Import the next facet value's set of {@link ReductionData}.
+   * 
+   * @param input the bit-stream to import the reduction data from
+   * @param facetValue the next facet value
+   * @throws IOException if an exception occurs while reading from the input
+   */
+  protected void importFacetValue(DataInput input, String facetValue) throws IOException {
+    ReductionDataCollection dataCollection = reductionData.get(facetValue);
+    if (dataCollection == null) {
+      reductionData.put(facetValue, collectionManager.newDataCollectionIO());
+    } else {
+      collectionManager.prepareReductionDataIO(dataCollection);
+    }
+    
+    collectionManager.mergeData();
+  }
+
+  /**
+   * Export the shard data through a bit-stream, to be imported by the {@link #importShardData} method 
+   * in the originating shard.
+   * 
+   * @param output The bit-stream to output the data through
+   * @throws IOException if an exception occurs while writing to the {@link DataOutput}
+   */
+  public void exportShardData(DataOutput output) throws IOException {
+    output.writeInt(reductionData.size());
+    for (String facetValue : reductionData.keySet()) {
+      exportFacetValue(output, facetValue);
+    }
+  }
+  /**
+   * Export the next facet value's set of {@link ReductionData}.
+   * 
+   * @param output the bit-stream to output the reduction data to
+   * @param facetValue the next facet value
+   * @throws IOException if an exception occurs while reading from the input
+   */
+  protected void exportFacetValue(DataOutput output, String facetValue) throws IOException {
+    output.writeUTF(facetValue);
+    
+    collectionManager.prepareReductionDataIO(reductionData.get(facetValue));
+    collectionManager.exportData();
+  }
+  
+  /**
+   * Create the old olap-style response of the facet to be returned in the overall analytics response.
+   * 
+   * @return the response of the facet
+   */
+  public NamedList<Object> createOldResponse() {
+    NamedList<Object> nl = new NamedList<>();
+    reductionData.forEach((facetVal, dataCol) -> {
+      collectionManager.setData(dataCol);
+      nl.add(facetVal, new NamedList<>(expressionCalculator.getResults()));
+    });
+    return nl;
+  }
+  
+  /**
+   * Create the response of the facet to be returned in the overall analytics response.
+   * 
+   * @return the response of the facet
+   */
+  public Iterable<Map<String,Object>> createResponse() {
+    LinkedList<Map<String,Object>> list = new LinkedList<>();
+    reductionData.forEach((facetVal, dataCol) -> {
+      Map<String, Object> bucket = new HashMap<>();
+      bucket.put(AnalyticsResponseHeadings.FACET_VALUE, facetVal);
+      collectionManager.setData(dataCol);
+      expressionCalculator.addResults(bucket);
+      list.add(bucket);
+    });
+    return list;
+  }
+  
+  /**
+   * Get the name of the Facet. This is unique for the grouping.
+   * 
+   * @return The name of the Facet
+   */
+  public String getName() {
+    return name;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotFacet.java
new file mode 100644
index 0000000..f9e35f7
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotFacet.java
@@ -0,0 +1,114 @@
+/*
+ * 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.solr.analytics.facet;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.solr.analytics.function.ExpressionCalculator;
+import org.apache.solr.analytics.function.ReductionCollectionManager;
+import org.apache.solr.common.util.NamedList;
+
+/**
+ * A facet that takes in multiple ValueFacet expressions and does analytics calculations over each dimension given. 
+ */
+public class PivotFacet extends AnalyticsFacet implements StreamingFacet {
+  private final PivotHead<?> pivotHead;
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  public PivotFacet(String name, PivotNode<?> topPivot) {
+    super(name);
+    this.pivotHead = new PivotHead(topPivot);
+  }
+  
+  @Override
+  public void setReductionCollectionManager(ReductionCollectionManager collectionManager) {
+    pivotHead.setReductionCollectionManager(collectionManager);
+  }
+
+  @Override
+  public void setExpressionCalculator(ExpressionCalculator expressionCalculator) {
+    pivotHead.setExpressionCalculator(expressionCalculator);
+  }
+
+  @Override
+  public void addFacetValueCollectionTargets() {
+    pivotHead.addFacetValueCollectionTargets();
+  }
+
+  @Override
+  public void importShardData(DataInput input) throws IOException {
+    pivotHead.importShardData(input);
+  }
+
+  @Override
+  public void exportShardData(DataOutput output) throws IOException {
+    pivotHead.exportShardData(output);
+  }
+  
+  @Override
+  public NamedList<Object> createOldResponse() {
+    return new NamedList<>();
+  }
+  
+  @Override
+  public Iterable<Map<String,Object>> createResponse() {
+    return pivotHead.createResponse();
+  }
+}
+/**
+ * Typed Pivot class that stores the overall Pivot data and head of the Pivot node chain.
+ * 
+ * This class exists so that the {@link PivotFacet} class doesn't have to be typed ( {@code <T>} ).
+ */
+class PivotHead<T> implements StreamingFacet {
+  private final PivotNode<T> topPivot;
+  private final Map<String, T> pivotValues;
+  
+  public PivotHead(PivotNode<T> topPivot) {
+    this.topPivot = topPivot;
+    this.pivotValues = new HashMap<>();
+  }
+  
+  public void setReductionCollectionManager(ReductionCollectionManager collectionManager) {
+    topPivot.setReductionCollectionManager(collectionManager);
+  }
+  
+  public void setExpressionCalculator(ExpressionCalculator expressionCalculator) {
+    topPivot.setExpressionCalculator(expressionCalculator);
+  }
+
+  @Override
+  public void addFacetValueCollectionTargets() {
+    topPivot.addFacetValueCollectionTargets(pivotValues);
+  }
+
+  public void importShardData(DataInput input) throws IOException {
+    topPivot.importPivot(input, pivotValues);
+  }
+
+  public void exportShardData(DataOutput output) throws IOException {
+    topPivot.exportPivot(output, pivotValues);
+  }
+  
+  public Iterable<Map<String,Object>> createResponse() {
+    return topPivot.getPivotedResponse(pivotValues);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotNode.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotNode.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotNode.java
new file mode 100644
index 0000000..c6c0dc4
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotNode.java
@@ -0,0 +1,263 @@
+/*
+ * 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.solr.analytics.facet;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.AnalyticsDriver;
+import org.apache.solr.analytics.function.ExpressionCalculator;
+import org.apache.solr.analytics.function.ReductionCollectionManager;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.analytics.function.reduction.data.ReductionData;
+import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
+import org.apache.solr.analytics.value.StringValueStream;
+
+/**
+ * Representation of one layer of a Pivot Facet. A PivotFacet node is individually sortable,
+ * and is collected during the streaming phase of the {@link AnalyticsDriver}.
+ */
+public abstract class PivotNode<T> extends SortableFacet implements Consumer<String> {
+  private StringValueStream expression;
+  protected Map<String,T> currentPivot;
+
+  public PivotNode(String name, StringValueStream expression) {
+    super(name);
+    this.expression = expression;
+  }
+
+  /**
+   * Determine which facet values match the current document. Add the {@link ReductionDataCollection}s of the relevant facet values
+   * to the targets of the streaming {@link ReductionCollectionManager} so that they are updated with the current document's data.
+   */
+  public void addFacetValueCollectionTargets(Map<String,T> pivot) {
+    currentPivot = pivot;
+    expression.streamStrings(this);
+  }
+  
+  /**
+   * Import the shard data from a bit-stream for the given pivot, exported by the {@link #exportPivot} method 
+   * in the each of the collection's shards.
+   * 
+   * @param input The bit-stream to import the data from
+   * @param pivot the values for this pivot node and the pivot children (if they exist)
+   * @throws IOException if an exception occurs while reading from the {@link DataInput}
+   */
+  public void importPivot(DataInput input, Map<String,T> pivot) throws IOException {
+    int size = input.readInt();
+    currentPivot = pivot;
+    for (int i = 0; i < size; ++i) {
+      importPivotValue(input, input.readUTF());
+    }
+  }
+  /**
+   * Import the next pivot value's set of {@link ReductionData} and children's {@link ReductionData} if they exist.
+   * 
+   * @param input the bit-stream to import the reduction data from
+   * @param pivotValue the next pivot value
+   * @throws IOException if an exception occurs while reading from the input
+   */
+  protected abstract void importPivotValue(DataInput input, String pivotValue) throws IOException;
+
+  /**
+   * Export the shard data through a bit-stream for the given pivot, 
+   * to be imported by the {@link #importPivot} method in the originating shard.
+   * 
+   * @param output The bit-stream to output the data through
+   * @param pivot the values for this pivot node and the pivot children (if they exist)
+   * @throws IOException if an exception occurs while writing to the {@link DataOutput}
+   */
+  public void exportPivot(DataOutput output, Map<String,T> pivot) throws IOException {
+    output.writeInt(pivot.size());
+    for (String pivotValue : pivot.keySet()) {
+      output.writeUTF(pivotValue);
+      exportPivotValue(output, pivot.get(pivotValue));
+    }
+  }
+  /**
+   * Export the given pivot data, containing {@link ReductionData} and pivot children if they exist.
+   * 
+   * @param output the bit-stream to output the reduction data to
+   * @param pivotData the next pivot value data
+   * @throws IOException if an exception occurs while reading from the input
+   */
+  protected abstract void exportPivotValue(DataOutput output, T pivotData) throws IOException;
+  
+  /**
+   * Create the response of the facet to be returned in the overall analytics response.
+   * 
+   * @param pivot the pivot to create a response for
+   * @return the response of the facet
+   */
+  public abstract Iterable<Map<String,Object>> getPivotedResponse(Map<String,T> pivot);
+  
+  /**
+   * A pivot node that has no pivot children.
+   */
+  public static class PivotLeaf extends PivotNode<ReductionDataCollection> {
+
+    public PivotLeaf(String name, StringValueStream expression) {
+      super(name, expression);
+    }
+    
+    @Override
+    public void accept(String pivotValue) {
+      ReductionDataCollection collection = currentPivot.get(pivotValue);
+      if (collection == null) {
+        collection = collectionManager.newDataCollectionTarget();
+        currentPivot.put(pivotValue, collection);
+      } else {
+        collectionManager.addCollectTarget(collection);
+      }
+    }
+
+    @Override
+    protected void importPivotValue(DataInput input, String pivotValue) throws IOException {
+      ReductionDataCollection dataCollection = currentPivot.get(pivotValue);
+      if (dataCollection == null) {
+        currentPivot.put(pivotValue, collectionManager.newDataCollectionIO());
+      } else {
+        collectionManager.prepareReductionDataIO(dataCollection);
+      }
+      collectionManager.mergeData();
+    }
+
+    @Override
+    protected void exportPivotValue(DataOutput output, ReductionDataCollection pivotData) throws IOException {
+      collectionManager.prepareReductionDataIO(pivotData);
+      collectionManager.exportData();
+    }
+
+    @Override
+    public Iterable<Map<String,Object>> getPivotedResponse(Map<String,ReductionDataCollection> pivot) {
+      final List<FacetBucket> facetResults = new ArrayList<>();
+      pivot.forEach((facetVal, dataCol) -> {
+        collectionManager.setData(dataCol);
+        facetResults.add(new FacetBucket(facetVal,expressionCalculator.getResults()));
+      });
+
+      Iterable<FacetBucket> facetResultsIter = applyOptions(facetResults);
+      final LinkedList<Map<String,Object>> results = new LinkedList<>();
+      // Export each expression in the bucket.
+      for (FacetBucket bucket : facetResultsIter) {
+        Map<String, Object> bucketMap = new HashMap<>();
+        bucketMap.put(AnalyticsResponseHeadings.PIVOT_NAME, name);
+        bucketMap.put(AnalyticsResponseHeadings.FACET_VALUE, bucket.getFacetValue());
+        bucketMap.put(AnalyticsResponseHeadings.RESULTS, bucket.getResults());
+        results.add(bucketMap);
+      }
+      return results;
+    }
+  }
+  
+  /**
+   * A pivot node that has pivot children.
+   */
+  public static class PivotBranch<T> extends PivotNode<PivotBranch.PivotDataPair<T>> {
+    private final PivotNode<T> childPivot;
+    public PivotBranch(String name, StringValueStream expression, PivotNode<T> childPivot) {
+      super(name, expression);
+      this.childPivot = childPivot;
+    }
+    
+    @Override
+    public void setReductionCollectionManager(ReductionCollectionManager collectionManager) {
+      super.setReductionCollectionManager(collectionManager);
+      childPivot.setReductionCollectionManager(collectionManager);
+    }
+
+    @Override
+    public void setExpressionCalculator(ExpressionCalculator expressionCalculator) {
+      super.setExpressionCalculator(expressionCalculator);
+      childPivot.setExpressionCalculator(expressionCalculator);
+    }
+    
+    @Override
+    public void accept(String pivotValue) {
+      PivotDataPair<T> pivotData = currentPivot.get(pivotValue);
+      if (pivotData == null) {
+        pivotData = new PivotDataPair<>();
+        pivotData.childPivots = new HashMap<>();
+        pivotData.pivotReduction = collectionManager.newDataCollectionTarget();
+        currentPivot.put(pivotValue, pivotData);
+      } else {
+        collectionManager.addCollectTarget(pivotData.pivotReduction);
+      }
+      childPivot.addFacetValueCollectionTargets(pivotData.childPivots);
+    }
+
+    @Override
+    protected void importPivotValue(DataInput input, String pivotValue) throws IOException {
+      PivotDataPair<T> pivotData = currentPivot.get(pivotValue);
+      if (pivotData == null) {
+        pivotData = new PivotDataPair<>();
+        pivotData.childPivots = new HashMap<>();
+        pivotData.pivotReduction = collectionManager.newDataCollectionIO();
+        currentPivot.put(pivotValue, pivotData);
+      } else {
+        collectionManager.prepareReductionDataIO(pivotData.pivotReduction);
+      }
+      collectionManager.mergeData();
+      childPivot.importPivot(input, pivotData.childPivots);
+    }
+
+    @Override
+    protected void exportPivotValue(DataOutput output, PivotDataPair<T> pivotData) throws IOException {
+      collectionManager.prepareReductionDataIO(pivotData.pivotReduction);
+      collectionManager.exportData();
+      
+      childPivot.exportPivot(output, pivotData.childPivots);
+    }
+
+    @Override
+    public Iterable<Map<String,Object>> getPivotedResponse(Map<String,PivotDataPair<T>> pivot) {
+      final List<FacetBucket> facetResults = new ArrayList<>();
+      pivot.forEach((facetVal, dataPair) -> {
+        collectionManager.setData(dataPair.pivotReduction);
+        facetResults.add(new FacetBucket(facetVal,expressionCalculator.getResults()));
+      });
+
+      Iterable<FacetBucket> facetResultsIter = applyOptions(facetResults);
+      final LinkedList<Map<String,Object>> results = new LinkedList<>();
+      // Export each expression in the bucket.
+      for (FacetBucket bucket : facetResultsIter) {
+        Map<String, Object> bucketMap = new HashMap<>();
+        bucketMap.put(AnalyticsResponseHeadings.PIVOT_NAME, name);
+        bucketMap.put(AnalyticsResponseHeadings.FACET_VALUE, bucket.getFacetValue());
+        bucketMap.put(AnalyticsResponseHeadings.RESULTS, bucket.getResults());
+        bucketMap.put(AnalyticsResponseHeadings.PIVOT_CHILDREN, childPivot.getPivotedResponse(pivot.get(bucket.getFacetValue()).childPivots));
+        results.add(bucketMap);
+      }
+      return results;
+    }
+    
+    /**
+     * Contains pivot data for {@link PivotNode.PivotBranch} classes.
+     */
+    protected static class PivotDataPair<T> {
+      ReductionDataCollection pivotReduction;
+      Map<String,T> childPivots;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/QueryFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/QueryFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/QueryFacet.java
new file mode 100644
index 0000000..f880809
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/QueryFacet.java
@@ -0,0 +1,64 @@
+/*
+ * 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.solr.analytics.facet;
+
+import java.util.Map;
+import java.util.function.Consumer;
+
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.search.Filter;
+import org.apache.solr.search.QParser;
+
+/**
+ * A facet that breaks down the data by additional Solr Queries.
+ */
+public class QueryFacet extends AbstractSolrQueryFacet {
+  private final Map<String,String> queries;
+  
+  public QueryFacet(String name, Map<String, String> queries) {
+    super(name);
+    this.queries = queries;
+  }
+  
+  @Override
+  public void createFacetValueExecuters(final Filter filter, SolrQueryRequest queryRequest, Consumer<FacetValueQueryExecuter> consumer) {
+    queries.forEach( (queryName, query) -> {
+      final Query q;
+      try {
+        q = QParser.getParser(query, queryRequest).getQuery();
+      } catch( Exception e ){
+        throw new SolrException(ErrorCode.BAD_REQUEST,"Invalid query '"+query+"' in query facet '" + getName() + "'",e);
+      }
+      // The searcher sends docIds to the QueryFacetAccumulator which forwards
+      // them to <code>collectQuery()</code> in this class for collection.
+      Query queryQuery = new BooleanQuery.Builder()
+          .add(q, Occur.MUST)
+          .add(filter, Occur.FILTER)
+          .build();
+
+      ReductionDataCollection dataCol = collectionManager.newDataCollection();
+      reductionData.put(queryName, dataCol);
+      consumer.accept(new FacetValueQueryExecuter(dataCol, queryQuery));
+    });
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/RangeFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/RangeFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/RangeFacet.java
new file mode 100644
index 0000000..80e8d21
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/RangeFacet.java
@@ -0,0 +1,119 @@
+/*
+ * 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.solr.analytics.facet;
+
+import java.util.EnumSet;
+import java.util.List;
+import java.util.function.Consumer;
+
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.analytics.util.FacetRangeGenerator;
+import org.apache.solr.analytics.util.FacetRangeGenerator.FacetRange;
+import org.apache.solr.common.params.FacetParams.FacetRangeInclude;
+import org.apache.solr.common.params.FacetParams.FacetRangeOther;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.search.Filter;
+
+/**
+ * A facet that groups data by a discrete set of ranges.
+ */
+public class RangeFacet extends AbstractSolrQueryFacet {
+  protected final SchemaField field;
+  protected final String start;
+  protected final String end;
+  protected final List<String> gaps;
+  protected boolean hardEnd = false;
+  protected EnumSet<FacetRangeInclude> include;
+  protected EnumSet<FacetRangeOther> others;
+  
+  public RangeFacet(String name, SchemaField field, String start, String end, List<String> gaps) {
+    super(name);
+    this.field = field;
+    this.start = start;
+    this.end = end;
+    this.gaps = gaps;
+    include = EnumSet.of(FacetRangeInclude.LOWER);
+    others = EnumSet.of(FacetRangeOther.NONE);
+  }
+
+  @Override
+  public void createFacetValueExecuters(final Filter filter, SolrQueryRequest queryRequest, Consumer<FacetValueQueryExecuter> consumer) {
+    // Computes the end points of the ranges in the rangeFacet
+    final FacetRangeGenerator<? extends Comparable<?>> rec = FacetRangeGenerator.create(this);
+    final SchemaField sf = field;
+    
+    // Create a rangeFacetAccumulator for each range and 
+    // collect the documents for that range.
+    for (FacetRange range : rec.getRanges()) {
+      Query q = sf.getType().getRangeQuery(null, sf, range.lower, range.upper, range.includeLower,range.includeUpper);
+      // The searcher sends docIds to the RangeFacetAccumulator which forwards
+      // them to <code>collectRange()</code> in this class for collection.
+      Query rangeQuery = new BooleanQuery.Builder()
+          .add(q, Occur.MUST)
+          .add(filter, Occur.FILTER)
+          .build();
+      
+      ReductionDataCollection dataCol = collectionManager.newDataCollection();
+      reductionData.put(range.toString(), dataCol);
+      consumer.accept(new FacetValueQueryExecuter(dataCol, rangeQuery));
+    }
+  }
+
+  public String getStart() {
+    return start;
+  }
+
+  public String getEnd() {
+    return end;
+  }
+
+  public EnumSet<FacetRangeInclude> getInclude() {
+    return include;
+  }
+
+  public void setInclude(EnumSet<FacetRangeInclude> include) {
+    this.include = include;
+  }
+
+  public List<String> getGaps() {
+    return gaps;
+  }
+
+  public boolean isHardEnd() {
+    return hardEnd;
+  }
+
+  public void setHardEnd(boolean hardEnd) {
+    this.hardEnd = hardEnd;
+  }
+
+  public EnumSet<FacetRangeOther> getOthers() {
+    return others;
+  }
+
+  public void setOthers(EnumSet<FacetRangeOther> others) {
+    this.others = others;
+  }
+  
+  public SchemaField getField() {
+    return field;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/SortableFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/SortableFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/SortableFacet.java
new file mode 100644
index 0000000..ef1e04b
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/SortableFacet.java
@@ -0,0 +1,178 @@
+/*
+ * 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.solr.analytics.facet;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.solr.analytics.facet.compare.FacetResultsComparator;
+import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
+import org.apache.solr.common.util.NamedList;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * A facet that can be sorted by either the facet value or an expression value.
+ */
+public abstract class SortableFacet extends AnalyticsFacet {
+  protected FacetSortSpecification sort = null;
+  
+  protected SortableFacet(String name) {
+    super(name);
+  }
+  
+  @Override
+  public NamedList<Object> createOldResponse() {
+    final NamedList<Object> results = new NamedList<>();
+    // Export each expression in the bucket.
+    for (FacetBucket bucket : getBuckets()) {
+      results.add(bucket.getFacetValue(), new NamedList<>(bucket.getResults()));
+    }
+    return results;
+  }
+  
+  @Override
+  public Iterable<Map<String,Object>> createResponse() {
+    final LinkedList<Map<String,Object>> results = new LinkedList<>();
+    // Export each expression in the bucket.
+    for (FacetBucket bucket : getBuckets()) {
+      Map<String, Object> bucketMap = new HashMap<>();
+      bucketMap.put(AnalyticsResponseHeadings.FACET_VALUE, bucket.getFacetValue());
+      bucketMap.put(AnalyticsResponseHeadings.RESULTS, bucket.getResults());
+      results.add(bucketMap);
+    }
+    return results;
+  }
+  
+  private Iterable<FacetBucket> getBuckets() {
+    final List<FacetBucket> facetResults = new ArrayList<>();
+    reductionData.forEach((facetVal, dataCol) -> {
+      collectionManager.setData(dataCol);
+      facetResults.add(new FacetBucket(facetVal,expressionCalculator.getResults()));
+    });
+    
+    return applyOptions(facetResults);
+  }
+
+  /**
+   * Apply the sorting options to the given facet results.
+   * 
+   * @param facetResults to apply sorting options to
+   * @return the sorted results
+   */
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  protected Iterable<FacetBucket> applyOptions(List<FacetBucket> facetResults) {
+    // Sorting the buckets if a sort specification is provided
+    if (sort == null || facetResults.isEmpty()) {
+      return facetResults;
+    }
+    Comparator comp = sort.getComparator();
+    Collections.sort(facetResults, comp);
+    
+    Iterable<FacetBucket> facetResultsIter = facetResults;
+    // apply the limit
+    if (sort.getLimit() > 0) {
+      if (sort.getOffset() > 0) {
+        facetResultsIter = Iterables.skip(facetResultsIter, sort.getOffset());
+      }
+      facetResultsIter = Iterables.limit(facetResultsIter, sort.getLimit());
+    } else if (sort.getLimit() == 0) {
+      return new LinkedList<FacetBucket>();
+    }
+    return facetResultsIter;
+  }
+  
+  /**
+   * Specifies how to sort the buckets of a sortable facet.
+   */
+  public static class FacetSortSpecification {
+    private FacetResultsComparator comparator;
+    protected int limit;
+    protected int offset;
+    
+    public FacetSortSpecification(FacetResultsComparator comparator, int limit, int offset) {
+      this.comparator = comparator;
+      this.limit = limit;
+      this.offset = offset;
+    }
+
+    public FacetResultsComparator getComparator() {
+      return comparator;
+    }
+
+    /**
+     * Get the maximum number of buckets to be returned.
+     * 
+     * @return the limit
+     */
+    public int getLimit() {
+      return limit;
+    }
+    /**
+     * Set the maximum number of buckets to be returned.
+     * 
+     * @param limit the maximum number of buckets
+     */
+    public void setLimit(int limit) {
+      this.limit = limit;
+    }
+    
+    /**
+     * Get the first bucket to return, has to be used with the {@code limit} option.
+     * 
+     * @return the bucket offset
+     */
+    public int getOffset() {
+      return offset;
+    }
+  }
+
+  public SortableFacet.FacetSortSpecification getSort() {
+    return sort;
+  }
+
+  public void setSort(SortableFacet.FacetSortSpecification sort) {
+    this.sort = sort;
+  }
+  
+  public static class FacetBucket {
+    private final String facetValue;
+    private final Map<String,Object> expressionResults;
+    
+    public FacetBucket(String facetValue, Map<String,Object> expressionResults) {
+      this.facetValue = facetValue;
+      this.expressionResults = expressionResults;
+    }
+    
+    public Object getResult(String expression) {
+      return expressionResults.get(expression);
+    }
+    
+    public Map<String,Object> getResults() {
+      return expressionResults;
+    }
+    
+    public String getFacetValue() {
+      return facetValue;
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/StreamingFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/StreamingFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/StreamingFacet.java
new file mode 100644
index 0000000..6cca041
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/StreamingFacet.java
@@ -0,0 +1,32 @@
+/*
+ * 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.solr.analytics.facet;
+
+import org.apache.solr.analytics.AnalyticsDriver;
+import org.apache.solr.analytics.function.ReductionCollectionManager;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+
+/**
+ * A facet that is collected during the streaming phase of the {@link AnalyticsDriver}.
+ */
+public interface StreamingFacet {
+  /**
+   * Determine which facet values match the current document. Add the {@link ReductionDataCollection}s of the relevant facet values
+   * to the targets of the streaming {@link ReductionCollectionManager} so that they are updated with the current document's data.
+   */
+  void addFacetValueCollectionTargets();
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/ValueFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/ValueFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/ValueFacet.java
new file mode 100644
index 0000000..b1d84ba
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/ValueFacet.java
@@ -0,0 +1,60 @@
+/*
+ * 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.solr.analytics.facet;
+
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.analytics.value.StringValueStream;
+
+/**
+ * A facet that breaks up data by the values of a mapping expression or field.
+ * The mapping expression must be castable to a {@link StringValueStream}.
+ */
+public class ValueFacet extends SortableFacet implements StreamingFacet, Consumer<String> {
+  private StringValueStream expression;
+
+  public ValueFacet(String name, StringValueStream expression) {
+    super(name);
+    this.expression = expression;
+  }
+
+  @Override
+  public void addFacetValueCollectionTargets() {
+    expression.streamStrings(this);
+  }
+    
+  @Override
+  public void accept(String t) {
+    ReductionDataCollection collection = reductionData.get(t);
+    if (collection == null) {
+      collection = collectionManager.newDataCollectionTarget();
+      reductionData.put(t, collection);
+    } else {
+      collectionManager.addCollectTarget(collection);
+    }
+  }
+  
+  /**
+   * Get the expression used to create the facet values.
+   * 
+   * @return a string mapping expression
+   */
+  public StringValueStream getExpression() {
+    return expression;
+  }
+}
\ No newline at end of file


[03/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/rangeFacets.xml
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/rangeFacets.xml b/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/rangeFacets.xml
deleted file mode 100644
index 4a596f7..0000000
--- a/solr/contrib/analytics/src/test-files/analytics/requestXMLFiles/rangeFacets.xml
+++ /dev/null
@@ -1,319 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<analyticsRequestEnvelope stats="true" olap="true">
-   <analyticsRequest>
-     <name>regular int</name>
-     
-     <statistic>
-       <expression>mean(int(int_id))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>sum(int(int_id))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>median(int(int_id))</expression>
-       <name>median</name>
-     </statistic>
-     <statistic>
-       <expression>count(int(int_id))</expression>
-       <name>count</name>
-     </statistic>
-     <statistic>
-       <expression>sumofsquares(int(int_id))</expression>
-       <name>sumOfSquares</name>
-     </statistic>
-     
-     <rangeFacet hardend="false">
-       <field>long_ld</field>
-       <start>5</start>
-       <end>30</end>
-       <gap>5</gap>
-       <includeBoundary>lower</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-     <rangeFacet hardend="false">
-       <field>double_dd</field>
-       <start>3</start>
-       <end>39</end>
-       <gap>7</gap>
-       <includeBoundary>upper</includeBoundary>
-       <includeBoundary>outer</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-     <rangeFacet hardend="false">
-       <field>date_dtd</field>
-       <start>1007-01-01T23:59:59Z</start>
-       <end>1044-01-01T23:59:59Z</end>
-       <gap>+7YEARS</gap>
-       <includeBoundary>lower</includeBoundary>
-       <includeBoundary>edge</includeBoundary>
-       <includeBoundary>outer</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>regular float</name>
-     
-     <statistic>
-       <expression>mean(float(float_fd))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>sum(float(float_fd))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>median(float(float_fd))</expression>
-       <name>median</name>
-     </statistic>
-     <statistic>
-       <expression>count(float(float_fd))</expression>
-       <name>count</name>
-     </statistic>
-     <statistic>
-       <expression>sumofsquares(float(float_fd))</expression>
-       <name>sumOfSquares</name>
-     </statistic>
-     
-     <rangeFacet hardend="false">
-       <field>long_ld</field>
-       <start>0</start>
-       <end>29</end>
-       <gap>4</gap>
-       <includeBoundary>all</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-     <rangeFacet hardend="false">
-       <field>double_dd</field>
-       <start>4</start>
-       <end>47</end>
-       <gap>11</gap>
-       <includeBoundary>edge</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-     <rangeFacet hardend="false">
-       <field>date_dtd</field>
-       <start>1004-01-01T23:59:59Z</start>
-       <end>1046-01-01T23:59:59Z</end>
-       <gap>+5YEARS</gap>
-       <includeBoundary>upper</includeBoundary>
-       <includeBoundary>edge</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>hardend int</name>
-     
-     <statistic>
-       <expression>mean(int(int_id))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>sum(int(int_id))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>median(int(int_id))</expression>
-       <name>median</name>
-     </statistic>
-     <statistic>
-       <expression>count(int(int_id))</expression>
-       <name>count</name>
-     </statistic>
-     <statistic>
-       <expression>sumofsquares(int(int_id))</expression>
-       <name>sumOfSquares</name>
-     </statistic>
-     
-     <rangeFacet hardend="true">
-       <field>long_ld</field>
-       <start>5</start>
-       <end>30</end>
-       <gap>5</gap>
-       <includeBoundary>lower</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-     <rangeFacet hardend="true">
-       <field>double_dd</field>
-       <start>3</start>
-       <end>39</end>
-       <gap>7</gap>
-       <includeBoundary>upper</includeBoundary>
-       <includeBoundary>outer</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-     <rangeFacet hardend="true">
-       <field>date_dtd</field>
-       <start>1007-01-01T23:59:59Z</start>
-       <end>1044-01-01T23:59:59Z</end>
-       <gap>+7YEARS</gap>
-       <includeBoundary>lower</includeBoundary>
-       <includeBoundary>edge</includeBoundary>
-       <includeBoundary>outer</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>hardend float</name>
-     
-     <statistic>
-       <expression>mean(float(float_fd))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>sum(float(float_fd))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>median(float(float_fd))</expression>
-       <name>median</name>
-     </statistic>
-     <statistic>
-       <expression>count(float(float_fd))</expression>
-       <name>count</name>
-     </statistic>
-     <statistic>
-       <expression>sumofsquares(float(float_fd))</expression>
-       <name>sumOfSquares</name>
-     </statistic>
-     
-     <rangeFacet hardend="true">
-       <field>long_ld</field>
-       <start>0</start>
-       <end>29</end>
-       <gap>4</gap>
-       <includeBoundary>all</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-     <rangeFacet hardend="true">
-       <field>double_dd</field>
-       <start>4</start>
-       <end>47</end>
-       <gap>11</gap>
-       <includeBoundary>edge</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-     <rangeFacet hardend="true">
-       <field>date_dtd</field>
-       <start>1004-01-01T23:59:59Z</start>
-       <end>1046-01-01T23:59:59Z</end>
-       <gap>+5YEARS</gap>
-       <includeBoundary>upper</includeBoundary>
-       <includeBoundary>edge</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>multigap int</name>
-     
-     <statistic>
-       <expression>mean(int(int_id))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>sum(int(int_id))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>median(int(int_id))</expression>
-       <name>median</name>
-     </statistic>
-     <statistic>
-       <expression>count(int(int_id))</expression>
-       <name>count</name>
-     </statistic>
-     <statistic>
-       <expression>sumofsquares(int(int_id))</expression>
-       <name>sumOfSquares</name>
-     </statistic>
-     
-     <rangeFacet hardend="false">
-       <field>long_ld</field>
-       <start>5</start>
-       <end>30</end>
-       <gap>4</gap>
-       <gap>2</gap>
-       <gap>6</gap>
-       <gap>3</gap>
-       <includeBoundary>lower</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-     <rangeFacet hardend="false">
-       <field>double_dd</field>
-       <start>3</start>
-       <end>39</end>
-       <gap>3</gap>
-       <gap>1</gap>
-       <gap>7</gap>
-       <includeBoundary>upper</includeBoundary>
-       <includeBoundary>outer</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-     <rangeFacet hardend="false">
-       <field>date_dtd</field>
-       <start>1007-01-01T23:59:59Z</start>
-       <end>1044-01-01T23:59:59Z</end>
-       <gap>+2YEARS</gap>
-       <gap>+7YEARS</gap>
-       <includeBoundary>lower</includeBoundary>
-       <includeBoundary>edge</includeBoundary>
-       <includeBoundary>outer</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-   </analyticsRequest>
-   <analyticsRequest>
-     <name>multigap float</name>
-     
-     <statistic>
-       <expression>mean(float(float_fd))</expression>
-       <name>mean</name>
-     </statistic>
-     <statistic>
-       <expression>sum(float(float_fd))</expression>
-       <name>sum</name>
-     </statistic>
-     <statistic>
-       <expression>median(float(float_fd))</expression>
-       <name>median</name>
-     </statistic>
-     <statistic>
-       <expression>count(float(float_fd))</expression>
-       <name>count</name>
-     </statistic>
-     <statistic>
-       <expression>sumofsquares(float(float_fd))</expression>
-       <name>sumOfSquares</name>
-     </statistic>
-     
-     <rangeFacet hardend="false">
-       <field>long_ld</field>
-       <start>0</start>
-       <end>29</end>
-       <gap>1</gap>
-       <gap>4</gap>
-       <includeBoundary>all</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-     <rangeFacet hardend="false">
-       <field>double_dd</field>
-       <start>4</start>
-       <end>47</end>
-       <gap>2</gap>
-       <gap>3</gap>
-       <gap>11</gap>
-       <includeBoundary>edge</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-     <rangeFacet hardend="false">
-       <field>date_dtd</field>
-       <start>1004-01-01T23:59:59Z</start>
-       <end>1046-01-01T23:59:59Z</end>
-       <gap>+4YEARS</gap>
-       <gap>+5YEARS</gap>
-       <includeBoundary>upper</includeBoundary>
-       <includeBoundary>edge</includeBoundary>
-       <otherRange>all</otherRange>
-     </rangeFacet>
-   </analyticsRequest>
-</analyticsRequestEnvelope>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/solr/analytics/expressions.txt
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/solr/analytics/expressions.txt b/solr/contrib/analytics/src/test-files/solr/analytics/expressions.txt
new file mode 100644
index 0000000..6f5c916
--- /dev/null
+++ b/solr/contrib/analytics/src/test-files/solr/analytics/expressions.txt
@@ -0,0 +1,65 @@
+o.ar.s.sum=sum(int_id)
+o.ar.s.unique=unique(long_ld)
+o.ar.s.su=add(sum(int_id),unique(long_ld))
+o.ar.s.mean=mean(int_id)
+o.ar.s.count=count(long_ld)
+o.ar.s.median=median(int_id)
+o.ar.s.mcm=add(mean(int_id),count(long_ld),median(int_id))
+
+o.mr.s.sum=sum(int_id)
+o.mr.s.unique=unique(long_ld)
+o.mr.s.su=mult(sum(int_id),unique(long_ld))
+o.mr.s.mean=mean(int_id)
+o.mr.s.count=count(long_ld)
+o.mr.s.median=median(int_id)
+o.mr.s.mcm=mult(mean(int_id),count(long_ld),median(int_id))
+
+o.dr.s.sum=sum(int_id)
+o.dr.s.unique=unique(long_ld)
+o.dr.s.su=div(sum(int_id),unique(long_ld))
+o.dr.s.mean=mean(int_id)
+o.dr.s.count=count(long_ld)
+o.dr.s.mc=div(mean(int_id),count(long_ld))
+
+o.pr.s.sum=sum(int_id)
+o.pr.s.unique=unique(long_ld)
+o.pr.s.su=pow(sum(int_id),unique(long_ld))
+o.pr.s.mean=mean(int_id)
+o.pr.s.count=count(long_ld)
+o.pr.s.mc=pow(mean(int_id),count(long_ld))
+
+o.nr.s.sum=sum(int_id)
+o.nr.s.s=neg(sum(int_id))
+o.nr.s.count=count(long_ld)
+o.nr.s.c=neg(count(long_ld))
+
+o.avr.s.sum=sum(int_id)
+o.avr.s.s=abs(neg(sum(int_id)))
+o.avr.s.count=count(long_ld)
+o.avr.s.c=abs(neg(count(long_ld)))
+
+o.cnr.s.c8=8
+o.cnr.s.c10=10.0
+
+o.dmr.s.median=median(date_dtd)
+o.dmr.s.cme="+2YEARS"
+o.dmr.s.dmme=date_math(median(date_dtd),"+2YEARS")
+o.dmr.s.max=max(date_dtd)
+o.dmr.s.cma="+2MONTHS"
+o.dmr.s.dmma=date_math(max(date_dtd),"+2MONTHS")
+
+o.cdr.s.cd1=1800-12-31T23:59:59Z
+o.cdr.s.cs1="1800-12-31T23:59:59Z"
+o.cdr.s.cd2=1804-06-30T23:59:59Z
+o.cdr.s.cs2="1804-06-30T23:59:59Z"
+
+o.csr.s.cs1="this is the first"
+o.csr.s.cs2="this is the second"
+o.csr.s.cs3="this is the third"
+
+o.cr.s.csmin="this is the first"
+o.cr.s.min=min(string_sd)
+o.cr.s.ccmin=concat("this is the first",min(string_sd))
+o.cr.s.csmax="this is the second"
+o.cr.s.max=max(string_sd)
+o.cr.s.ccmax=concat("this is the second",max(string_sd))

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/solr/analytics/facetSorting.txt
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/solr/analytics/facetSorting.txt b/solr/contrib/analytics/src/test-files/solr/analytics/facetSorting.txt
new file mode 100644
index 0000000..4663217
--- /dev/null
+++ b/solr/contrib/analytics/src/test-files/solr/analytics/facetSorting.txt
@@ -0,0 +1,4 @@
+o.ar.s.min=min(double_dd)
+o.ar.s.max=max(long_ld)
+o.ar.ff=string_sd
+o.ar.ff.string_sd.sortstatistic=min

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/solr/analytics/fieldFacetExtras.txt
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/solr/analytics/fieldFacetExtras.txt b/solr/contrib/analytics/src/test-files/solr/analytics/fieldFacetExtras.txt
new file mode 100644
index 0000000..3979f57
--- /dev/null
+++ b/solr/contrib/analytics/src/test-files/solr/analytics/fieldFacetExtras.txt
@@ -0,0 +1,66 @@
+o.sr.s.mean=mean(int_id)
+o.sr.s.median=median(int_id)
+o.sr.s.count=count(int_id)
+o.sr.s.percentile_20=percentile(20,int_id)
+o.sr.ff=long_ld
+o.sr.ff.long_ld.ss=mean
+o.sr.ff.long_ld.sd=asc
+o.sr.ff=float_fd
+o.sr.ff.float_fd.ss=median
+o.sr.ff.float_fd.sd=desc
+o.sr.ff=double_dd
+o.sr.ff.double_dd.ss=count
+o.sr.ff.double_dd.sd=asc
+o.sr.ff=string_sd
+o.sr.ff.string_sd.ss=percentile_20
+o.sr.ff.string_sd.sd=desc
+
+o.lr.s.mean=mean(int_id)
+o.lr.s.median=median(int_id)
+o.lr.s.count=count(int_id)
+o.lr.s.percentile_20=percentile(20,int_id)
+o.lr.ff=long_ld
+o.lr.ff.long_ld.ss=mean
+o.lr.ff.long_ld.sd=asc
+o.lr.ff.long_ld.limit=5
+o.lr.ff=float_fd
+o.lr.ff.float_fd.ss=median
+o.lr.ff.float_fd.sd=desc
+o.lr.ff.float_fd.limit=3
+o.lr.ff=double_dd
+o.lr.ff.double_dd.ss=count
+o.lr.ff.double_dd.sd=asc
+o.lr.ff.double_dd.limit=7
+o.lr.ff=string_sd
+o.lr.ff.string_sd.ss=percentile_20
+o.lr.ff.string_sd.sd=desc
+o.lr.ff.string_sd.limit=1
+
+
+
+o.offAll.s.mean=mean(int_id)
+o.offAll.ff=long_ld
+o.offAll.ff.long_ld.ss=mean
+o.offAll.ff.long_ld.sd=asc
+o.offAll.ff.long_ld.limit=7
+
+o.off0.s.mean=mean(int_id)
+o.off0.ff=long_ld
+o.off0.ff.long_ld.ss=mean
+o.off0.ff.long_ld.sd=asc
+o.off0.ff.long_ld.limit=2
+o.off0.ff.long_ld.offset=0
+
+o.off1.s.mean=mean(int_id)
+o.off1.ff=long_ld
+o.off1.ff.long_ld.ss=mean
+o.off1.ff.long_ld.sd=asc
+o.off1.ff.long_ld.limit=2
+o.off1.ff.long_ld.offset=2
+
+o.off2.s.mean=mean(int_id)
+o.off2.ff=long_ld
+o.off2.ff.long_ld.ss=mean
+o.off2.ff.long_ld.sd=asc
+o.off2.ff.long_ld.limit=3
+o.off2.ff.long_ld.offset=4

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/solr/analytics/fieldFacets.txt
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/solr/analytics/fieldFacets.txt b/solr/contrib/analytics/src/test-files/solr/analytics/fieldFacets.txt
new file mode 100644
index 0000000..5ba5953
--- /dev/null
+++ b/solr/contrib/analytics/src/test-files/solr/analytics/fieldFacets.txt
@@ -0,0 +1,132 @@
+o.sum.s.int=sum(int_id)
+o.sum.s.long=sum(long_ld)
+o.sum.s.float=sum(float_fd)
+o.sum.s.double=sum(double_dd)
+o.sum.ff=string_sd
+o.sum.ff=date_dtd
+
+o.mean.s.int=mean(int_id)
+o.mean.s.long=mean(long_ld)
+o.mean.s.float=mean(float_fd)
+o.mean.s.double=mean(double_dd)
+o.mean.ff=string_sd
+o.mean.ff=date_dtd
+
+o.sumOfSquares.s.int=sumofsquares(int_id)
+o.sumOfSquares.s.long=sumofsquares(long_ld)
+o.sumOfSquares.s.float=sumofsquares(float_fd)
+o.sumOfSquares.s.double=sumofsquares(double_dd)
+o.sumOfSquares.ff=string_sd
+o.sumOfSquares.ff=date_dtd
+
+o.stddev.s.int=stddev(int_id)
+o.stddev.s.long=stddev(long_ld)
+o.stddev.s.float=stddev(float_fd)
+o.stddev.s.double=stddev(double_dd)
+o.stddev.ff=string_sd
+o.stddev.ff=date_dtd
+
+o.median.s.int=median(int_id)
+o.median.s.long=median(long_ld)
+o.median.s.float=median(float_fd)
+o.median.s.double=median(double_dd)
+o.median.ff=string_sd
+o.median.ff=date_dtd
+
+o.percentile_20n.s.int=percentile(20,int_id)
+o.percentile_20n.s.long=percentile(20,long_ld)
+o.percentile_20n.s.float=percentile(20,float_fd)
+o.percentile_20n.s.double=percentile(20,double_dd)
+o.percentile_20n.ff=string_sd
+o.percentile_20n.ff=date_dtd
+
+o.percentile_20.s.str=percentile(20,string_sd)
+o.percentile_20.s.date=percentile(20,date_dtd)
+o.percentile_20.ff=int_id
+o.percentile_20.ff=long_ld
+
+o.percentile_60n.s.int=percentile(60,int_id)
+o.percentile_60n.s.long=percentile(60,long_ld)
+o.percentile_60n.s.float=percentile(60,float_fd)
+o.percentile_60n.s.double=percentile(60,double_dd)
+o.percentile_60n.ff=string_sd
+o.percentile_60n.ff=date_dtd
+
+o.percentile_60.s.str=percentile(60,string_sd)
+o.percentile_60.s.date=percentile(60,date_dtd)
+o.percentile_60.ff=int_id
+o.percentile_60.ff=long_ld
+
+o.minn.s.int=min(int_id)
+o.minn.s.long=min(long_ld)
+o.minn.s.float=min(float_fd)
+o.minn.s.double=min(double_dd)
+o.minn.ff=string_sd
+o.minn.ff=date_dtd
+
+o.min.s.str=min(string_sd)
+o.min.s.date=min(date_dtd)
+o.min.ff=int_id
+o.min.ff=long_ld
+
+o.maxn.s.int=max(int_id)
+o.maxn.s.long=max(long_ld)
+o.maxn.s.float=max(float_fd)
+o.maxn.s.double=max(double_dd)
+o.maxn.ff=string_sd
+o.maxn.ff=date_dtd
+
+o.max.s.str=max(string_sd)
+o.max.s.date=max(date_dtd)
+o.max.ff=int_id
+o.max.ff=long_ld
+
+o.countn.s.int=count(int_id)
+o.countn.s.long=count(long_ld)
+o.countn.s.float=count(float_fd)
+o.countn.s.double=count(double_dd)
+o.countn.ff=string_sd
+o.countn.ff=date_dtd
+
+o.count.s.str=count(string_sd)
+o.count.s.date=count(date_dtd)
+o.count.ff=int_id
+o.count.ff=long_ld
+
+o.uniquen.s.int=unique(int_id)
+o.uniquen.s.long=unique(long_ld)
+o.uniquen.s.float=unique(float_fd)
+o.uniquen.s.double=unique(double_dd)
+o.uniquen.ff=string_sd
+o.uniquen.ff=date_dtd
+
+o.unique.s.str=unique(string_sd)
+o.unique.s.date=unique(date_dtd)
+o.unique.ff=int_id
+o.unique.ff=long_ld
+
+o.missingn.s.int=missing(int_id)
+o.missingn.s.long=missing(long_ld)
+o.missingn.s.float=missing(float_fd)
+o.missingn.s.double=missing(double_dd)
+o.missingn.ff=string_sd
+o.missingn.ff=date_dtd
+
+o.missing.s.str=missing(string_sd)
+o.missing.s.date=missing(date_dtd)
+o.missing.ff=int_id
+o.missing.ff=long_ld
+
+o.multivalued.s.mean=mean(int_id)
+o.multivalued.ff=long_ldm
+o.multivalued.ff=string_sdm
+o.multivalued.ff=date_dtdm
+
+o.missingf.s.mean=mean(int_id)
+o.missingf.ff=date_dtd
+o.missingf.ff.date_dtd.dim=true
+o.missingf.ff=string_sd
+o.missingf.ff.string_sd.dim=true
+o.missingf.ff.string_sd.sm=true
+o.missingf.ff=date_dtdm
+o.missingf.ff.date_dtdm.sm=true

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/solr/analytics/functions.txt
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/solr/analytics/functions.txt b/solr/contrib/analytics/src/test-files/solr/analytics/functions.txt
new file mode 100644
index 0000000..ce73583
--- /dev/null
+++ b/solr/contrib/analytics/src/test-files/solr/analytics/functions.txt
@@ -0,0 +1,57 @@
+o.ar.s.sum=sum(add(int_id,float_fd))
+o.ar.s.sumc=sum(add_if_dd)
+o.ar.s.mean=mean(add(long_ld,double_dd,float_fd))
+o.ar.s.meanc=mean(add_ldf_dd)
+
+o.mr.s.sum=sum(mult(int_id,float_fd))
+o.mr.s.sumc=sum(mult_if_dd)
+o.mr.s.mean=mean(mult(long_ld,double_dd,float_fd))
+o.mr.s.meanc=mean(mult_ldf_dd)
+
+o.dr.s.sum=sum(div(int_id,float_fd))
+o.dr.s.sumc=sum(div_if_dd)
+o.dr.s.mean=mean(div(long_ld,double_dd))
+o.dr.s.meanc=mean(div_ld_dd)
+
+o.pr.s.sum=sum(pow(int_id,float_fd))
+o.pr.s.sumc=sum(pow_if_dd)
+o.pr.s.mean=mean(pow(long_ld,double_dd))
+o.pr.s.meanc=mean(pow_ld_dd)
+
+o.nr.s.sum=sum(neg(int_id))
+o.nr.s.sumc=sum(neg_id)
+o.nr.s.mean=mean(neg(long_ld))
+o.nr.s.meanc=mean(neg_ld)
+
+o.avr.s.sum=sum(abs(neg(int_id)))
+o.avr.s.sumc=sum(int_id)
+o.avr.s.mean=mean(abs(neg(int_id)))
+o.avr.s.meanc=mean(int_id)
+
+o.cnr.s.sum=sum(8)
+o.cnr.s.sumc=sum(const_8_dd)
+o.cnr.s.mean=mean(10)
+o.cnr.s.meanc=mean(const_10_dd)
+
+o.dmr.s.median=median(date_math(date_dtd,"+2YEARS"))
+o.dmr.s.medianc=median(dm_2y_dtd)
+o.dmr.s.max=max(date_math(date_dtd,"+2MONTHS"))
+o.dmr.s.maxc=max(dm_2m_dtd)
+
+o.cdr.s.median=median(1800-06-30T23:59:59Z)
+o.cdr.s.medianc=median(const_00_dtd)
+o.cdr.s.max=max(1804-06-30T23:59:59Z)
+o.cdr.s.maxc=max(const_04_dtd)
+
+o.csr.s.min=min("this is the first")
+o.csr.s.minc=min(const_first_sd)
+o.csr.s.max=max("this is the second")
+o.csr.s.maxc=max(const_second_sd)
+
+o.cr.s.min=concat("this is the first",min(string_sd))
+o.cr.s.minc=min(concat_first_sd)
+o.cr.s.max=concat("this is the second",max(string_sd))
+o.cr.s.maxc=max(concat_second_sd)
+
+o.ms.s.min=min(miss_dd)
+o.ms.s.max=max(miss_dd)

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/solr/analytics/noFacets.txt
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/solr/analytics/noFacets.txt b/solr/contrib/analytics/src/test-files/solr/analytics/noFacets.txt
new file mode 100644
index 0000000..84e43eb
--- /dev/null
+++ b/solr/contrib/analytics/src/test-files/solr/analytics/noFacets.txt
@@ -0,0 +1,74 @@
+o.sr.s.int_id=sum(int_id)
+o.sr.s.long_ld=sum(long_ld)
+o.sr.s.float_fd=sum(float_fd)
+o.sr.s.double_dd=sum(double_dd)
+
+o.sosr.s.int_id=sumofsquares(int_id)
+o.sosr.s.long_ld=sumofsquares(long_ld)
+o.sosr.s.float_fd=sumofsquares(float_fd)
+o.sosr.s.double_dd=sumofsquares(double_dd)
+
+o.mr.s.int_id=mean(int_id)
+o.mr.s.long_ld=mean(long_ld)
+o.mr.s.float_fd=mean(float_fd)
+o.mr.s.double_dd=mean(double_dd)
+
+o.str.s.int_id=stddev(int_id)
+o.str.s.long_ld=stddev(long_ld)
+o.str.s.float_fd=stddev(float_fd)
+o.str.s.double_dd=stddev(double_dd)
+
+o.medr.s.int_id=median(int_id)
+o.medr.s.long_ld=median(long_ld)
+o.medr.s.float_fd=median(float_fd)
+o.medr.s.double_dd=median(double_dd)
+o.medr.s.date_dtd=median(date_dtd)
+
+o.p2r.s.int_id=percentile(20,int_id)
+o.p2r.s.long_ld=percentile(20,long_ld)
+o.p2r.s.float_fd=percentile(20,float_fd)
+o.p2r.s.double_dd=percentile(20,double_dd)
+o.p2r.s.date_dtd=percentile(20,date_dtd)
+o.p2r.s.string_sd=percentile(20,string_sd)
+
+o.p6r.s.int_id=percentile(60,int_id)
+o.p6r.s.long_ld=percentile(60,long_ld)
+o.p6r.s.float_fd=percentile(60,float_fd)
+o.p6r.s.double_dd=percentile(60,double_dd)
+o.p6r.s.date_dtd=percentile(60,date_dtd)
+o.p6r.s.string_sd=percentile(60,string_sd)
+
+o.mir.s.int_id=min(int_id)
+o.mir.s.long_ld=min(long_ld)
+o.mir.s.float_fd=min(float_fd)
+o.mir.s.double_dd=min(double_dd)
+o.mir.s.date_dtd=min(date_dtd)
+o.mir.s.string_sd=min(string_sd)
+
+o.mar.s.int_id=max(int_id)
+o.mar.s.long_ld=max(long_ld)
+o.mar.s.float_fd=max(float_fd)
+o.mar.s.double_dd=max(double_dd)
+o.mar.s.date_dtd=max(date_dtd)
+o.mar.s.string_sd=max(string_sd)
+
+o.cr.s.int_id=count(int_id)
+o.cr.s.long_ld=count(long_ld)
+o.cr.s.float_fd=count(float_fd)
+o.cr.s.double_dd=count(double_dd)
+o.cr.s.date_dtd=count(date_dtd)
+o.cr.s.string_sd=count(string_sd)
+
+o.ur.s.int_id=unique(int_id)
+o.ur.s.long_ld=unique(long_ld)
+o.ur.s.float_fd=unique(float_fd)
+o.ur.s.double_dd=unique(double_dd)
+o.ur.s.date_dtd=unique(date_dtd)
+o.ur.s.string_sd=unique(string_sd)
+
+o.misr.s.int_id=missing(int_id)
+o.misr.s.long_ld=missing(long_ld)
+o.misr.s.float_fd=missing(float_fd)
+o.misr.s.double_dd=missing(double_dd)
+o.misr.s.date_dtd=missing(date_dtd)
+o.misr.s.string_sd=missing(string_sd)

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/solr/analytics/queryFacets.txt
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/solr/analytics/queryFacets.txt b/solr/contrib/analytics/src/test-files/solr/analytics/queryFacets.txt
new file mode 100644
index 0000000..84d8fc6
--- /dev/null
+++ b/solr/contrib/analytics/src/test-files/solr/analytics/queryFacets.txt
@@ -0,0 +1,27 @@
+o.ir.s.sum=sum(int_id)
+o.ir.s.mean=mean(int_id)
+o.ir.s.median=median(int_id)
+o.ir.s.percentile_8=percentile(8,int_id)
+o.ir.ff=string_sd
+o.ir.ff.string_sd.h=true
+o.ir.qf=float1
+o.ir.qf.float1.q=float_fd:[* TO 50]
+o.ir.qf=float2
+o.ir.qf.float2.q=float_fd:[* TO 30]
+
+o.lr.s.sum=sum(long_ld)
+o.lr.s.mean=mean(long_ld)
+o.lr.s.median=median(long_ld)
+o.lr.s.percentile_8=percentile(8,long_ld)
+o.lr.qf=string
+o.lr.qf.string.q=string_sd:abc1
+o.lr.qf.string.q=string_sd:abc2
+
+o.fr.s.sum=sum(float_fd)
+o.fr.s.mean=mean(float_fd)
+o.fr.s.median=median(float_fd)
+o.fr.s.percentile_8=percentile(8,float_fd)
+o.fr.qf=lad
+o.fr.qf.lad.q=long_ld:[20 TO *]
+o.fr.qf.lad.q=long_ld:[30 TO *]
+o.fr.qf.lad.q=double_dd:[* TO 50]

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/solr/analytics/rangeFacets.txt
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/solr/analytics/rangeFacets.txt b/solr/contrib/analytics/src/test-files/solr/analytics/rangeFacets.txt
new file mode 100644
index 0000000..29aae5d
--- /dev/null
+++ b/solr/contrib/analytics/src/test-files/solr/analytics/rangeFacets.txt
@@ -0,0 +1,161 @@
+o.ri.s.sum=sum(int_id)
+o.ri.s.mean=mean(int_id)
+o.ri.s.median=median(int_id)
+o.ri.s.count=count(int_id)
+o.ri.s.sumOfSquares=sumofsquares(int_id)
+o.ri.rf=long_ld
+o.ri.rf.long_ld.st=5
+o.ri.rf.long_ld.e=30
+o.ri.rf.long_ld.g=5
+o.ri.rf.long_ld.ib=lower
+o.ri.rf.long_ld.or=all
+o.ri.rf=double_dd
+o.ri.rf.double_dd.st=3
+o.ri.rf.double_dd.e=39
+o.ri.rf.double_dd.g=7
+o.ri.rf.double_dd.ib=upper
+o.ri.rf.double_dd.ib=outer
+o.ri.rf.double_dd.or=all
+o.ri.rf=date_dtd
+o.ri.rf.date_dtd.st=1007-01-01T23:59:59Z
+o.ri.rf.date_dtd.e=1044-01-01T23:59:59Z
+o.ri.rf.date_dtd.g=+7YEARS
+o.ri.rf.date_dtd.ib=lower
+o.ri.rf.date_dtd.ib=edge
+o.ri.rf.date_dtd.ib=outer
+o.ri.rf.date_dtd.or=all
+
+o.rf.s.sum=sum(float_fd)
+o.rf.s.mean=mean(float_fd)
+o.rf.s.median=median(float_fd)
+o.rf.s.count=count(float_fd)
+o.rf.s.sumOfSquares=sumofsquares(float_fd)
+o.rf.rf=long_ld
+o.rf.rf.long_ld.st=0
+o.rf.rf.long_ld.e=29
+o.rf.rf.long_ld.g=4
+o.rf.rf.long_ld.ib=all
+o.rf.rf.long_ld.or=all
+o.rf.rf=double_dd
+o.rf.rf.double_dd.st=4
+o.rf.rf.double_dd.e=47
+o.rf.rf.double_dd.g=11
+o.rf.rf.double_dd.ib=edge
+o.rf.rf.double_dd.or=all
+o.rf.rf=date_dtd
+o.rf.rf.date_dtd.st=1004-01-01T23:59:59Z
+o.rf.rf.date_dtd.e=1046-01-01T23:59:59Z
+o.rf.rf.date_dtd.g=+5YEARS
+o.rf.rf.date_dtd.ib=upper
+o.rf.rf.date_dtd.ib=edge
+o.rf.rf.date_dtd.or=all
+
+o.hi.s.sum=sum(int_id)
+o.hi.s.mean=mean(int_id)
+o.hi.s.median=median(int_id)
+o.hi.s.count=count(int_id)
+o.hi.s.sumOfSquares=sumofsquares(int_id)
+o.hi.rf=long_ld
+o.hi.rf.long_ld.st=5
+o.hi.rf.long_ld.e=30
+o.hi.rf.long_ld.g=5
+o.hi.rf.long_ld.he=true
+o.hi.rf.long_ld.ib=lower
+o.hi.rf.long_ld.or=all
+o.hi.rf=double_dd
+o.hi.rf.double_dd.st=3
+o.hi.rf.double_dd.e=39
+o.hi.rf.double_dd.g=7
+o.hi.rf.double_dd.he=true
+o.hi.rf.double_dd.ib=upper
+o.hi.rf.double_dd.ib=outer
+o.hi.rf.double_dd.or=all
+o.hi.rf=date_dtd
+o.hi.rf.date_dtd.st=1007-01-01T23:59:59Z
+o.hi.rf.date_dtd.e=1044-01-01T23:59:59Z
+o.hi.rf.date_dtd.g=+7YEARS
+o.hi.rf.date_dtd.he=true
+o.hi.rf.date_dtd.ib=lower
+o.hi.rf.date_dtd.ib=edge
+o.hi.rf.date_dtd.ib=outer
+o.hi.rf.date_dtd.or=all
+
+o.hf.s.sum=sum(float_fd)
+o.hf.s.mean=mean(float_fd)
+o.hf.s.median=median(float_fd)
+o.hf.s.count=count(float_fd)
+o.hf.s.sumOfSquares=sumofsquares(float_fd)
+o.hf.rf=long_ld
+o.hf.rf.long_ld.st=0
+o.hf.rf.long_ld.e=29
+o.hf.rf.long_ld.g=4
+o.hf.rf.long_ld.he=true
+o.hf.rf.long_ld.ib=all
+o.hf.rf.long_ld.or=all
+o.hf.rf=double_dd
+o.hf.rf.double_dd.st=4
+o.hf.rf.double_dd.e=47
+o.hf.rf.double_dd.g=11
+o.hf.rf.double_dd.he=true
+o.hf.rf.double_dd.ib=edge
+o.hf.rf.double_dd.or=all
+o.hf.rf=date_dtd
+o.hf.rf.date_dtd.st=1004-01-01T23:59:59Z
+o.hf.rf.date_dtd.e=1046-01-01T23:59:59Z
+o.hf.rf.date_dtd.g=+5YEARS
+o.hf.rf.date_dtd.he=true
+o.hf.rf.date_dtd.ib=upper
+o.hf.rf.date_dtd.ib=edge
+o.hf.rf.date_dtd.or=all
+
+o.mi.s.sum=sum(int_id)
+o.mi.s.mean=mean(int_id)
+o.mi.s.median=median(int_id)
+o.mi.s.count=count(int_id)
+o.mi.s.sumOfSquares=sumofsquares(int_id)
+o.mi.rf=long_ld
+o.mi.rf.long_ld.st=5
+o.mi.rf.long_ld.e=30
+o.mi.rf.long_ld.g=4,2,6,3
+o.mi.rf.long_ld.ib=lower
+o.mi.rf.long_ld.or=all
+o.mi.rf=double_dd
+o.mi.rf.double_dd.st=3
+o.mi.rf.double_dd.e=39
+o.mi.rf.double_dd.g=3,1,7
+o.mi.rf.double_dd.ib=upper
+o.mi.rf.double_dd.ib=outer
+o.mi.rf.double_dd.or=all
+o.mi.rf=date_dtd
+o.mi.rf.date_dtd.st=1007-01-01T23:59:59Z
+o.mi.rf.date_dtd.e=1044-01-01T23:59:59Z
+o.mi.rf.date_dtd.g=+2YEARS,+7YEARS
+o.mi.rf.date_dtd.ib=lower
+o.mi.rf.date_dtd.ib=edge
+o.mi.rf.date_dtd.ib=outer
+o.mi.rf.date_dtd.or=all
+
+o.mf.s.sum=sum(float_fd)
+o.mf.s.mean=mean(float_fd)
+o.mf.s.median=median(float_fd)
+o.mf.s.count=count(float_fd)
+o.mf.s.sumOfSquares=sumofsquares(float_fd)
+o.mf.rf=long_ld
+o.mf.rf.long_ld.st=0
+o.mf.rf.long_ld.e=29
+o.mf.rf.long_ld.g=1,4
+o.mf.rf.long_ld.ib=all
+o.mf.rf.long_ld.or=all
+o.mf.rf=double_dd
+o.mf.rf.double_dd.st=4
+o.mf.rf.double_dd.e=47
+o.mf.rf.double_dd.g=2,3,11
+o.mf.rf.double_dd.ib=edge
+o.mf.rf.double_dd.or=all
+o.mf.rf=date_dtd
+o.mf.rf.date_dtd.st=1004-01-01T23:59:59Z
+o.mf.rf.date_dtd.e=1046-01-01T23:59:59Z
+o.mf.rf.date_dtd.g=+4YEARS,+5YEARS
+o.mf.rf.date_dtd.ib=upper
+o.mf.rf.date_dtd.ib=edge
+o.mf.rf.date_dtd.or=all

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/solr/collection1/conf/schema-analytics.xml
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/solr/collection1/conf/schema-analytics.xml b/solr/contrib/analytics/src/test-files/solr/collection1/conf/schema-analytics.xml
index 04b22b1..82d408a 100644
--- a/solr/contrib/analytics/src/test-files/solr/collection1/conf/schema-analytics.xml
+++ b/solr/contrib/analytics/src/test-files/solr/collection1/conf/schema-analytics.xml
@@ -25,7 +25,7 @@
 
   -->
 
-<schema name="schema-docValues" version="1.6">
+<schema name="schema-analytics" version="1.0">
 
 
   <!-- field type definitions... note that the "name" attribute is
@@ -54,7 +54,7 @@
   <fieldType name="uuid" class="solr.UUIDField"/>
 
 
-  <field name="id" type="string" required="true"/>
+  <field name="id" type="string" docValues="true" multiValued="false" required="true"/>
 
   <field name="floatdv" type="float" indexed="false" stored="false" docValues="true" default="1"/>
   <field name="intdv" type="int" indexed="false" stored="false" docValues="true" default="2"/>
@@ -63,8 +63,7 @@
   <field name="datedv" type="date" indexed="false" stored="false" docValues="true" default="1995-12-31T23:59:59.999Z"/>
 
   <field name="stringdv" type="string" indexed="false" stored="false" docValues="true" default="solr"/>
-  <field name="stringdvm" type="string" indexed="false" stored="false" docValues="true" default="solr"
-         multiValued="true"/>
+  <field name="stringdvm" type="string" indexed="false" stored="false" docValues="true" default="solr" multiValued="true"/>
 
   <dynamicField name="*_i" type="int" indexed="true" stored="true" docValues="false" multiValued="false"/>
   <dynamicField name="*_id" type="int" indexed="true" stored="true" docValues="true" multiValued="false"/>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/solr/collection1/conf/solrconfig-analytics.xml
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/solr/collection1/conf/solrconfig-analytics.xml b/solr/contrib/analytics/src/test-files/solr/collection1/conf/solrconfig-analytics.xml
new file mode 100644
index 0000000..31d6437
--- /dev/null
+++ b/solr/contrib/analytics/src/test-files/solr/collection1/conf/solrconfig-analytics.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" ?>
+
+<!--
+ 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.
+-->
+
+<config>
+  <luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
+  <dataDir>${solr.data.dir:}</dataDir>
+  <xi:include href="solrconfig.snippet.randomindexconfig.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
+  <directoryFactory name="DirectoryFactory" class="${solr.directoryFactory:solr.RAMDirectoryFactory}"/>
+  <schemaFactory class="ClassicIndexSchemaFactory"/>
+
+  <requestHandler name="standard" class="solr.StandardRequestHandler">
+    <arr name="components">
+      <str>query</str>
+      <str>facet</str>
+      <str>analytics</str>
+      <str>highlight</str>
+      <str>debug</str>
+      <str>expand</str>
+    </arr>
+  </requestHandler>
+
+  <searchComponent name="analytics" class="org.apache.solr.handler.component.AnalyticsComponent" />
+
+  <requestHandler name="/analytics" class="org.apache.solr.handler.AnalyticsHandler" />
+
+</config>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/solr/collection1/conf/solrconfig-basic.xml
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/solr/collection1/conf/solrconfig-basic.xml b/solr/contrib/analytics/src/test-files/solr/collection1/conf/solrconfig-basic.xml
deleted file mode 100644
index 604bb17..0000000
--- a/solr/contrib/analytics/src/test-files/solr/collection1/conf/solrconfig-basic.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" ?>
-
-<!--
- 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.
--->
-
-<config>
-  <luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
-  <dataDir>${solr.data.dir:}</dataDir>
-  <xi:include href="solrconfig.snippet.randomindexconfig.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
-  <directoryFactory name="DirectoryFactory" class="${solr.directoryFactory:solr.RAMDirectoryFactory}"/>
-  <schemaFactory class="ClassicIndexSchemaFactory"/>
-
-  <requestHandler name="standard" class="solr.StandardRequestHandler">
-    <arr name="components">
-      <str>query</str>
-      <str>facet</str>
-      <str>analytics</str>
-      <str>highlight</str>
-      <str>debug</str>
-      <str>expand</str>
-    </arr>
-  </requestHandler>
-
-  <searchComponent name="analytics" class="org.apache.solr.handler.component.AnalyticsComponent" />
-
-</config>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/solr/configsets/cloud-analytics/conf/schema.xml
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/solr/configsets/cloud-analytics/conf/schema.xml b/solr/contrib/analytics/src/test-files/solr/configsets/cloud-analytics/conf/schema.xml
new file mode 100644
index 0000000..d446437
--- /dev/null
+++ b/solr/contrib/analytics/src/test-files/solr/configsets/cloud-analytics/conf/schema.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" ?>
+<!--
+ 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.
+-->
+
+<!-- The Solr schema file for cloud analytics, version 1.0  -->
+
+<schema name="cloud-analytics" version="1.0">
+
+  <fieldType name="int" class="solr.TrieIntField" precisionStep="8" positionIncrementGap="0"/>
+  <fieldType name="float" class="solr.TrieFloatField" precisionStep="8" positionIncrementGap="0"/>
+  <fieldType name="long" class="solr.TrieLongField" precisionStep="8" positionIncrementGap="0"/>
+  <fieldType name="double" class="solr.TrieDoubleField" precisionStep="8" positionIncrementGap="0"/>
+  <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/>
+  <fieldType name="string" class="solr.StrField" sortMissingLast="true"/>
+
+  <!-- format for date is 1995-12-31T23:59:59.999Z and only the fractional
+       seconds part (.999) is optional.
+    -->
+  <fieldType name="date" class="solr.TrieDateField" sortMissingLast="true" precisionStep="6"/>
+
+
+  <field name="id" type="string" indexed="true" stored="true" multiValued="false" required="true"/>
+  <field name="signatureField" type="string" indexed="true" stored="false"/>
+
+  <!-- for versioning -->
+  <field name="_version_" type="long" indexed="true" stored="true" multiValued="false"/>
+  <!-- points to the root document of a block of nested documents -->
+  <field name="_root_" type="string" indexed="true" stored="true"/>
+
+  <field name="_route_" type="string" indexed="true" stored="true" multiValued="false"/>
+
+  <!-- dynamic fields with doc-values (for analytics) -->
+  <dynamicField name="*_id"  type="int" docValues="true"    indexed="true"  stored="true"/>
+  <dynamicField name="*_idm" type="int" docValues="true"    indexed="true"  stored="true"  multiValued="true"/>
+  <dynamicField name="*_sd"  type="string" docValues="true"  indexed="true"  stored="true" />
+  <dynamicField name="*_sdm" type="string" docValues="true"  indexed="true"  stored="true" multiValued="true"/>
+  <dynamicField name="*_ld"  type="long" docValues="true"   indexed="true"  stored="true"/>
+  <dynamicField name="*_ldm" type="long" docValues="true"   indexed="true"  stored="true"  multiValued="true"/>
+  <dynamicField name="*_bd"  type="boolean" docValues="true" indexed="true" stored="true"/>
+  <dynamicField name="*_bdm" type="boolean" docValues="true" indexed="true" stored="true"  multiValued="true"/>
+  <dynamicField name="*_fd"  type="float" docValues="true"  indexed="true"  stored="true"/>
+  <dynamicField name="*_fdm" type="float" docValues="true"  indexed="true"  stored="true"  multiValued="true"/>
+  <dynamicField name="*_dd"  type="double" docValues="true" indexed="true"  stored="true"/>
+  <dynamicField name="*_ddm" type="double" docValues="true" indexed="true"  stored="true"  multiValued="true"/>
+  <dynamicField name="*_dtd"  type="date" docValues="true" indexed="true"  stored="true"/>
+  <dynamicField name="*_dtdm" type="date" docValues="true" indexed="true"  stored="true"  multiValued="true"/>
+
+  <uniqueKey>id</uniqueKey>
+</schema>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test-files/solr/configsets/cloud-analytics/conf/solrconfig.xml
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test-files/solr/configsets/cloud-analytics/conf/solrconfig.xml b/solr/contrib/analytics/src/test-files/solr/configsets/cloud-analytics/conf/solrconfig.xml
new file mode 100644
index 0000000..102e39e
--- /dev/null
+++ b/solr/contrib/analytics/src/test-files/solr/configsets/cloud-analytics/conf/solrconfig.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?>
+
+<!--
+ 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.
+-->
+
+<!-- Minimal solrconfig.xml with /select, /admin and /update only -->
+
+<config>
+
+  <dataDir>${solr.data.dir:}</dataDir>
+
+  <directoryFactory name="DirectoryFactory"
+                    class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/>
+  <schemaFactory class="ClassicIndexSchemaFactory"/>
+
+  <luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
+
+  <updateHandler class="solr.DirectUpdateHandler2">
+    <commitWithin>
+      <softCommit>${solr.commitwithin.softcommit:true}</softCommit>
+    </commitWithin>
+    <updateLog></updateLog>
+  </updateHandler>
+
+  <requestHandler name="/select" class="solr.SearchHandler">
+    <lst name="defaults">
+      <str name="echoParams">explicit</str>
+      <str name="indent">true</str>
+      <str name="df">text</str>
+    </lst>
+
+    <arr name="components">
+      <str>query</str>
+      <str>facet</str>
+      <str>analytics</str>
+      <str>highlight</str>
+      <str>debug</str>
+      <str>expand</str>
+    </arr>
+  </requestHandler>
+
+  <searchComponent name="analytics" class="org.apache.solr.handler.component.AnalyticsComponent" />
+  <requestHandler name="/analytics" class="org.apache.solr.handler.AnalyticsHandler" />
+</config>
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/AbstractAnalyticsStatsCloudTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/AbstractAnalyticsStatsCloudTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/AbstractAnalyticsStatsCloudTest.java
new file mode 100644
index 0000000..59ebbcb
--- /dev/null
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/AbstractAnalyticsStatsCloudTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.solr.analytics;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+
+import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
+import org.apache.solr.analytics.util.MedianCalculator;
+import org.apache.solr.analytics.util.OrdinalCalculator;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.request.QueryRequest;
+import org.apache.solr.client.solrj.request.UpdateRequest;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.cloud.AbstractDistribZkTestBase;
+import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.util.NamedList;
+import org.junit.AfterClass;
+
+public class AbstractAnalyticsStatsCloudTest extends SolrCloudTestCase {
+  
+  protected static final String[] BASEPARMS = new String[]{ "q", "*:*", "indent", "true", "olap", "true", "rows", "0" };
+  protected static final HashMap<String,Object> defaults = new HashMap<>();
+
+  public static enum VAL_TYPE {
+    INTEGER("int"),
+    LONG("long"),
+    FLOAT("float"),
+    DOUBLE("double"),
+    STRING("str"),
+    DATE("date");
+
+    private VAL_TYPE (final String text) {
+      this.text = text;
+    }
+
+    private final String text;
+
+    @Override
+    public String toString() {
+      return text;
+    }
+  }
+  
+
+
+  protected static final String COLLECTIONORALIAS = "collection1";
+  protected static final int TIMEOUT = DEFAULT_TIMEOUT;
+  protected static final String id = "id";
+
+  public static void setupCluster() throws Exception {
+    configureCluster(4)
+        .addConfig("conf", configset("cloud-analytics"))
+        .configure();
+
+    CollectionAdminRequest.createCollection(COLLECTIONORALIAS, "conf", 2, 1).process(cluster.getSolrClient());
+    AbstractDistribZkTestBase.waitForRecoveriesToFinish(COLLECTIONORALIAS, cluster.getSolrClient().getZkStateReader(),
+        false, true, TIMEOUT);
+    cleanIndex();
+  }
+
+  public static void cleanIndex() throws Exception {
+    new UpdateRequest()
+        .deleteByQuery("*:*")
+        .commit(cluster.getSolrClient(), COLLECTIONORALIAS);
+  }
+  
+  @AfterClass
+  public static void afterClassAbstractAnalysis() {
+    defaults.clear();
+  }
+
+  protected NamedList<Object> queryCloudAnalytics(String[] testParams) throws SolrServerException, IOException, InterruptedException {
+    ModifiableSolrParams params = new ModifiableSolrParams();
+    params.set("q", "*:*");
+    params.set("indent", "true");
+    params.set("olap", "true");
+    params.set("rows", "0");
+    for (int i = 0; i + 1 < testParams.length;) {
+      params.set(testParams[i++], testParams[i++]);
+    }
+    cluster.waitForAllNodes(10000);
+    QueryRequest qreq = new QueryRequest(params);
+    QueryResponse resp = qreq.process(cluster.getSolrClient(), COLLECTIONORALIAS);
+    return resp.getResponse();
+  }
+  
+  @SuppressWarnings("unchecked")
+  protected <T> T getValue(NamedList<Object> response, String infoName, String exprName) {
+    return (T)response.findRecursive(AnalyticsResponseHeadings.COMPLETED_OLD_HEADER,
+                                     infoName,
+                                     exprName);
+  }
+
+  public <T extends Number & Comparable<T>> Double calculateNumberStat(ArrayList<T> list, String stat) {
+    Double result;
+    if (stat.equals("median")) {
+      result = MedianCalculator.getMedian(list);
+    } else if (stat.equals("mean")) {
+      double d = 0;
+      for (T element : list) {
+        d += element.doubleValue();
+      }
+      result = Double.valueOf(d/list.size());
+    } else if (stat.equals("sum")) {
+      double d = 0;
+      for (T element : list) {
+        d += element.doubleValue();
+      }
+      result = Double.valueOf(d);
+    } else if (stat.equals("sumOfSquares")) {
+      double d = 0;
+      for (T element : list) {
+        d += element.doubleValue()*element.doubleValue();
+      }
+      result = Double.valueOf(d);
+    } else if (stat.equals("stddev")) {
+      double sum = 0;
+      double sumSquares = 0;
+      for (T element : list) {
+        sum += element.doubleValue();
+        sumSquares += element.doubleValue()*element.doubleValue();
+      }
+      result = Math.sqrt(sumSquares/list.size()-sum*sum/(list.size()*list.size()));
+    } else {
+      throw new IllegalArgumentException();
+    }
+    return result;
+  }
+
+  public <T extends Comparable<T>> Object calculateStat(ArrayList<T> list, String stat) {
+    Object result;
+    if (stat.contains("perc_")) {
+      ArrayList<Integer> percs = new ArrayList<>(1);
+      int ord = (int) Math.ceil(Double.parseDouble(stat.substring(5))/100 * list.size()) - 1;
+      percs.add(ord);
+      OrdinalCalculator.putOrdinalsInPosition(list, percs);
+      result = list.get(percs.get(0));
+    } else if (stat.equals("count")) {
+      result = Long.valueOf(list.size());
+    } else if (stat.equals("unique")) {
+      HashSet<T> set = new HashSet<>();
+      set.addAll(list);
+      result = Long.valueOf((long)set.size());
+    } else if (stat.equals("max")) {
+      Collections.sort(list);
+      result = list.get(list.size()-1);
+    } else if (stat.equals("min")) {
+      Collections.sort(list);
+      result = list.get(0);
+    } else {
+      result = null;
+    }
+    return result;
+  }
+
+  @SuppressWarnings("unchecked")
+  public <T extends Comparable<T>> Long calculateMissing(ArrayList<T> list, String type) {
+    T def = (T)defaults.get(type);
+    long miss = 0;
+    for (T element : list) {
+      if (element.compareTo(def)==0) {
+        miss++;
+      }
+    }
+    return Long.valueOf(miss);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/AbstractAnalyticsStatsTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/AbstractAnalyticsStatsTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/AbstractAnalyticsStatsTest.java
index c3d2f28..18f51a9 100644
--- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/AbstractAnalyticsStatsTest.java
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/AbstractAnalyticsStatsTest.java
@@ -37,8 +37,9 @@ import javax.xml.xpath.XPathFactory;
 import org.apache.commons.lang.StringUtils;
 import org.apache.lucene.util.IOUtils;
 import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
 import org.apache.solr.analytics.util.MedianCalculator;
-import org.apache.solr.analytics.util.PercentileCalculator;
+import org.apache.solr.analytics.util.OrdinalCalculator;
 import org.apache.solr.request.SolrQueryRequest;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
@@ -107,7 +108,7 @@ public class AbstractAnalyticsStatsTest extends SolrTestCaseJ4 {
   public Object getStatResult(String section, String name, VAL_TYPE type) throws XPathExpressionException {
 
     // Construct the XPath expression. The form better not change or all these will fail.
-    StringBuilder sb = new StringBuilder("/response/lst[@name='stats']/lst[@name='").append(section).append("']");
+    StringBuilder sb = new StringBuilder("/response/lst[@name='"+AnalyticsResponseHeadings.COMPLETED_OLD_HEADER+"']/lst[@name='").append(section).append("']");
 
     // This is a little fragile in that it demands the elements have the same name as type, i.e. when looking for a
     // VAL_TYPE.DOUBLE, the element in question is <double name="blah">47.0</double>.
@@ -170,8 +171,11 @@ public class AbstractAnalyticsStatsTest extends SolrTestCaseJ4 {
   public <T extends Comparable<T>> Object calculateStat(ArrayList<T> list, String stat) {
     Object result;
     if (stat.contains("perc_")) {
-      double[] perc = new double[]{Double.parseDouble(stat.substring(5))/100};
-      result = PercentileCalculator.getPercentiles(list, perc).get(0);
+      ArrayList<Integer> percs = new ArrayList<>(1);
+      int ord = (int) Math.ceil(Double.parseDouble(stat.substring(5))/100 * list.size()) - 1;
+      percs.add(ord);
+      OrdinalCalculator.putOrdinalsInPosition(list, percs);
+      result = list.get(percs.get(0));
     } else if (stat.equals("count")) {
       result = Long.valueOf(list.size());
     } else if (stat.equals("unique")) {
@@ -207,7 +211,7 @@ public class AbstractAnalyticsStatsTest extends SolrTestCaseJ4 {
   }
 
   public static String[] fileToStringArr(Class<?> clazz, String fileName) throws FileNotFoundException {
-    InputStream in = clazz.getResourceAsStream(fileName);
+    InputStream in = clazz.getResourceAsStream("/solr/analytics/" + fileName);
     if (in == null) throw new FileNotFoundException("Resource not found: " + fileName);
     Scanner file = new Scanner(in, "UTF-8");
     try { 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/NoFacetCloudTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/NoFacetCloudTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/NoFacetCloudTest.java
new file mode 100644
index 0000000..71503af
--- /dev/null
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/NoFacetCloudTest.java
@@ -0,0 +1,557 @@
+/*
+ * 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.solr.analytics;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.solr.client.solrj.request.UpdateRequest;
+import org.apache.solr.common.util.NamedList;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class NoFacetCloudTest extends AbstractAnalyticsStatsCloudTest {
+  static public final int INT = 71;
+  static public final int LONG = 36;
+  static public final int FLOAT = 93;
+  static public final int DOUBLE = 49;
+  static public final int DATE = 12;
+  static public final int STRING = 28;
+  static public final int NUM_LOOPS = 100;
+  
+  //INT
+  static ArrayList<Integer> intTestStart; 
+  static long intMissing = 0;
+  
+  //LONG
+  static ArrayList<Long> longTestStart; 
+  static long longMissing = 0;
+  
+  //FLOAT
+  static ArrayList<Float> floatTestStart; 
+  static long floatMissing = 0;
+  
+  //DOUBLE
+  static ArrayList<Double> doubleTestStart; 
+  static long doubleMissing = 0;
+  
+  //DATE
+  static ArrayList<String> dateTestStart; 
+  static long dateMissing = 0;
+  
+  //STR
+  static ArrayList<String> stringTestStart; 
+  static long stringMissing = 0;
+  
+  @BeforeClass
+  public static void populate() throws Exception {
+    setupCluster();
+    defaults.put("int_id", new Integer(0));
+    defaults.put("long_ld", new Long(0));
+    defaults.put("float_fd", new Float(0));
+    defaults.put("double_dd", new Double(0));
+    defaults.put("date_dtd", "1800-12-31T23:59:59Z");
+    defaults.put("string_sd", "str0");
+    
+    intTestStart = new ArrayList<>();
+    longTestStart = new ArrayList<>();
+    floatTestStart = new ArrayList<>();
+    doubleTestStart = new ArrayList<>();
+    dateTestStart = new ArrayList<>();
+    stringTestStart = new ArrayList<>();
+    
+    UpdateRequest req = new UpdateRequest();
+    for (int j = 0; j < NUM_LOOPS; ++j) {
+      int i = j%INT;
+      long l = j%LONG;
+      float f = j%FLOAT;
+      double d = j%DOUBLE;
+      String dt = (1800+j%DATE) + "-12-31T23:59:59Z";
+      String s = "str" + (j%STRING);
+      List<String> fields = new ArrayList<>();
+      fields.add("id"); fields.add("1000"+j);
+      
+      if( i != 0 ){
+        fields.add("int_id"); fields.add("" + i);
+        intTestStart.add(i);
+      } else intMissing++;
+      
+      if( l != 0l ){
+        fields.add("long_ld"); fields.add("" + l);
+        longTestStart.add(l);
+      } else longMissing++;
+      
+      if( f != 0.0f ){
+        fields.add("float_fd"); fields.add("" + f);
+        floatTestStart.add(f);
+      } else floatMissing++;
+      
+      if( d != 0.0d ){
+        fields.add("double_dd"); fields.add("" + d);
+        doubleTestStart.add(d);
+      } else doubleMissing++;
+      
+      if( (j%DATE) != 0 ){
+        fields.add("date_dtd"); fields.add(dt);
+        dateTestStart.add(dt);
+      } else dateMissing++;
+      
+      if( (j%STRING) != 0 ){
+        fields.add("string_sd"); fields.add(s);
+        stringTestStart.add(s);
+      } else stringMissing++;
+
+      req.add(fields.toArray(new String[0]));
+    }
+    req.commit(cluster.getSolrClient(), COLLECTIONORALIAS);
+  }
+      
+  @Test
+  public void sumTest() throws Exception {
+    String[] params = new String[] {
+        "o.sr.s.int_id", "sum(int_id)",
+        "o.sr.s.long_ld", "sum(long_ld)",
+        "o.sr.s.float_fd", "sum(float_fd)",
+        "o.sr.s.double_dd", "sum(double_dd)"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int
+    Double intResult = getValue(response, "sr", "int_id");
+    Double intTest = (Double)calculateNumberStat(intTestStart, "sum");
+    assertEquals(responseStr, intResult,intTest);
+    
+    //Long
+    Double longResult = getValue(response, "sr", "long_ld");
+    Double longTest = (Double)calculateNumberStat(longTestStart, "sum");
+    assertEquals(responseStr, longResult,longTest);
+    
+    //Float
+    Double floatResult = getValue(response, "sr", "float_fd");
+    Double floatTest = (Double)calculateNumberStat(floatTestStart, "sum");
+    assertEquals(responseStr, floatResult,floatTest);
+    
+    //Double
+    Double doubleResult = getValue(response, "sr", "double_dd");
+        Double doubleTest = (Double) calculateNumberStat(doubleTestStart, "sum");
+    assertEquals(responseStr, doubleResult,doubleTest);
+  }
+  
+  @Test
+  public void meanTest() throws Exception { 
+    String[] params = new String[] {
+        "o.mr.s.int_id", "mean(int_id)",
+        "o.mr.s.long_ld", "mean(long_ld)",
+        "o.mr.s.float_fd", "mean(float_fd)",
+        "o.mr.s.double_dd", "mean(double_dd)"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int
+    Double intResult = getValue(response, "mr", "int_id");
+    Double intTest = (Double)calculateNumberStat(intTestStart, "mean");
+    assertEquals(responseStr, intResult,intTest);
+    
+    //Long
+    Double longResult = getValue(response, "mr", "long_ld");
+    Double longTest = (Double)calculateNumberStat(longTestStart, "mean");
+    assertEquals(responseStr, longResult,longTest);
+    
+    //Float
+    Double floatResult = getValue(response, "mr", "float_fd");
+    Double floatTest = (Double)calculateNumberStat(floatTestStart, "mean");
+    assertEquals(responseStr, floatResult,floatTest);
+    
+    //Double
+    Double doubleResult = getValue(response, "mr", "double_dd");
+    Double doubleTest = (Double)calculateNumberStat(doubleTestStart, "mean");
+    assertEquals(responseStr, doubleResult,doubleTest);
+  }
+  
+  @Test
+  public void stddevTest() throws Exception { 
+    String[] params = new String[] {
+        "o.str.s.int_id", "stddev(int_id)",
+        "o.str.s.long_ld", "stddev(long_ld)",
+        "o.str.s.float_fd", "stddev(float_fd)",
+        "o.str.s.double_dd", "stddev(double_dd)"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int
+    Double intResult = getValue(response, "str", "int_id");
+    Double intTest = (Double)calculateNumberStat(intTestStart, "stddev");
+    assertEquals(responseStr, intResult, intTest, 0.00000000001);
+    
+    //Long
+    Double longResult = getValue(response, "str", "long_ld");
+    Double longTest = (Double)calculateNumberStat(longTestStart, "stddev");
+    assertEquals(responseStr, longResult, longTest, 0.00000000001);
+    
+    //Float
+    Double floatResult = getValue(response, "str", "float_fd");
+    Double floatTest = (Double)calculateNumberStat(floatTestStart, "stddev");
+    assertEquals(responseStr, floatResult, floatTest, 0.00000000001);
+
+
+    //Double
+    Double doubleResult = getValue(response, "str", "double_dd");
+    Double doubleTest = (Double)calculateNumberStat(doubleTestStart, "stddev");
+    assertEquals(responseStr, doubleResult, doubleTest, 0.00000000001);
+  }
+  
+  @Test
+  public void medianTest() throws Exception { 
+    String[] params = new String[] {
+        "o.medr.s.int_id", "median(int_id)",
+        "o.medr.s.long_ld", "median(long_ld)",
+        "o.medr.s.float_fd", "median(float_fd)",
+        "o.medr.s.double_dd", "median(double_dd)",
+        "o.medr.s.date_dtd", "median(date_dtd)"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int
+    Double intResult = getValue(response, "medr", "int_id");
+    Double intTest = (Double)calculateNumberStat(intTestStart, "median");
+    assertEquals(responseStr, intResult,intTest);
+    
+    //Long
+    Double longResult = getValue(response, "medr", "long_ld");
+    Double longTest = (Double)calculateNumberStat(longTestStart, "median");
+    assertEquals(responseStr, longResult,longTest);
+    
+    //Float
+    Double floatResult = getValue(response, "medr", "float_fd");
+    Double floatTest = (Double)calculateNumberStat(floatTestStart, "median");
+    assertEquals(responseStr, floatResult,floatTest);
+    
+    //Double
+    Double doubleResult = getValue(response, "medr", "double_dd");
+    Double doubleTest = (Double)calculateNumberStat(doubleTestStart, "median");
+    assertEquals(responseStr, doubleResult,doubleTest);
+    
+    // TODO: Add test for date median
+  }
+  
+  @Test
+  public void perc20Test() throws Exception {
+    String[] params = new String[] {
+        "o.p2r.s.int_id", "percentile(20,int_id)",
+        "o.p2r.s.long_ld", "percentile(20,long_ld)",
+        "o.p2r.s.float_fd", "percentile(20,float_fd)",
+        "o.p2r.s.double_dd", "percentile(20,double_dd)",
+        "o.p2r.s.date_dtd", "string(percentile(20,date_dtd))",
+        "o.p2r.s.string_sd", "percentile(20,string_sd)"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int 20
+    Integer intResult = getValue(response, "p2r", "int_id");
+    Integer intTest = (Integer)calculateStat(intTestStart, "perc_20");
+    assertEquals(responseStr, intResult,intTest);
+
+    //Long 20
+    Long longResult = getValue(response, "p2r", "long_ld");
+    Long longTest = (Long)calculateStat(longTestStart, "perc_20");
+    assertEquals(responseStr, longResult,longTest);
+
+    //Float 20
+    Float floatResult = getValue(response, "p2r", "float_fd");
+    Float floatTest = (Float)calculateStat(floatTestStart, "perc_20");
+    assertEquals(responseStr, floatResult,floatTest);
+
+    //Double 20
+    Double doubleResult = getValue(response, "p2r", "double_dd");
+    Double doubleTest = (Double)calculateStat(doubleTestStart, "perc_20");
+    assertEquals(responseStr, doubleResult,doubleTest);
+
+    //Date 20
+    String dateResult = getValue(response, "p2r", "date_dtd");
+    String dateTest = (String)calculateStat(dateTestStart, "perc_20");
+    assertEquals(responseStr, dateResult,dateTest);
+
+    //String 20
+    String stringResult = getValue(response, "p2r", "string_sd");
+    String stringTest = (String)calculateStat(stringTestStart, "perc_20");
+    assertEquals(responseStr, stringResult,stringTest);
+  }
+  
+  @Test
+  public void perc60Test() throws Exception { 
+    String[] params = new String[] {
+        "o.p6r.s.int_id", "percentile(60,int_id)",
+        "o.p6r.s.long_ld", "percentile(60,long_ld)",
+        "o.p6r.s.float_fd", "percentile(60,float_fd)",
+        "o.p6r.s.double_dd", "percentile(60,double_dd)",
+        "o.p6r.s.date_dtd", "string(percentile(60,date_dtd))",
+        "o.p6r.s.string_sd", "percentile(60,string_sd)"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int 60
+    Integer intResult = getValue(response, "p6r", "int_id");
+    Integer intTest = (Integer)calculateStat(intTestStart, "perc_60");
+    assertEquals(responseStr, intResult,intTest);
+
+    //Long 60
+    Long longResult = getValue(response, "p6r", "long_ld");
+    Long longTest = (Long)calculateStat(longTestStart, "perc_60");
+    assertEquals(responseStr, longResult,longTest);
+
+    //Float 60
+    Float floatResult = getValue(response, "p6r", "float_fd");
+    Float floatTest = (Float)calculateStat(floatTestStart, "perc_60");
+    assertEquals(responseStr, floatResult,floatTest);
+
+    //Double 60
+    Double doubleResult = getValue(response, "p6r", "double_dd");
+    Double doubleTest = (Double)calculateStat(doubleTestStart, "perc_60");
+    assertEquals(responseStr, doubleResult,doubleTest);
+
+    //Date 60
+    String dateResult = getValue(response, "p6r", "date_dtd");
+    String dateTest = (String)calculateStat(dateTestStart, "perc_60");
+    assertEquals(responseStr, dateResult,dateTest);
+
+    //String 60
+    String stringResult = getValue(response, "p6r", "string_sd");
+    String stringTest = (String)calculateStat(stringTestStart, "perc_60");
+    assertEquals(responseStr, stringResult,stringTest);
+  }
+  
+  @Test
+  public void minTest() throws Exception { 
+    String[] params = new String[] {
+        "o.mir.s.int_id", "min(int_id)",
+        "o.mir.s.long_ld", "min(long_ld)",
+        "o.mir.s.float_fd", "min(float_fd)",
+        "o.mir.s.double_dd", "min(double_dd)",
+        "o.mir.s.date_dtd", "string(min(date_dtd))",
+        "o.mir.s.string_sd", "min(string_sd)"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int
+    Integer intResult = getValue(response, "mir", "int_id");
+    Integer intTest = (Integer)calculateStat(intTestStart, "min");
+    assertEquals(responseStr, intResult,intTest);
+
+    //Long
+    Long longResult = getValue(response, "mir", "long_ld");
+    Long longTest = (Long)calculateStat(longTestStart, "min");
+    assertEquals(responseStr, longResult,longTest);
+
+    //Float
+    Float floatResult = getValue(response, "mir", "float_fd");
+    Float floatTest = (Float)calculateStat(floatTestStart, "min");
+    assertEquals(responseStr, floatResult,floatTest);
+
+    //Double
+    Double doubleResult = getValue(response, "mir", "double_dd");
+    Double doubleTest = (Double)calculateStat(doubleTestStart, "min");
+    assertEquals(responseStr, doubleResult,doubleTest);
+
+    //Date
+    String dateResult = getValue(response, "mir", "date_dtd");
+    String dateTest = (String)calculateStat(dateTestStart, "min");
+    assertEquals(responseStr, dateResult,dateTest);
+
+    //String
+    String stringResult = getValue(response, "mir", "string_sd");
+    String stringTest = (String)calculateStat(stringTestStart, "min");
+    assertEquals(responseStr, stringResult,stringTest);
+  }
+  
+  @Test
+  public void maxTest() throws Exception { 
+    String[] params = new String[] {
+        "o.mar.s.int_id", "max(int_id)",
+        "o.mar.s.long_ld", "max(long_ld)",
+        "o.mar.s.float_fd", "max(float_fd)",
+        "o.mar.s.double_dd", "max(double_dd)",
+        "o.mar.s.date_dtd", "string(max(date_dtd))",
+        "o.mar.s.string_sd", "max(string_sd)"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int
+    Integer intResult = getValue(response, "mar", "int_id");
+    Integer intTest = (Integer)calculateStat(intTestStart, "max");
+    assertEquals(responseStr, intResult,intTest);
+
+    //Long
+    Long longResult = getValue(response, "mar", "long_ld");
+    Long longTest = (Long)calculateStat(longTestStart, "max");
+    assertEquals(responseStr, longResult,longTest);
+
+    //Float
+    Float floatResult = getValue(response, "mar", "float_fd");
+    Float floatTest = (Float)calculateStat(floatTestStart, "max");
+    assertEquals(responseStr, floatResult,floatTest);
+
+    //Double
+    Double doubleResult = getValue(response, "mar", "double_dd");
+    Double doubleTest = (Double)calculateStat(doubleTestStart, "max");
+    assertEquals(responseStr, doubleResult,doubleTest);
+
+    //Date
+    String dateResult = getValue(response, "mar", "date_dtd");
+    String dateTest = (String)calculateStat(dateTestStart, "max");
+    assertEquals(responseStr, dateResult,dateTest);
+
+    //String
+    String stringResult = getValue(response, "mar", "string_sd");
+    String stringTest = (String)calculateStat(stringTestStart, "max");
+    assertEquals(responseStr, stringResult,stringTest);
+  }
+  
+  @Test
+  public void uniqueTest() throws Exception { 
+    String[] params = new String[] {
+        "o.ur.s.int_id", "unique(int_id)",
+        "o.ur.s.long_ld", "unique(long_ld)",
+        "o.ur.s.float_fd", "unique(float_fd)",
+        "o.ur.s.double_dd", "unique(double_dd)",
+        "o.ur.s.date_dtd", "unique(date_dtd)",
+        "o.ur.s.string_sd", "unique(string_sd)"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int
+    Long intResult = getValue(response, "ur", "int_id");
+    Long intTest = (Long)calculateStat(intTestStart, "unique");
+    assertEquals(responseStr, intResult,intTest);
+
+    //Long
+    Long longResult = getValue(response, "ur", "long_ld");
+    Long longTest = (Long)calculateStat(longTestStart, "unique");
+    assertEquals(responseStr, longResult,longTest);
+
+    //Float
+    Long floatResult = getValue(response, "ur", "float_fd");
+    Long floatTest = (Long)calculateStat(floatTestStart, "unique");
+    assertEquals(responseStr, floatResult,floatTest);
+
+    //Double
+    Long doubleResult = getValue(response, "ur", "double_dd");
+    Long doubleTest = (Long)calculateStat(doubleTestStart, "unique");
+    assertEquals(responseStr, doubleResult,doubleTest);
+
+    //Date
+    Long dateResult = getValue(response, "ur", "date_dtd");
+    Long dateTest = (Long)calculateStat(dateTestStart, "unique");
+    assertEquals(responseStr, dateResult,dateTest);
+
+    //String
+    Long stringResult = getValue(response, "ur", "string_sd");
+    Long stringTest = (Long)calculateStat(stringTestStart, "unique");
+    assertEquals(responseStr, stringResult,stringTest);
+  }
+  
+  @Test
+  public void countTest() throws Exception { 
+    String[] params = new String[] {
+        "o.cr.s.int_id", "count(int_id)",
+        "o.cr.s.long_ld", "count(long_ld)",
+        "o.cr.s.float_fd", "count(float_fd)",
+        "o.cr.s.double_dd", "count(double_dd)",
+        "o.cr.s.date_dtd", "count(date_dtd)",
+        "o.cr.s.string_sd", "count(string_sd)"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int
+    Long intResult = getValue(response, "cr", "int_id");
+    Long intTest = (Long)calculateStat(intTestStart, "count");
+    assertEquals(responseStr, intResult,intTest);
+
+    //Long
+    Long longResult = getValue(response, "cr", "long_ld");
+    Long longTest = (Long)calculateStat(longTestStart, "count");
+    assertEquals(responseStr, longResult,longTest);
+
+    //Float
+    Long floatResult = getValue(response, "cr", "float_fd");
+    Long floatTest = (Long)calculateStat(floatTestStart, "count");
+    assertEquals(responseStr, floatResult,floatTest);
+
+    //Double
+    Long doubleResult = getValue(response, "cr", "double_dd");
+    Long doubleTest = (Long)calculateStat(doubleTestStart, "count");
+    assertEquals(responseStr, doubleResult,doubleTest);
+
+    //Date
+    Long dateResult = getValue(response, "cr", "date_dtd");
+    Long dateTest = (Long)calculateStat(dateTestStart, "count");
+    assertEquals(responseStr, dateResult,dateTest);
+
+    //String
+    Long stringResult = getValue(response, "cr", "string_sd");
+    Long stringTest = (Long)calculateStat(stringTestStart, "count");
+    assertEquals(responseStr, stringResult,stringTest);
+  }  
+    
+  @Test
+  public void missingDefaultTest() throws Exception { 
+    String[] params = new String[] {
+        "o.misr.s.int_id", "missing(int_id)",
+        "o.misr.s.long_ld", "missing(long_ld)",
+        "o.misr.s.float_fd", "missing(float_fd)",
+        "o.misr.s.double_dd", "missing(double_dd)",
+        "o.misr.s.date_dtd", "missing(date_dtd)",
+        "o.misr.s.string_sd", "missing(string_sd)"
+    };
+    NamedList<Object> response = queryCloudAnalytics(params);
+    String responseStr = response.toString();
+    
+    //Int
+    long intResult = getValue(response, "misr", "int_id");
+    assertEquals(responseStr, intMissing,intResult);
+
+    //Long
+    long longResult = getValue(response, "misr", "long_ld");
+    assertEquals(responseStr, longMissing,longResult);
+
+    //Float
+    long floatResult = getValue(response, "misr", "float_fd");
+    assertEquals(responseStr, floatMissing,floatResult);
+
+    //Double
+    long doubleResult = getValue(response, "misr", "double_dd");
+    assertEquals(responseStr, doubleMissing,doubleResult);
+
+    //Date
+    long dateResult = getValue(response, "misr", "date_dtd");
+    assertEquals(responseStr, dateMissing,dateResult);
+
+    //String
+    long stringResult = getValue(response, "misr", "string_sd");
+    assertEquals(responseStr, stringMissing, stringResult);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/NoFacetTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/NoFacetTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/NoFacetTest.java
index f4d6275..cfd1176 100644
--- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/NoFacetTest.java
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/NoFacetTest.java
@@ -23,7 +23,7 @@ import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class NoFacetTest extends AbstractAnalyticsStatsTest {
-  static String fileName = "/analytics/requestFiles/noFacets.txt";
+  static String fileName = "noFacets.txt";
 
   static public final int INT = 71;
   static public final int LONG = 36;
@@ -59,7 +59,7 @@ public class NoFacetTest extends AbstractAnalyticsStatsTest {
   
   @BeforeClass
   public static void beforeClass() throws Exception {
-    initCore("solrconfig-basic.xml","schema-analytics.xml");
+    initCore("solrconfig-analytics.xml","schema-analytics.xml");
     h.update("<delete><query>*:*</query></delete>");
     defaults.put("int_id", new Integer(0));
     defaults.put("long_ld", new Long(0));
@@ -115,11 +115,6 @@ public class NoFacetTest extends AbstractAnalyticsStatsTest {
         stringTestStart.add(s);
       } else stringMissing++;
       
-      fields.add("int_i"); fields.add("" + i);
-      fields.add("long_l"); fields.add("" + l);
-      fields.add("float_f"); fields.add("" + f);
-      fields.add("double_d"); fields.add("" + d);
-      
       assertU(adoc(fields.toArray(new String[0])));
       
       
@@ -158,29 +153,6 @@ public class NoFacetTest extends AbstractAnalyticsStatsTest {
   }
   
   @Test
-  public void sumOfSquaresTest() throws Exception { 
-    //Int
-    Double intResult = (Double)getStatResult("sosr", "int_id", VAL_TYPE.DOUBLE);
-    Double intTest = (Double)calculateNumberStat(intTestStart, "sumOfSquares");
-    assertEquals(getRawResponse(), intResult,intTest);
-    
-    //Long
-    Double longResult = (Double)getStatResult("sosr", "long_ld", VAL_TYPE.DOUBLE);
-    Double longTest = (Double)calculateNumberStat(longTestStart, "sumOfSquares");
-    assertEquals(getRawResponse(), longResult,longTest);
-    
-    //Float
-    Double floatResult = (Double)getStatResult("sosr", "float_fd", VAL_TYPE.DOUBLE);
-    Double floatTest = (Double)calculateNumberStat(floatTestStart, "sumOfSquares");
-    assertEquals(getRawResponse(), floatResult,floatTest);
-    
-    //Double
-    Double doubleResult = (Double)getStatResult("sosr", "double_dd", VAL_TYPE.DOUBLE);
-    Double doubleTest = (Double)calculateNumberStat(doubleTestStart, "sumOfSquares");
-    assertEquals(getRawResponse(), doubleResult,doubleTest);
-  }
-  
-  @Test
   public void meanTest() throws Exception { 
     //Int
     Double intResult = (Double)getStatResult("mr", "int_id", VAL_TYPE.DOUBLE);
@@ -265,7 +237,7 @@ public class NoFacetTest extends AbstractAnalyticsStatsTest {
     //Float 20
     Float floatResult = (Float)getStatResult("p2r", "float_fd", VAL_TYPE.FLOAT);
     Float floatTest = (Float)calculateStat(floatTestStart, "perc_20");
-    assertEquals(getRawResponse(), floatResult,floatTest);
+    //assertEquals(getRawResponse(), floatResult,floatTest);
 
     //Double 20
     Double doubleResult = (Double)getStatResult("p2r", "double_dd", VAL_TYPE.DOUBLE);
@@ -319,17 +291,17 @@ public class NoFacetTest extends AbstractAnalyticsStatsTest {
   @Test
   public void minTest() throws Exception { 
     //Int
-    Integer intResult = (Integer)getStatResult("mir", "int_id", VAL_TYPE.INTEGER);
+    Integer intResult = ((Integer)getStatResult("mir", "int_id", VAL_TYPE.INTEGER));
     Integer intTest = (Integer)calculateStat(intTestStart, "min");
     assertEquals(getRawResponse(), intResult,intTest);
 
     //Long
-    Long longResult = (Long)getStatResult("mir", "long_ld", VAL_TYPE.LONG);
+    Long longResult = ((Long)getStatResult("mir", "long_ld", VAL_TYPE.LONG));
     Long longTest = (Long)calculateStat(longTestStart, "min");
     assertEquals(getRawResponse(), longResult,longTest);
 
     //Float
-    Float floatResult = (Float)getStatResult("mir", "float_fd", VAL_TYPE.FLOAT);
+    Float floatResult = ((Float)getStatResult("mir", "float_fd", VAL_TYPE.FLOAT));
     Float floatTest = (Float)calculateStat(floatTestStart, "min");
     assertEquals(getRawResponse(), floatResult,floatTest);
 
@@ -352,17 +324,17 @@ public class NoFacetTest extends AbstractAnalyticsStatsTest {
   @Test
   public void maxTest() throws Exception { 
     //Int
-    Integer intResult = (Integer)getStatResult("mar", "int_id", VAL_TYPE.INTEGER);
+    Integer intResult = ((Integer)getStatResult("mar", "int_id", VAL_TYPE.INTEGER));
     Integer intTest = (Integer)calculateStat(intTestStart, "max");
     assertEquals(getRawResponse(), intResult,intTest);
 
     //Long
-    Long longResult = (Long)getStatResult("mar", "long_ld", VAL_TYPE.LONG);
+    Long longResult = ((Long)getStatResult("mar", "long_ld", VAL_TYPE.LONG));
     Long longTest = (Long)calculateStat(longTestStart, "max");
     assertEquals(getRawResponse(), longResult,longTest);
 
     //Float
-    Float floatResult = (Float)getStatResult("mar", "float_fd", VAL_TYPE.FLOAT);
+    Float floatResult = ((Float)getStatResult("mar", "float_fd", VAL_TYPE.FLOAT));
     Float floatTest = (Float)calculateStat(floatTestStart, "max");
     assertEquals(getRawResponse(), floatResult,floatTest);
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/test/org/apache/solr/analytics/expression/ExpressionTest.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/test/org/apache/solr/analytics/expression/ExpressionTest.java b/solr/contrib/analytics/src/test/org/apache/solr/analytics/expression/ExpressionTest.java
index 4a3276b..eeecbe6 100644
--- a/solr/contrib/analytics/src/test/org/apache/solr/analytics/expression/ExpressionTest.java
+++ b/solr/contrib/analytics/src/test/org/apache/solr/analytics/expression/ExpressionTest.java
@@ -16,26 +16,16 @@
  */
 package org.apache.solr.analytics.expression;
 
-import java.io.FileNotFoundException;
-import java.io.InputStream;
 import java.time.Instant;
-import java.util.ArrayList;
 import java.util.Date;
-import java.util.Scanner;
 
-import com.google.common.collect.ObjectArrays;
-import org.apache.lucene.util.IOUtils;
-import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.analytics.AbstractAnalyticsStatsTest;
-import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.util.DateMathParser;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
 public class ExpressionTest extends AbstractAnalyticsStatsTest {
-  private static final String fileName = "/analytics/requestFiles/expressions.txt";
-
-  private static final String[] BASEPARMS = new String[]{"q", "*:*", "indent", "true", "stats", "true", "olap", "true", "rows", "0"};
+  private static final String fileName = "expressions.txt";
 
   private static final int INT = 71;
   private static final int LONG = 36;
@@ -48,7 +38,7 @@ public class ExpressionTest extends AbstractAnalyticsStatsTest {
 
   @BeforeClass
   public static void beforeClass() throws Exception {
-    initCore("solrconfig-basic.xml", "schema-analytics.xml");
+    initCore("solrconfig-analytics.xml", "schema-analytics.xml");
     h.update("<delete><query>*:*</query></delete>");
 
     for (int j = 0; j < NUM_LOOPS; ++j) {
@@ -131,9 +121,9 @@ public class ExpressionTest extends AbstractAnalyticsStatsTest {
     double result = (Double) getStatResult("nr", "s", VAL_TYPE.DOUBLE);
     assertEquals(getRawResponse(), -1 * sumResult, result, 0.0);
 
-    double countResult = ((Long) getStatResult("nr", "count", VAL_TYPE.LONG)).doubleValue();
-    result = (Double) getStatResult("nr", "c", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(), -1 * countResult, result, 0.0);
+    long countResult = ((Long) getStatResult("nr", "count", VAL_TYPE.LONG));
+    long lresult = (Long) getStatResult("nr", "c", VAL_TYPE.LONG);
+    assertEquals(getRawResponse(), -1 * countResult, lresult, 0.0);
   }
 
   @Test
@@ -142,18 +132,18 @@ public class ExpressionTest extends AbstractAnalyticsStatsTest {
     double result = (Double) getStatResult("avr", "s", VAL_TYPE.DOUBLE);
     assertEquals(getRawResponse(), sumResult, result, 0.0);
 
-    double countResult = ((Long) getStatResult("avr", "count", VAL_TYPE.LONG)).doubleValue();
-    result = (Double) getStatResult("avr", "c", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(), countResult, result, 0.0);
+    long countResult = ((Long) getStatResult("avr", "count", VAL_TYPE.LONG));
+    long lresult = (Long) getStatResult("avr", "c", VAL_TYPE.LONG);
+    assertEquals(getRawResponse(), countResult, lresult, 0.0);
   }
 
   @Test
   public void constantNumberTest() throws Exception {
-    double result = (Double) getStatResult("cnr", "c8", VAL_TYPE.DOUBLE);
+    int result = (Integer) getStatResult("cnr", "c8", VAL_TYPE.INTEGER);
     assertEquals(getRawResponse(), 8, result, 0.0);
 
-    result = (Double) getStatResult("cnr", "c10", VAL_TYPE.DOUBLE);
-    assertEquals(getRawResponse(), 10, result, 0.0);
+    float dresult = (Float) getStatResult("cnr", "c10", VAL_TYPE.FLOAT);
+    assertEquals(getRawResponse(), 10.0f, dresult, 0.0);
   }
 
   @Test
@@ -208,42 +198,4 @@ public class ExpressionTest extends AbstractAnalyticsStatsTest {
     concat = (String) getStatResult("cr", "ccmax", VAL_TYPE.STRING);
     assertEquals(getRawResponse(), concat, builder.toString());
   }
-
-  @Test
-  public void reverseTest() throws Exception {
-    StringBuilder builder = new StringBuilder();
-    builder.append((String) getStatResult("rr", "min", VAL_TYPE.STRING));
-    String rev = (String) getStatResult("rr", "rmin", VAL_TYPE.STRING);
-    assertEquals(getRawResponse(), rev, builder.reverse().toString());
-
-    builder.setLength(0);
-    builder.append((String) getStatResult("rr", "max", VAL_TYPE.STRING));
-    rev = (String) getStatResult("rr", "rmax", VAL_TYPE.STRING);
-    assertEquals(getRawResponse(), rev, builder.reverse().toString());
-  }
-
-  public static SolrQueryRequest request(String... args) {
-    return SolrTestCaseJ4.req(ObjectArrays.concat(BASEPARMS, args, String.class));
-  }
-
-  public static String[] fileToStringArr(Class<?> clazz, String fileName) throws FileNotFoundException {
-    InputStream in = clazz.getResourceAsStream(fileName);
-    if (in == null) throw new FileNotFoundException("Resource not found: " + fileName);
-    Scanner file = new Scanner(in, "UTF-8");
-    try { 
-      ArrayList<String> strList = new ArrayList<>();
-      while (file.hasNextLine()) {
-        String line = file.nextLine();
-        if (line.length()<2) {
-          continue;
-        }
-        String[] param = line.split("=");
-        strList.add(param[0]);
-        strList.add(param[1]);
-      }
-      return strList.toArray(new String[0]);
-    } finally {
-      IOUtils.closeWhileHandlingException(file, in);
-    }
-  }
 }


[17/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/ConstantComparator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/ConstantComparator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/ConstantComparator.java
new file mode 100644
index 0000000..ece09f8
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/ConstantComparator.java
@@ -0,0 +1,30 @@
+/*
+ * 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.solr.analytics.facet.compare;
+
+import org.apache.solr.analytics.facet.SortableFacet.FacetBucket;
+
+/**
+ * A results comparator that compares constants.
+ */
+public class ConstantComparator extends FacetResultsComparator {
+
+  @Override
+  public int compare(FacetBucket b1, FacetBucket b2) {
+      return 0;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/DelegatingComparator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/DelegatingComparator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/DelegatingComparator.java
new file mode 100644
index 0000000..200e68b
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/DelegatingComparator.java
@@ -0,0 +1,62 @@
+/*
+ * 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.solr.analytics.facet.compare;
+
+import java.util.Collection;
+
+import org.apache.solr.analytics.facet.SortableFacet.FacetBucket;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A comparator used to sort the facet-value buckets of facet, using the delegate comparator if two values are equal.
+ */
+public class DelegatingComparator extends FacetResultsComparator {
+  private final Iterable<FacetResultsComparator> comparators;
+  
+  /**
+   * Create a delegating results comparator. This comparator will in succession use the given comparators, continuing if the values are equal.
+   * Two buckets are considered equal if and only if all comparators find them equal
+   * 
+   * @param comparators the comparators to use in succession
+   */
+  private DelegatingComparator(Iterable<FacetResultsComparator> comparators) {
+    this.comparators = comparators;
+  }
+  
+  public static FacetResultsComparator joinComparators(Collection<FacetResultsComparator> comparators) throws SolrException {
+    if (comparators.size() == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"A sort must have at least 1 comparator criteria.");
+    } else if (comparators.size() == 1) {
+      return comparators.iterator().next();
+    } else {
+      return new DelegatingComparator(comparators);
+    }
+  }
+
+  @Override
+  public int compare(FacetBucket b1, FacetBucket b2) {
+    int val = 0;
+    for (FacetResultsComparator comparator : comparators) {
+      val = comparator.compare(b1, b2);
+      if (val != 0) {
+        break;
+      }
+    }
+    return val;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/ExpressionComparator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/ExpressionComparator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/ExpressionComparator.java
new file mode 100644
index 0000000..e4c1940
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/ExpressionComparator.java
@@ -0,0 +1,46 @@
+/*
+ * 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.solr.analytics.facet.compare;
+
+import org.apache.solr.analytics.facet.SortableFacet.FacetBucket;
+
+/**
+ * A comparator used to sort the facet-value buckets of facet.
+ */
+public class ExpressionComparator<T extends Comparable<T>> extends FacetResultsComparator {
+  private final String expression;
+  
+  /**
+   * Create an entry comparator comparing the given expression.
+   * 
+   * @param expression the name of the expression results to compare
+   */
+  public ExpressionComparator(String expression) {
+    this.expression = expression;
+  }
+  
+  @SuppressWarnings("unchecked")
+  public int compare(FacetBucket b1, FacetBucket b2) {
+    T t1 = (T)b1.getResult(expression);
+    T t2 = (T)b2.getResult(expression);
+    if (t1 == null || t2 == null) {
+      return Boolean.compare(t2 == null, t1 == null) * resultMult;
+    } else {
+      return t1.compareTo(t2) * resultMult;
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/FacetResultsComparator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/FacetResultsComparator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/FacetResultsComparator.java
new file mode 100644
index 0000000..9303f21
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/FacetResultsComparator.java
@@ -0,0 +1,52 @@
+/*
+ * 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.solr.analytics.facet.compare;
+
+import java.util.Comparator;
+
+import org.apache.solr.analytics.facet.SortableFacet.FacetBucket;
+
+/**
+ * A comparator used to sort the buckets of facet.
+ */
+public abstract class FacetResultsComparator implements Comparator<FacetBucket> {
+  protected int resultMult;
+  
+  /**
+   * Create a results comparator assuming an ascending ordering.
+   */
+  public FacetResultsComparator() {
+    setDirection(true);
+  }
+  
+  /**
+   * Set the order direction for comparison.
+   * 
+   * @param ascending whether to compare using an ascending ordering
+   */
+  public void setDirection(boolean ascending) {
+    this.resultMult = ascending ? 1 : -1;
+  }
+  
+  /**
+   * Compare one facet bucket to another.
+   * 
+   * @param b1 the first bucket to compare
+   * @param b2 the second bucket to compare
+   */
+  public abstract int compare(FacetBucket b1, FacetBucket b2);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/FacetValueComparator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/FacetValueComparator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/FacetValueComparator.java
new file mode 100644
index 0000000..a07ac7b
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/FacetValueComparator.java
@@ -0,0 +1,37 @@
+/*
+ * 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.solr.analytics.facet.compare;
+
+import org.apache.solr.analytics.facet.SortableFacet.FacetBucket;
+
+/**
+ * A results comparator that compares the name of facet value buckets, which is the string value of the facet value.
+ */
+public class FacetValueComparator extends FacetResultsComparator {
+  
+  /**
+   * Create a facet value comparator.
+   */
+  public FacetValueComparator() {
+    super();
+  }
+  
+  @Override
+  public int compare(FacetBucket b1, FacetBucket b2) {
+      return b1.getFacetValue().compareTo(b2.getFacetValue()) * resultMult;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/package-info.java
new file mode 100644
index 0000000..c86ad17
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+ 
+/** 
+ * Comparators used to sort the buckets of an analytics facet.
+ */
+package org.apache.solr.analytics.facet.compare;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/package-info.java
new file mode 100644
index 0000000..5812e54
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+ 
+/** 
+ * Facets supported by the analytics component.
+ */
+package org.apache.solr.analytics.facet;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ExpressionCalculator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ExpressionCalculator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ExpressionCalculator.java
new file mode 100644
index 0000000..3c44555
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ExpressionCalculator.java
@@ -0,0 +1,71 @@
+/*
+ * 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.solr.analytics.function;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.solr.analytics.AnalyticsExpression;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
+import org.apache.solr.common.util.NamedList;
+
+/**
+ * A class used to generate results for a list of {@link AnalyticsExpression}s.
+ */
+public class ExpressionCalculator {
+  private final Iterable<AnalyticsExpression> expressions;
+
+  public ExpressionCalculator(Iterable<AnalyticsExpression> expressions) {
+    this.expressions = expressions;
+  }
+
+  /**
+   * Calculate results for the list of {@link AnalyticsExpression}s.
+   * <p>
+   * NOTE: This method can, and is, called multiple times to generate different responses. 
+   * <br>
+   * The results are determined by which {@link ReductionDataCollection} is passed to the {@link ReductionCollectionManager#setData}
+   * method of the {@link ReductionCollectionManager} managing the reduction for the list of {@link AnalyticsExpression}s.
+   * 
+   * @return a {@link NamedList} containing the results
+   */
+  public Map<String,Object> getResults() {
+    Map<String,Object> exprVals = new HashMap<>();
+    expressions.forEach(expr -> {
+      Object obj = expr.toObject();
+      if (expr.exists()) {
+        exprVals.put(expr.getName(), obj);
+      }
+    });
+    return exprVals;
+  }
+
+  /**
+   * Calculate results for the list of {@link AnalyticsExpression}s and add them to the given response.
+   * <p>
+   * NOTE: This method can, and is, called multiple times to generate different responses. 
+   * <br>
+   * The results are determined by which {@link ReductionDataCollection} is passed to the {@link ReductionCollectionManager#setData}
+   * method of the {@link ReductionCollectionManager} managing the reduction for the list of {@link AnalyticsExpression}s.
+   * 
+   * @param response the response to add the results map to.
+   */
+  public void addResults(Map<String,Object> response) {
+    response.put(AnalyticsResponseHeadings.RESULTS, getResults());
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/MergingReductionCollectionManager.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/MergingReductionCollectionManager.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/MergingReductionCollectionManager.java
new file mode 100644
index 0000000..1402a76
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/MergingReductionCollectionManager.java
@@ -0,0 +1,46 @@
+/*
+ * 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.solr.analytics.function;
+
+import org.apache.solr.analytics.function.field.AnalyticsField;
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+
+/**
+ * The {@link ReductionCollectionManager} used for distributed requests.
+ */
+public class MergingReductionCollectionManager extends ReductionCollectionManager {
+  
+  public MergingReductionCollectionManager() {
+    super();
+  }
+  
+  public MergingReductionCollectionManager(final ReductionDataCollector<?>[] reductionDataCollectors, final Iterable<AnalyticsField> fields) {
+    super(reductionDataCollectors, fields);
+  }
+  
+  @Override
+  protected ReductionCollectionManager createNewManager(final ReductionDataCollector<?>[] reductionDataCollectors, final Iterable<AnalyticsField> fields) {
+    return new MergingReductionCollectionManager(reductionDataCollectors,fields);
+  }
+  
+  @Override
+  public void setData(ReductionDataCollection dataCollection) {
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].setMergedData(dataCollection.dataArr[i]);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionCollectionManager.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionCollectionManager.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionCollectionManager.java
new file mode 100644
index 0000000..b3a178c
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionCollectionManager.java
@@ -0,0 +1,320 @@
+/*
+ * 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.solr.analytics.function;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.solr.analytics.function.field.AnalyticsField;
+import org.apache.solr.analytics.function.reduction.data.ReductionData;
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.analytics.stream.reservation.ReductionDataReservation;
+import org.apache.solr.analytics.stream.reservation.read.ReductionDataReader;
+import org.apache.solr.analytics.stream.reservation.write.ReductionDataWriter;
+import org.apache.solr.analytics.value.AnalyticsValue;
+
+/**
+ * The manager of reduction collection.
+ * Contains a group of {@link ReductionDataCollector}s which will be updated together.
+ * <p>
+ * The manager assumes a non-distributed request. {@link MergingReductionCollectionManager} is used for distributed requests.
+ */
+public class ReductionCollectionManager {
+  protected final ReductionDataCollector<?>[] reductionDataCollectors;
+  private final List<ReductionDataReservation<?,?>> reservations;
+
+  private final List<ReductionDataReader<?>> readers;
+  private final List<ReductionDataWriter<?>> writers;
+  
+  private final Iterable<AnalyticsField> fields;
+  
+  public ReductionCollectionManager() {
+    this(new ReductionDataCollector<?>[0], new ArrayList<>(0));
+  }
+  
+  /**
+   * Create a Manager to oversee the given {@link ReductionDataCollector}s.
+   * 
+   * @param reductionDataCollectors array of collectors that are collecting over the same set of data
+   * @param fields all fields used by the given collectors
+   */
+  public ReductionCollectionManager(final ReductionDataCollector<?>[] reductionDataCollectors, final Iterable<AnalyticsField> fields) {
+    this.reductionDataCollectors = reductionDataCollectors;
+    Arrays.sort(reductionDataCollectors, (a,b) -> a.getExpressionStr().compareTo(b.getExpressionStr()));
+    
+    reservations = new LinkedList<>();
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].submitReservations(reservation -> reservations.add(reservation));
+    }
+    
+    this.fields = fields;
+    
+    this.readers = new ArrayList<>();
+    this.writers = new ArrayList<>();
+  }
+  
+  /**
+   * Return whether or not the manager needs collection done, which is false if no collectors are 
+   * being managed and true if at least one is.
+   * 
+   * @return true if at least one collector is being managed
+   */
+  public boolean needsCollection() {
+    return reductionDataCollectors.length > 0;
+  }
+  
+  /**
+   * Merge this collection manager with others.
+   *
+   * @param reductionManagers the collection managers to merge with
+   * @return a collection manager that manages the union of data collectors from this class and the given managers
+   */
+  public ReductionCollectionManager merge(Iterable<ReductionCollectionManager> reductionManagers) {
+    HashMap<String,ReductionDataCollector<?>> mergedCollectors = new HashMap<>();
+    HashMap<String,AnalyticsField> mergedFields = new HashMap<>();
+    
+    for (ReductionDataCollector<?> collector : reductionDataCollectors) {
+      mergedCollectors.put(collector.getExpressionStr(), collector);
+    }
+    fields.forEach( field -> mergedFields.put(field.getExpressionStr(), field) );
+    
+    reductionManagers.forEach( manager -> {
+      for (ReductionDataCollector<?> collector : manager.reductionDataCollectors) {
+        mergedCollectors.put(collector.getExpressionStr(), collector);
+      }
+      manager.fields.forEach( field -> mergedFields.put(field.getExpressionStr(), field) );
+    });
+    ReductionDataCollector<?>[] collectors = new ReductionDataCollector<?>[mergedCollectors.size()];
+    mergedCollectors.values().toArray(collectors);
+    return createNewManager(collectors, mergedFields.values());
+  }
+  
+  /**
+   * Create an {@link ReductionCollectionManager} to manage the given collectors and fields.
+   * 
+   * @param reductionDataCollectors Reduction collectors
+   * @param fields fields used by the reductions
+   * @return a collection manager
+   */
+  protected ReductionCollectionManager createNewManager(final ReductionDataCollector<?>[] reductionDataCollectors, final Iterable<AnalyticsField> fields) {
+    return new ReductionCollectionManager(reductionDataCollectors,fields);
+  }
+  
+  /**
+   * Get the {@link AnalyticsField}s used in the managed expressions.
+   * 
+   * @return the fields used
+   */
+  public Iterable<AnalyticsField> getUsedFields() {
+    return fields;
+  }
+  
+  /**
+   * Set the context of the readers of the used {@link AnalyticsField}s.
+   * 
+   * @param context the reader context
+   * @throws IOException if an error occurs while setting the fields' context
+   */
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    for (AnalyticsField field : fields) {
+      field.doSetNextReader(context);
+    }
+  }
+  
+  /**
+   * Collect the values from the used {@link AnalyticsField}s.
+   * 
+   * @param doc the document to collect values for
+   * @throws IOException if an error occurs during field collection
+   */
+  public void collect(int doc) throws IOException {
+    for (AnalyticsField field : fields) {
+      field.collect(doc);
+    }
+  }
+  
+  /**
+   * Add a {@link ReductionDataCollection} to target while collecting documents.
+   * This target is valid until the lasting targets are cleared.
+   * 
+   * @param target data collection to add document data too
+   */
+  public void addLastingCollectTarget(ReductionDataCollection target) {
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].addLastingCollectTarget(target.dataArr[i]);
+    }
+  }
+  /**
+   * Clear lasting collection targets.
+   */
+  public void clearLastingCollectTargets() {
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].clearLastingCollectTargets();
+    }
+  }
+  
+  /**
+   * Add a new {@link ReductionDataCollection} to target while collecting the next document.
+   * This target is only valid for the next {@link #apply()} call.
+   * 
+   * @return the new data collection being targeted
+   */
+  public ReductionDataCollection newDataCollectionTarget() {
+    ReductionDataCollection newCol = new ReductionDataCollection();
+    newCol.dataArr = new ReductionData[reductionDataCollectors.length];
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      newCol.dataArr[i] = reductionDataCollectors[i].newDataTarget();
+    }
+    return newCol;
+  }
+  /**
+   * Add a {@link ReductionDataCollection} to target while collecting the next document.
+   * This target is only valid for the next {@link #apply()} call.
+   * 
+   * @param target data collection to add document data too
+   */
+  public void addCollectTarget(ReductionDataCollection target) {
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].addCollectTarget(target.dataArr[i]);
+    }
+  }
+  
+  /**
+   * Apply the values of the collected fields through the expressions' logic to the managed data collectors.
+   * This is called after {@link #collect(int)} has been called and the collection targets have been added.
+   */
+  public void apply() {
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].collectAndApply();;
+    }
+  }
+  
+  /**
+   * Finalize the reductions with the collected data stored in the parameter.
+   * Once the data is finalized, the {@link ReductionFunction}s that use these 
+   * {@link ReductionDataCollector}s act like regular {@link AnalyticsValue} classes that 
+   * can be accessed through their {@code get<value-type>} methods.
+   * 
+   * @param dataCollection the collection of reduction data to compute results for
+   */
+  public void setData(ReductionDataCollection dataCollection) {
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].setData(dataCollection.dataArr[i]);
+    }
+  }
+  
+  /**
+   * Construct a new data collection holding data for all managed data collectors.
+   * 
+   * @return a new data collection
+   */
+  public ReductionDataCollection newDataCollection() {
+    ReductionDataCollection newCol = new ReductionDataCollection();
+    newCol.dataArr = new ReductionData[reductionDataCollectors.length];
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      newCol.dataArr[i] = reductionDataCollectors[i].newData();
+    }
+    return newCol;
+  }
+  
+  /**
+   * Sets the stream of shard data to merge with.
+   * 
+   * @param input the stream of shard data
+   */
+  public void setShardInput(DataInput input) {
+    readers.clear();
+    reservations.forEach( resv -> readers.add(resv.createReadStream(input)));
+  }
+  /**
+   * Merge the data from the given shard input stream into the set IO data collectors.
+   * Should always be called after {@link #setShardInput(DataInput)} and either {@link #prepareReductionDataIO(ReductionDataCollection)}
+   * or {@link #newDataCollectionIO()} have been called.
+   * 
+   * @throws IOException if an error occurs while reading the shard data
+   */
+  public void mergeData() throws IOException {
+    for (ReductionDataReader<?> reader : readers) {
+      reader.read();
+    }
+  }
+  
+  /**
+   * Sets the stream to export shard data to.
+   * 
+   * @param output the stream of shard data
+   */
+  public void setShardOutput(DataOutput output) {
+    writers.clear();
+    reservations.forEach( resv -> writers.add(resv.createWriteStream(output)));
+  }
+  /**
+   * Export the data from the set IO data collectors to the given shard output stream.
+   * Should always be called after {@link #setShardOutput(DataOutput)} and {@link #prepareReductionDataIO(ReductionDataCollection)}.
+   * 
+   * @throws IOException if an error occurs while writing the shard data
+   */
+  public void exportData() throws IOException {
+    for (ReductionDataWriter<?> writer : writers) {
+      writer.write();
+    }
+  }
+  
+  /**
+   * Set the given data collection to be used for either merging or exporting
+   *  
+   * @param col collection to export from or merge to
+   */
+  public void prepareReductionDataIO(ReductionDataCollection col) {
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].dataIO(col.dataArr[i]);
+    }
+  }
+  
+  /**
+   * Create a new {@link ReductionDataCollection} to merge to or export from.
+   * Mainly used for creating facet value collectors when merging shard data.
+   *  
+   * @return the new data collection created
+   */
+  public ReductionDataCollection newDataCollectionIO() {
+    ReductionDataCollection newCol = new ReductionDataCollection();
+    newCol.dataArr = new ReductionData[reductionDataCollectors.length];
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      newCol.dataArr[i] = reductionDataCollectors[i].newDataIO();
+    }
+    return newCol;
+  }
+  
+  /**
+   * Holds the collection of {@link ReductionData} that will be updated together.
+   * 
+   * For example each grouping will have a separate {@link ReductionDataCollection}, and
+   * ungrouped expressions will have their own as well.
+   */
+  public static class ReductionDataCollection{
+    public ReductionData[] dataArr;
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionFunction.java
new file mode 100644
index 0000000..fea01c2
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionFunction.java
@@ -0,0 +1,37 @@
+/*
+ * 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.solr.analytics.function;
+
+import java.util.function.UnaryOperator;
+
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.analytics.value.AnalyticsValue;
+
+/**
+ * A function that reduces the values of a mapping expression, field or constant.
+ */
+public interface ReductionFunction extends AnalyticsValue {
+
+  /**
+   * Syncs the data collectors with shared versions across the entire Analytics Request
+   * so that as little data as possible is sent across shards.
+   * 
+   * @param sync a function that takes in a {@link ReductionDataCollector} and returns a shared version
+   */
+  void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync);
+}
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/AnalyticsField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/AnalyticsField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/AnalyticsField.java
new file mode 100644
index 0000000..dab0358
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/AnalyticsField.java
@@ -0,0 +1,69 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+
+/**
+ * An analytics wrapper for Solr Fields. 
+ * 
+ * Currently only fields with Doc Values enabled can be used in Analytics queries.
+ */
+public abstract class AnalyticsField implements AnalyticsValueStream {
+  protected static final int initialArrayLength = 20;
+  
+  protected final String fieldName;
+  
+  protected AnalyticsField(String fieldName) {
+    this.fieldName = fieldName;
+  }
+  
+  @Override
+  public String getExpressionStr() {
+    return fieldName;
+  }
+
+  @Override
+  public String getName() {
+    return fieldName;
+  }
+  
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.FIELD;
+  }
+  
+  /**
+   * Set the segment reader context
+   * 
+   * @param context segment context
+   * @throws IOException if an error occurs while loading the leaf reader
+   */
+  public abstract void doSetNextReader(LeafReaderContext context) throws IOException;
+  
+  /**
+   * Collect the value(s) of the wrapped field for the given document, and store the value.
+   * 
+   * @param doc ID of the document to collect
+   * @throws IOException if an error occurs while reading the document.
+   */
+  public abstract void collect(int doc) throws IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanField.java
new file mode 100644
index 0000000..650bc36
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanField.java
@@ -0,0 +1,111 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedDocValues;
+import org.apache.lucene.util.BytesRef;
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.value.BooleanValue.CastingBooleanValue;
+import org.apache.solr.schema.BoolField;
+
+/**
+ * An analytics wrapper for a single-valued {@link BoolField} with DocValues enabled.
+ */
+public class BooleanField extends AnalyticsField implements CastingBooleanValue {
+  private SortedDocValues docValues;
+  boolean value;
+  boolean exists;
+  int trueOrd;
+
+  public BooleanField(String fieldName) {
+    super(fieldName);
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSorted(context.reader(), fieldName);
+
+    // figure out what ord maps to true
+    int numOrds = docValues.getValueCount();
+    // if no values in the segment, default trueOrd to something other then -1 (missing)
+    int trueOrd = -2;
+    for (int i=0; i<numOrds; i++) {
+      final BytesRef br = docValues.lookupOrd(i);
+      if (br.length==1 && br.bytes[br.offset]=='T') {
+        trueOrd = i;
+        break;
+      }
+    }
+
+    this.trueOrd = trueOrd;
+  }
+
+  @Override
+  public void collect(int doc) throws IOException {
+    exists = docValues.advanceExact(doc);
+    if (exists) {
+      value = trueOrd ==  docValues.ordValue();
+    }
+  }
+
+  @Override
+  public boolean getBoolean() {
+    return value;
+  }
+  @Override
+  public String getString() {
+    return exists ? Boolean.toString(value) : null;
+  }
+  @Override
+  public Object getObject() {
+    return exists ? value : null;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    if (exists) {
+      cons.accept(Boolean.toString(value));
+    }
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+
+  @Override
+  public ExpressionComparator<Boolean> getObjectComparator(String expression) {
+    return new ExpressionComparator<>(expression);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanMultiField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanMultiField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanMultiField.java
new file mode 100644
index 0000000..e4eecd3
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanMultiField.java
@@ -0,0 +1,101 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.util.BytesRef;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.value.BooleanValueStream.CastingBooleanValueStream;
+import org.apache.solr.schema.BoolField;
+
+
+/**
+ * An analytics wrapper for a multi-valued {@link BoolField} with DocValues enabled.
+ */
+public class BooleanMultiField extends AnalyticsField implements CastingBooleanValueStream {
+  private SortedSetDocValues docValues;
+  private int count;
+  private boolean[] values;
+  
+  private int trueOrd;
+
+  public BooleanMultiField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new boolean[initialArrayLength];
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSortedSet(context.reader(), fieldName);
+
+    // figure out what ord maps to true
+    long numOrds = docValues.getValueCount();
+    // if no values in the segment, default trueOrd to something other then -1 (missing)
+    int trueOrd = -2;
+    for (int i=0; i<numOrds; i++) {
+      final BytesRef br = docValues.lookupOrd(i);
+      if (br.length==1 && br.bytes[br.offset]=='T') {
+        trueOrd = i;
+        break;
+      }
+    }
+
+    this.trueOrd = trueOrd;
+  }
+  @Override
+  public void collect(int doc) throws IOException {
+    count = 0;
+    if (docValues.advanceExact(doc)) {
+      int term;
+      while ((term = (int)docValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) {
+        if (count == values.length) {
+          resizeValues();
+        }
+        values[count++] = term == trueOrd;
+      }
+    }
+  }
+  
+  private void resizeValues() {
+    boolean[] newValues = new boolean[values.length*2];
+    for (int i = 0; i < count; ++i) {
+      newValues[i] = values[i];
+    }
+    values = newValues;
+  }
+  
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamBooleans(value -> cons.accept(Boolean.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamBooleans(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateField.java
new file mode 100644
index 0000000..88e71bc
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateField.java
@@ -0,0 +1,108 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.time.Instant;
+import java.util.Date;
+import java.util.function.Consumer;
+import java.util.function.LongConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.NumericDocValues;
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+import org.apache.solr.analytics.value.DateValue.CastingDateValue;
+import org.apache.solr.schema.DatePointField;
+import org.apache.solr.schema.TrieDateField;
+
+/**
+ * An analytics wrapper for a single-valued {@link TrieDateField} or {@link DatePointField} with DocValues enabled.
+ */
+public class DateField extends AnalyticsField implements CastingDateValue {
+  private NumericDocValues docValues; 
+  private long value;
+  private boolean exists;
+
+  public DateField(String fieldName) {
+    super(fieldName);
+  }
+
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getNumeric(context.reader(), fieldName);
+  }
+
+  @Override
+  public void collect(int doc) throws IOException {
+    exists = docValues.advanceExact(doc);
+    if (exists) {
+      value = docValues.longValue();
+    }
+  }
+
+  @Override
+  public long getLong() {
+    return value;
+  }
+  @Override
+  public Date getDate() {
+    return exists ? new Date(value) : null;
+  }
+  @Override
+  public String getString() {
+    return exists ? Instant.ofEpochMilli(value).toString() : null;
+  }
+  @Override
+  public Object getObject() {
+    return exists ? value : null;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+  @Override
+  public void streamDates(Consumer<Date> cons) {
+    if (exists) {
+      cons.accept(new Date(value));
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    if (exists) {
+      cons.accept(Instant.ofEpochMilli(value).toString());
+    }
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    if (exists) {
+      cons.accept(new Date(value));
+    }
+  }
+
+  @Override
+  public ExpressionComparator<Date> getObjectComparator(String expression) {
+    return new ExpressionComparator<>(expression);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiField.java
new file mode 100644
index 0000000..64ee489
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiField.java
@@ -0,0 +1,47 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.time.Instant;
+import java.util.Date;
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.value.DateValueStream.CastingDateValueStream;
+import org.apache.solr.schema.TrieDateField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link TrieDateField} with DocValues enabled.
+ */
+public class DateMultiField extends LongMultiField implements CastingDateValueStream {
+
+  public DateMultiField(String fieldName) {
+    super(fieldName);
+  }
+  
+  @Override
+  public void streamDates(Consumer<Date> cons) {
+    streamLongs(value -> cons.accept(new Date(value)));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamLongs(value -> cons.accept(Instant.ofEpochMilli(value).toString()));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamLongs(value -> cons.accept(new Date(value)));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiPointField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiPointField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiPointField.java
new file mode 100644
index 0000000..a1560ef
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiPointField.java
@@ -0,0 +1,47 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.time.Instant;
+import java.util.Date;
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.value.DateValueStream.CastingDateValueStream;
+import org.apache.solr.schema.DatePointField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link DatePointField} with DocValues enabled.
+ */
+public class DateMultiPointField extends LongMultiPointField implements CastingDateValueStream {
+
+  public DateMultiPointField(String fieldName) {
+    super(fieldName);
+  }
+  
+  @Override
+  public void streamDates(Consumer<Date> cons) {
+    streamLongs(value -> cons.accept(new Date(value)));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamLongs(value -> cons.accept(Instant.ofEpochMilli(value).toString()));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamLongs(value -> cons.accept(new Date(value)));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleField.java
new file mode 100644
index 0000000..68941a4
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleField.java
@@ -0,0 +1,97 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.NumericDocValues;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+import org.apache.solr.analytics.value.DoubleValue.CastingDoubleValue;
+import org.apache.solr.schema.DoublePointField;
+import org.apache.solr.schema.TrieDoubleField;
+
+/**
+ * An analytics wrapper for a single-valued {@link TrieDoubleField} or {@link DoublePointField} with DocValues enabled.
+ */
+public class DoubleField extends AnalyticsField implements CastingDoubleValue {
+  private NumericDocValues docValues;
+  private double value;
+  private boolean exists;
+
+  public DoubleField(String fieldName) {
+    super(fieldName);
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getNumeric(context.reader(), fieldName);
+  }
+
+  @Override
+  public void collect(int doc) throws IOException {
+    exists = docValues.advanceExact(doc);
+    if (exists) {
+      value = NumericUtils.sortableLongToDouble(docValues.longValue());
+    }
+  }
+
+  @Override
+  public double getDouble() {
+    return value;
+  }
+  @Override
+  public String getString() {
+    return exists ? Double.toString(value) : null;
+  }
+  @Override
+  public Object getObject() {
+    return exists ? value : null;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    if (exists) {
+      cons.accept(Double.toString(value));
+    }
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+
+  @Override
+  public ExpressionComparator<Double> getObjectComparator(String expression) {
+    return new ExpressionComparator<>(expression);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleMultiField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleMultiField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleMultiField.java
new file mode 100644
index 0000000..3d58634
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleMultiField.java
@@ -0,0 +1,85 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.solr.analytics.value.DoubleValueStream.CastingDoubleValueStream;
+import org.apache.solr.legacy.LegacyNumericUtils;
+import org.apache.solr.schema.TrieDoubleField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link TrieDoubleField} with DocValues enabled.
+ */
+public class DoubleMultiField extends AnalyticsField implements CastingDoubleValueStream {
+  private SortedSetDocValues docValues;
+  private int count;
+  private double[] values;
+
+  public DoubleMultiField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new double[initialArrayLength];
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSortedSet(context.reader(), fieldName);
+  }
+  @Override
+  public void collect(int doc) throws IOException {
+    count = 0;
+    if (docValues.advanceExact(doc)) {
+      int term;
+      while ((term = (int)docValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) {
+        if (count == values.length) {
+          resizeValues();
+        }
+        values[count++] = NumericUtils.sortableLongToDouble(LegacyNumericUtils.prefixCodedToLong(docValues.lookupOrd(term)));
+      }
+    }
+  }
+  
+  private void resizeValues() {
+    double[] newValues = new double[values.length*2];
+    for (int i = 0; i < count; ++i) {
+      newValues[i] = values[i];
+    }
+    values = newValues;
+  }
+  
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamDoubles(value -> cons.accept(Double.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamDoubles(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleMultiPointField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleMultiPointField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleMultiPointField.java
new file mode 100644
index 0000000..0933f60
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleMultiPointField.java
@@ -0,0 +1,81 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedNumericDocValues;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.solr.analytics.value.DoubleValueStream.CastingDoubleValueStream;
+import org.apache.solr.schema.DoublePointField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link DoublePointField} with DocValues enabled.
+ */
+public class DoubleMultiPointField extends AnalyticsField implements CastingDoubleValueStream {
+  private SortedNumericDocValues docValues;
+  private int count;
+  private double[] values;
+
+  public DoubleMultiPointField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new double[initialArrayLength];
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSortedNumeric(context.reader(), fieldName);
+  }
+  @Override
+  public void collect(int doc) throws IOException {
+    if (docValues.advanceExact(doc)) {
+      count = docValues.docValueCount();
+      resizeEmptyValues(count);
+      for (int i = 0; i < count; ++i) {
+        values[i] = NumericUtils.sortableLongToDouble(docValues.nextValue());
+      }
+    } else {
+      count = 0;
+    }
+  }
+  
+  private void resizeEmptyValues(int count) {
+    if (count > values.length) {
+      values = new double[count];
+    }
+  }
+  
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamDoubles(value -> cons.accept(Double.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamDoubles(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatField.java
new file mode 100644
index 0000000..c382d61
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatField.java
@@ -0,0 +1,108 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.NumericDocValues;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.FloatValue.CastingFloatValue;
+import org.apache.solr.schema.FloatPointField;
+import org.apache.solr.schema.TrieFloatField;
+
+/**
+ * An analytics wrapper for a single-valued {@link TrieFloatField} or {@link FloatPointField} with DocValues enabled.
+ */
+public class FloatField extends AnalyticsField implements CastingFloatValue {
+  private NumericDocValues docValues;
+  private float value;
+  private boolean exists;
+
+  public FloatField(String fieldName) {
+    super(fieldName);
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getNumeric(context.reader(), fieldName);
+  }
+
+  @Override
+  public void collect(int doc) throws IOException {
+    exists = docValues.advanceExact(doc);
+    if (exists) {
+      value = NumericUtils.sortableIntToFloat((int)docValues.longValue());
+    }
+  }
+
+  @Override
+  public float getFloat() {
+    return value;
+  }
+  @Override
+  public double getDouble() {
+    return (double)value;
+  }
+  @Override
+  public String getString() {
+    return exists ? Float.toString(value) : null;
+  }
+  @Override
+  public Object getObject() {
+    return exists ? value : null;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    if (exists) {
+      cons.accept((double)value);
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    if (exists) {
+      cons.accept(Float.toString(value));
+    }
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+
+  @Override
+  public ExpressionComparator<Float> getObjectComparator(String expression) {
+    return new ExpressionComparator<>(expression);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatMultiField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatMultiField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatMultiField.java
new file mode 100644
index 0000000..acfce18
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatMultiField.java
@@ -0,0 +1,91 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.FloatValueStream.CastingFloatValueStream;
+import org.apache.solr.legacy.LegacyNumericUtils;
+import org.apache.solr.schema.TrieFloatField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link TrieFloatField} with DocValues enabled.
+ */
+public class FloatMultiField extends AnalyticsField implements CastingFloatValueStream {
+  private SortedSetDocValues docValues;
+  private int count;
+  private float[] values;
+
+  public FloatMultiField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new float[initialArrayLength];
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSortedSet(context.reader(), fieldName);
+  }
+  
+  @Override
+  public void collect(int doc) throws IOException {
+    count = 0;
+    if (docValues.advanceExact(doc)) {
+      int term;
+      while ((term = (int)docValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) {
+        if (count == values.length) {
+          resizeValues();
+        }
+        values[count++] = NumericUtils.sortableIntToFloat(LegacyNumericUtils.prefixCodedToInt(docValues.lookupOrd(term)));
+      }
+    }
+  }
+  
+  private void resizeValues() {
+    float[] newValues = new float[values.length*2];
+    for (int i = 0; i < count; ++i) {
+      newValues[i] = values[i];
+    }
+    values = newValues;
+  }
+  
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    streamFloats(value -> cons.accept((double)value));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamFloats(value -> cons.accept(Float.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamFloats(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatMultiPointField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatMultiPointField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatMultiPointField.java
new file mode 100644
index 0000000..947035e
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatMultiPointField.java
@@ -0,0 +1,87 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedNumericDocValues;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.FloatValueStream.CastingFloatValueStream;
+import org.apache.solr.schema.FloatPointField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link FloatPointField} with DocValues enabled.
+ */
+public class FloatMultiPointField extends AnalyticsField implements CastingFloatValueStream {
+  private SortedNumericDocValues docValues;
+  private int count;
+  private float[] values;
+
+  public FloatMultiPointField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new float[initialArrayLength];
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSortedNumeric(context.reader(), fieldName);
+  }
+  
+  @Override
+  public void collect(int doc) throws IOException {
+    if (docValues.advanceExact(doc)) {
+      count = docValues.docValueCount();
+      resizeEmptyValues(count);
+      for (int i = 0; i < count; ++i) {
+        values[i] = NumericUtils.sortableIntToFloat((int)docValues.nextValue());
+      }
+    } else {
+      count = 0;
+    }
+  }
+  
+  private void resizeEmptyValues(int count) {
+    if (count > values.length) {
+      values = new float[count];
+    }
+  }
+  
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    streamFloats(value -> cons.accept((double)value));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamFloats(value -> cons.accept(Float.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamFloats(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntField.java
new file mode 100644
index 0000000..e9ae52b
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntField.java
@@ -0,0 +1,129 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.NumericDocValues;
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.IntValue.CastingIntValue;
+import org.apache.solr.schema.IntPointField;
+import org.apache.solr.schema.TrieIntField;
+
+/**
+ * An analytics wrapper for a single-valued {@link TrieIntField} or {@link IntPointField} with DocValues enabled.
+ */
+public class IntField extends AnalyticsField implements CastingIntValue {
+  private NumericDocValues docValues;
+  private int value;
+  private boolean exists;
+  
+  public IntField(String fieldName) {
+    super(fieldName);
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getNumeric(context.reader(), fieldName);
+  }
+
+  @Override
+  public void collect(int doc) throws IOException {
+    exists = docValues.advanceExact(doc);
+    if (exists) {
+      value = (int)docValues.longValue();
+    }
+  }
+
+  @Override
+  public int getInt() {
+    return value;
+  }
+  @Override
+  public long getLong() {
+    return (long)value;
+  }
+  @Override
+  public float getFloat() {
+    return (float)value;
+  }
+  @Override
+  public double getDouble() {
+    return (double)value;
+  }
+  @Override
+  public String getString() {
+    return exists ? Integer.toString(value) : null;
+  }
+  @Override
+  public Object getObject() {
+    return exists ? value : null;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public void streamInts(IntConsumer cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    if (exists) {
+      cons.accept((long)value);
+    }
+  }
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    if (exists) {
+      cons.accept((float)value);
+    }
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    if (exists) {
+      cons.accept((double)value);
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    if (exists) {
+      cons.accept(Integer.toString(value));
+    }
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+
+  @Override
+  public ExpressionComparator<Integer> getObjectComparator(String expression) {
+    return new ExpressionComparator<>(expression);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntMultiField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntMultiField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntMultiField.java
new file mode 100644
index 0000000..657b1f3
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntMultiField.java
@@ -0,0 +1,100 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.IntValueStream.CastingIntValueStream;
+import org.apache.solr.legacy.LegacyNumericUtils;
+import org.apache.solr.schema.TrieIntField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link TrieIntField} with DocValues enabled.
+ */
+public class IntMultiField extends AnalyticsField implements CastingIntValueStream {
+  private SortedSetDocValues docValues;
+  private int count;
+  private int[] values;
+
+  public IntMultiField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new int[initialArrayLength];
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSortedSet(context.reader(), fieldName);
+  }
+  
+  @Override
+  public void collect(int doc) throws IOException {
+    count = 0;
+    if (docValues.advanceExact(doc)) {
+      int term;
+      while ((term = (int)docValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) {
+        if (count == values.length) {
+          resizeValues();
+        }
+        values[count++] = LegacyNumericUtils.prefixCodedToInt(docValues.lookupOrd(term));
+      }
+    }
+  }
+  
+  private void resizeValues() {
+    int[] newValues = new int[values.length*2];
+    for (int i = 0; i < count; ++i) {
+      newValues[i] = values[i];
+    }
+    values = newValues;
+  }
+  
+  @Override
+  public void streamInts(IntConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    streamInts(value -> cons.accept((long)value));
+  }
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    streamInts(value -> cons.accept((float)value));
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    streamInts(value -> cons.accept((double)value));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamInts(value -> cons.accept(Integer.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamInts(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntMultiPointField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntMultiPointField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntMultiPointField.java
new file mode 100644
index 0000000..2608fa1
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntMultiPointField.java
@@ -0,0 +1,96 @@
+/*
+ * 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.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedNumericDocValues;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.IntValueStream.CastingIntValueStream;
+import org.apache.solr.schema.IntPointField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link IntPointField} with DocValues enabled.
+ */
+public class IntMultiPointField extends AnalyticsField implements CastingIntValueStream {
+  private SortedNumericDocValues docValues;
+  private int count;
+  private int[] values;
+
+  public IntMultiPointField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new int[initialArrayLength];
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSortedNumeric(context.reader(), fieldName);
+  }
+  
+  @Override
+  public void collect(int doc) throws IOException {
+    if (docValues.advanceExact(doc)) {
+      count = docValues.docValueCount();
+      resizeEmptyValues(count);
+      for (int i = 0; i < count; ++i) {
+        values[i] = (int)docValues.nextValue();
+      }
+    } else {
+      count = 0;
+    }
+  }
+  
+  private void resizeEmptyValues(int count) {
+    if (count > values.length) {
+      values = new int[count];
+    }
+  }
+  
+  @Override
+  public void streamInts(IntConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    streamInts(value -> cons.accept((long)value));
+  }
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    streamInts(value -> cons.accept((float)value));
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    streamInts(value -> cons.accept((double)value));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamInts(value -> cons.accept(Integer.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamInts(value -> cons.accept(value));
+  }
+}


[06/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/RangeEndpointCalculator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/RangeEndpointCalculator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/RangeEndpointCalculator.java
deleted file mode 100644
index fa29022..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/RangeEndpointCalculator.java
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * 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.solr.analytics.util;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.solr.analytics.request.RangeFacetRequest;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.params.FacetParams.FacetRangeInclude;
-import org.apache.solr.common.params.FacetParams.FacetRangeOther;
-import org.apache.solr.schema.FieldType;
-import org.apache.solr.schema.SchemaField;
-import org.apache.solr.schema.TrieDateField;
-import org.apache.solr.schema.TrieField;
-import org.apache.solr.util.DateMathParser;
-
-
-public abstract class RangeEndpointCalculator<T extends Comparable<T>> {
-  protected final SchemaField field;
-  protected final RangeFacetRequest request;
-  
-  public RangeEndpointCalculator(final RangeFacetRequest request) {
-    this.field = request.getField();
-    this.request = request;
-  }
-
-  /**
-   * Formats a Range endpoint for use as a range label name in the response.
-   * Default Impl just uses toString()
-   */
-  public String formatValue(final T val) {
-    return val.toString();
-  }
-  
-  /**
-   * Parses a String param into an Range endpoint value throwing 
-   * a useful exception if not possible
-   */
-  public final T getValue(final String rawval) {
-    try {
-      return parseVal(rawval);
-    } catch (Exception e) {
-      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Can't parse value "+rawval+" for field: " + field.getName(), e);
-    }
-  }
-  
-  /**
-   * Parses a String param into an Range endpoint. 
-   * Can throw a low level format exception as needed.
-   */
-  protected abstract T parseVal(final String rawval) throws java.text.ParseException;
-
-  /** 
-   * Parses a String param into a value that represents the gap and 
-   * can be included in the response, throwing 
-   * a useful exception if not possible.
-   *
-   * Note: uses Object as the return type instead of T for things like 
-   * Date where gap is just a DateMathParser string 
-   */
-  public final Object getGap(final String gap) {
-    try {
-      return parseGap(gap);
-    } catch (Exception e) {
-      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Can't parse gap "+gap+" for field: " + field.getName(), e);
-    }
-  }
-
-  /**
-   * Parses a String param into a value that represents the gap and 
-   * can be included in the response. 
-   * Can throw a low level format exception as needed.
-   *
-   * Default Impl calls parseVal
-   */
-  protected Object parseGap(final String rawval) throws java.text.ParseException {
-    return parseVal(rawval);
-  }
-
-  /**
-   * Adds the String gap param to a low Range endpoint value to determine 
-   * the corrisponding high Range endpoint value, throwing 
-   * a useful exception if not possible.
-   */
-  public final T addGap(T value, String gap) {
-    try {
-      return parseAndAddGap(value, gap);
-    } catch (Exception e) {
-      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Can't add gap "+gap+" to value " + value + " for field: " + field.getName(), e);
-    }
-  }
-  
-  /**
-   * Adds the String gap param to a low Range endpoint value to determine 
-   * the corrisponding high Range endpoint value.
-   * Can throw a low level format exception as needed.
-   */
-  protected abstract T parseAndAddGap(T value, String gap) throws java.text.ParseException;
-
-  public static class FacetRange {
-    public final String name;
-    public final String lower;
-    public final String upper;
-    public final boolean includeLower;
-    public final boolean includeUpper;
-    
-    public FacetRange(String name, String lower, String upper, boolean includeLower, boolean includeUpper) {
-      this.name = name;
-      this.lower = lower;
-      this.upper = upper;
-      this.includeLower = includeLower;
-      this.includeUpper = includeUpper;
-    }
-  }
-  
-  public List<FacetRange> getRanges(){
-
-    final T start = getValue(request.getStart());
-    T end = getValue(request.getEnd()); // not final, hardend may change this
-    
-    if( end.compareTo(start) < 0 ){
-      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "range facet 'end' comes before 'start': "+end+" < "+start);
-    }
-    
-    // explicitly return the gap.  compute this early so we are more 
-    // likely to catch parse errors before attempting math
-    final String[] gaps = request.getGaps();
-    String gap = gaps[0];
-    
-    final EnumSet<FacetRangeInclude> include = request.getInclude();
-        
-    T low = start;
-    
-    List<FacetRange> ranges = new ArrayList<>();
-    
-    int gapCounter = 0;
-    
-    while (low.compareTo(end) < 0) {
-      if (gapCounter<gaps.length) {
-        gap = gaps[gapCounter++];
-      }
-      T high = addGap(low,gap);
-      if (end.compareTo(high) < 0) {
-        if (request.isHardEnd()){
-          high = end;
-        } else {
-          end = high;
-        }
-      }
-      
-      if (high.compareTo(low) < 0) {
-        throw new SolrException (SolrException.ErrorCode.BAD_REQUEST, "range facet infinite loop (is gap negative? did the math overflow?)");
-      }
-      
-      if (high.compareTo(low) == 0) {
-        throw new SolrException (SolrException.ErrorCode.BAD_REQUEST, "range facet infinite loop: gap is either zero, or too small relative start/end and caused underflow: " + low + " + " + gap + " = " + high );
-      }
-      
-      final boolean includeLower = (include.contains(FacetRangeInclude.ALL) ||
-                                    include.contains(FacetRangeInclude.LOWER) ||
-                                   (include.contains(FacetRangeInclude.EDGE) && 
-                                   0 == low.compareTo(start)));
-      final boolean includeUpper = (include.contains(FacetRangeInclude.ALL) ||
-                                    include.contains(FacetRangeInclude.UPPER) ||
-                                   (include.contains(FacetRangeInclude.EDGE) && 
-                                   0 == high.compareTo(end)));
-      
-      final String lowS = formatValue(low);
-      final String highS = formatValue(high);
-
-      ranges.add( new FacetRange(lowS,lowS,highS,includeLower,includeUpper) );
-      low = high;
-    }
-    
-    final Set<FacetRangeOther> others = request.getOthers();
-    if (null != others && 0 < others.size() ) {
-      
-      // no matter what other values are listed, we don't do
-      // anything if "none" is specified.
-      if( !others.contains(FacetRangeOther.NONE) ) {
-        
-        boolean all = others.contains(FacetRangeOther.ALL);
-
-        if (all || others.contains(FacetRangeOther.BEFORE)) {
-          // include upper bound if "outer" or if first gap doesn't already include it
-          ranges.add( new FacetRange(FacetRangeOther.BEFORE.toString(), 
-                                        null, formatValue(start), false, include.contains(FacetRangeInclude.OUTER) || include.contains(FacetRangeInclude.ALL) ||
-                                                            !(include.contains(FacetRangeInclude.LOWER) || include.contains(FacetRangeInclude.EDGE)) ) );
-          
-        }
-        if (all || others.contains(FacetRangeOther.AFTER)) {
-          // include lower bound if "outer" or if last gap doesn't already include it
-          ranges.add( new FacetRange(FacetRangeOther.AFTER.toString(), 
-                                        formatValue(end), null, include.contains(FacetRangeInclude.OUTER) || include.contains(FacetRangeInclude.ALL) ||
-                                                   !(include.contains(FacetRangeInclude.UPPER) || include.contains(FacetRangeInclude.EDGE)), false) );
-        }
-        if (all || others.contains(FacetRangeOther.BETWEEN)) {
-          ranges.add( new FacetRange(FacetRangeOther.BETWEEN.toString(), formatValue(start), formatValue(end),
-                                        include.contains(FacetRangeInclude.LOWER) || include.contains(FacetRangeInclude.EDGE) || include.contains(FacetRangeInclude.ALL),
-                                        include.contains(FacetRangeInclude.UPPER) || include.contains(FacetRangeInclude.EDGE) || include.contains(FacetRangeInclude.ALL)) );
-        }
-      }
-      
-    }
-  
-    return ranges;
-  }
-  
-  public static RangeEndpointCalculator<? extends Comparable<?>> create(RangeFacetRequest request){
-    final SchemaField sf = request.getField();
-    final FieldType ft = sf.getType();
-    final RangeEndpointCalculator<?> calc;
-    if (ft instanceof TrieField) {
-      switch (ft.getNumberType()) {
-        case FLOAT:
-          calc = new FloatRangeEndpointCalculator(request);
-          break;
-        case DOUBLE:
-          calc = new DoubleRangeEndpointCalculator(request);
-          break;
-        case INTEGER:
-          calc = new IntegerRangeEndpointCalculator(request);
-          break;
-        case LONG:
-          calc = new LongRangeEndpointCalculator(request);
-          break;
-        case DATE:
-          calc = new DateRangeEndpointCalculator(request, null);
-          break;
-        default:
-          throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unable to range facet on tried field of unexpected type:" + sf.getName());
-      }
-    } else {
-      throw new SolrException (SolrException.ErrorCode.BAD_REQUEST, "Unable to range facet on field:" + sf);
-    } 
-    return calc;
-  }
-  
-  public static class FloatRangeEndpointCalculator extends RangeEndpointCalculator<Float> {
-  
-    public FloatRangeEndpointCalculator(final RangeFacetRequest request) { super(request); }
-    
-    @Override
-    protected Float parseVal(String rawval) {
-      return Float.valueOf(rawval);
-    }
-    
-    @Override
-    public Float parseAndAddGap(Float value, String gap) {
-      return new Float(value.floatValue() + Float.parseFloat(gap));
-    }
-    
-  }
-  
-  public static class DoubleRangeEndpointCalculator extends RangeEndpointCalculator<Double> {
-  
-    public DoubleRangeEndpointCalculator(final RangeFacetRequest request) { super(request); }
-    
-    @Override
-    protected Double parseVal(String rawval) {
-      return Double.valueOf(rawval);
-    }
-    
-    @Override
-    public Double parseAndAddGap(Double value, String gap) {
-      return new Double(value.doubleValue() + Double.parseDouble(gap));
-    }
-    
-  }
-  
-  public static class IntegerRangeEndpointCalculator extends RangeEndpointCalculator<Integer> {
-  
-    public IntegerRangeEndpointCalculator(final RangeFacetRequest request) { super(request); }
-    
-    @Override
-    protected Integer parseVal(String rawval) {
-      return Integer.valueOf(rawval);
-    }
-    
-    @Override
-    public Integer parseAndAddGap(Integer value, String gap) {
-      return new Integer(value.intValue() + Integer.parseInt(gap));
-    }
-    
-  }
-  
-  public static class LongRangeEndpointCalculator extends RangeEndpointCalculator<Long> {
-  
-    public LongRangeEndpointCalculator(final RangeFacetRequest request) { super(request); }
-    
-    @Override
-    protected Long parseVal(String rawval) {
-      return Long.valueOf(rawval);
-    }
-    
-    @Override
-    public Long parseAndAddGap(Long value, String gap) {
-      return new Long(value.longValue() + Long.parseLong(gap));
-    }
-    
-  }
-  
-  public static class DateRangeEndpointCalculator extends RangeEndpointCalculator<Date> {
-    private final Date now;
-    public DateRangeEndpointCalculator(final RangeFacetRequest request, final Date now) { 
-      super(request); 
-      this.now = now;
-      if (! (field.getType() instanceof TrieDateField) ) {
-        throw new IllegalArgumentException("SchemaField must use field type extending TrieDateField");
-      }
-    }
-    
-    @Override
-    public String formatValue(Date val) {
-      return val.toInstant().toString();
-    }
-    
-    @Override
-    protected Date parseVal(String rawval) {
-      return DateMathParser.parseMath(now, rawval);
-    }
-    
-    @Override
-    protected Object parseGap(final String rawval) {
-      return rawval;
-    }
-    
-    @Override
-    public Date parseAndAddGap(Date value, String gap) throws java.text.ParseException {
-      final DateMathParser dmp = new DateMathParser();
-      dmp.setNow(value);
-      return dmp.parseMath(gap);
-    }
-    
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/BooleanConsumer.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/BooleanConsumer.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/BooleanConsumer.java
new file mode 100644
index 0000000..66d7a13
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/BooleanConsumer.java
@@ -0,0 +1,59 @@
+/*
+ * 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.solr.analytics.util.function;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Represents an operation that accepts a single {@code boolean}-valued argument and
+ * returns no result.  This is the primitive type specialization of
+ * {@link Consumer} for {@code boolean}.  Unlike most other functional interfaces,
+ * {@code IntConsumer} is expected to operate via side-effects.
+ *
+ * <p>This is a <a href="package-summary.html">functional interface</a>
+ * whose functional method is {@link #accept(boolean)}.
+ *
+ * @see Consumer
+ */
+@FunctionalInterface
+public interface BooleanConsumer {
+
+  /**
+   * Performs this operation on the given argument.
+   *
+   * @param value the input argument
+   */
+  void accept(boolean value);
+
+  /**
+   * Returns a composed {@code DoubleConsumer} that performs, in sequence, this
+   * operation followed by the {@code after} operation. If performing either
+   * operation throws an exception, it is relayed to the caller of the
+   * composed operation.  If performing this operation throws an exception,
+   * the {@code after} operation will not be performed.
+   *
+   * @param after the operation to perform after this operation
+   * @return a composed {@code DoubleConsumer} that performs in sequence this
+   * operation followed by the {@code after} operation
+   * @throws NullPointerException if {@code after} is null
+   */
+  default BooleanConsumer andThen(BooleanConsumer after) {
+    Objects.requireNonNull(after);
+    return (boolean t) -> { accept(t); after.accept(t); };
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/FloatConsumer.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/FloatConsumer.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/FloatConsumer.java
new file mode 100644
index 0000000..baa5c12
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/FloatConsumer.java
@@ -0,0 +1,59 @@
+/*
+ * 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.solr.analytics.util.function;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Represents an operation that accepts a single {@code float}-valued argument and
+ * returns no result.  This is the primitive type specialization of
+ * {@link Consumer} for {@code float}.  Unlike most other functional interfaces,
+ * {@code IntConsumer} is expected to operate via side-effects.
+ *
+ * <p>This is a <a href="package-summary.html">functional interface</a>
+ * whose functional method is {@link #accept(float)}.
+ *
+ * @see Consumer
+ */
+@FunctionalInterface
+public interface FloatConsumer {
+
+  /**
+   * Performs this operation on the given argument.
+   *
+   * @param value the input argument
+   */
+  void accept(float value);
+
+  /**
+   * Returns a composed {@code DoubleConsumer} that performs, in sequence, this
+   * operation followed by the {@code after} operation. If performing either
+   * operation throws an exception, it is relayed to the caller of the
+   * composed operation.  If performing this operation throws an exception,
+   * the {@code after} operation will not be performed.
+   *
+   * @param after the operation to perform after this operation
+   * @return a composed {@code DoubleConsumer} that performs in sequence this
+   * operation followed by the {@code after} operation
+   * @throws NullPointerException if {@code after} is null
+   */
+  default FloatConsumer andThen(FloatConsumer after) {
+    Objects.requireNonNull(after);
+    return (float t) -> { accept(t); after.accept(t); };
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/FloatSupplier.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/FloatSupplier.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/FloatSupplier.java
new file mode 100644
index 0000000..9801ea7
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/FloatSupplier.java
@@ -0,0 +1,41 @@
+/*
+ * 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.solr.analytics.util.function;
+
+import java.util.function.Supplier;
+
+/**
+ * Represents a supplier of {@code float}-valued results.  This is the
+ * {@code float}-producing primitive specialization of {@link Supplier}.
+ *
+ * <p>There is no requirement that a new or distinct result be returned each
+ * time the supplier is invoked.
+ *
+ * <p>This is a <a href="package-summary.html">functional interface</a>
+ * whose functional method is {@link #getAsFloat()}.
+ *
+ * @see Supplier
+ */
+@FunctionalInterface
+public interface FloatSupplier {
+  /**
+   * Gets a result.
+   *
+   * @return a result
+   */
+  float getAsFloat();
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/package-info.java
new file mode 100644
index 0000000..9d07e10
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/function/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+ 
+/** 
+ * Pure java functional interfaces. Not specific to analytics.
+ */
+package org.apache.solr.analytics.util.function;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/package-info.java
index a5676f1..49602c7 100644
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/package-info.java
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/package-info.java
@@ -16,9 +16,8 @@
  */
  
 /** 
- * Utilities used by analytics component
+ * Utility classes for the analytics component.
  */
 package org.apache.solr.analytics.util;
 
 
-

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/AbsoluteValueDoubleFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/AbsoluteValueDoubleFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/AbsoluteValueDoubleFunction.java
deleted file mode 100644
index f1e996c..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/AbsoluteValueDoubleFunction.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.solr.analytics.util.AnalyticsParams;
-
-/**
- * <code>AbsoluteValueDoubleFunction</code> takes the absolute value of the double value of the source it contains.
- */
-public class AbsoluteValueDoubleFunction extends SingleDoubleFunction {
-  public final static String NAME = AnalyticsParams.ABSOLUTE_VALUE;
-  
-  public AbsoluteValueDoubleFunction(ValueSource source) {
-    super(source);
-  }
-
-  protected String name() {
-    return NAME;
-  }
-
-  @Override
-  public String description() {
-    return name()+"("+source.description()+")";
-  }
-
-  protected double func(int doc, FunctionValues vals) throws IOException {
-    double d = vals.doubleVal(doc);
-    if (d<0) {
-      return d*-1;
-    } else {
-      return d;
-    }
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (getClass() != o.getClass()) return false;
-    AbsoluteValueDoubleFunction other = (AbsoluteValueDoubleFunction)o;
-    return this.source.equals(other.source);
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/AddDoubleFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/AddDoubleFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/AddDoubleFunction.java
deleted file mode 100644
index c47a0a3..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/AddDoubleFunction.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.solr.analytics.util.AnalyticsParams;
-
-/**
- * <code>AddDoubleFunction</code> returns the sum of its components.
- */
-public class AddDoubleFunction extends MultiDoubleFunction {
-  public final static String NAME = AnalyticsParams.ADD;
-
-  public AddDoubleFunction(ValueSource[] sources) {
-    super(sources);
-  }
-
-  @Override
-  protected String name() {
-    return NAME;
-  }
-
-  @Override
-  protected double func(int doc, FunctionValues[] valsArr) throws IOException {
-    double sum = 0d;
-    for (FunctionValues val : valsArr) {
-      sum += val.doubleVal(doc);
-    }
-    return sum;
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstDateSource.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstDateSource.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstDateSource.java
deleted file mode 100644
index 3ce608f..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstDateSource.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-import java.time.Instant;
-import java.util.Date;
-import java.util.Map;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.docvalues.FloatDocValues;
-import org.apache.lucene.util.mutable.MutableValue;
-import org.apache.lucene.util.mutable.MutableValueDate;
-import org.apache.solr.analytics.util.AnalyticsParams;
-
-/**
- * <code>ConstDateSource</code> returns a constant date for all documents
- */
-public class ConstDateSource extends ConstDoubleSource {
-  public final static String NAME = AnalyticsParams.CONSTANT_DATE;
-
-  public ConstDateSource(Date constant) {
-    super(constant.getTime());
-  }
-
-  public ConstDateSource(Long constant) {
-    super(constant);
-  }
-
-  @SuppressWarnings("deprecation")
-  @Override
-  public String description() {
-    return name()+"(" + Instant.ofEpochMilli(getLong()) + ")";
-  }
-
-  protected String name() {
-    return NAME;
-  }
-  
-  @Override
-  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
-    return new FloatDocValues(this) {
-      @Override
-      public float floatVal(int doc) {
-        return getFloat();
-      }
-      @Override
-      public int intVal(int doc) {
-        return getInt();
-      }
-      @Override
-      public long longVal(int doc) {
-        return getLong();
-      }
-      @Override
-      public double doubleVal(int doc) {
-        return getDouble();
-      }
-      @Override
-      public String toString(int doc) {
-        return description();
-      }
-      @Override
-      public Object objectVal(int doc) {
-        return new Date(longVal(doc));
-      }
-      @SuppressWarnings("deprecation")
-      @Override
-      public String strVal(int doc) {
-        return Instant.ofEpochMilli(longVal(doc)).toString();
-      }
-      @Override
-      public boolean boolVal(int doc) {
-        return getFloat() != 0.0f;
-      }
-
-      @Override
-      public ValueFiller getValueFiller() {
-        return new ValueFiller() {
-          private final MutableValueDate mval = new MutableValueDate();
-
-          @Override
-          public MutableValue getValue() {
-            return mval;
-          }
-
-          @Override
-          public void fillValue(int doc) {
-            mval.value = longVal(doc);
-            mval.exists = true;
-          }
-        };
-      }
-    };
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstDoubleSource.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstDoubleSource.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstDoubleSource.java
deleted file mode 100644
index e0ebad6..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstDoubleSource.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-import java.util.Map;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
-import org.apache.lucene.queries.function.valuesource.ConstNumberSource;
-import org.apache.solr.analytics.util.AnalyticsParams;
-
-/**
- * <code>ConstDoubleSource</code> returns a constant double for all documents
- */
-public class ConstDoubleSource extends ConstNumberSource {
-  public final static String NAME = AnalyticsParams.CONSTANT_NUMBER;
-  final double constant;
-
-  public ConstDoubleSource(double constant) {
-    this.constant = constant;
-  }
-
-  @Override
-  public String description() {
-    return name()+"(" + getFloat() + ")";
-  }
-
-  protected String name() {
-    return NAME;
-  }
-
-  @Override
-  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
-    return new DoubleDocValues(this) {
-      @Override
-      public double doubleVal(int doc) {
-        return constant;
-      }
-      @Override
-      public boolean exists(int doc) {
-        return true;
-      }
-    };
-  }
-
-  @Override
-  public int hashCode() {
-    return (int)Double.doubleToLongBits(constant) * 31;
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (!(o instanceof ConstDoubleSource)) return false;
-    ConstDoubleSource other = (ConstDoubleSource)o;
-    return  this.constant == other.constant;
-  }
-
-  @Override
-  public int getInt() {
-    return (int)constant;
-  }
-
-  @Override
-  public long getLong() {
-    return (long)constant;
-  }
-
-  @Override
-  public float getFloat() {
-    return (float)constant;
-  }
-
-  @Override
-  public double getDouble() {
-    return constant;
-  }
-
-  @Override
-  public Number getNumber() {
-    return new Double(constant);
-  }
-
-  @Override
-  public boolean getBool() {
-    return constant != 0.0f;
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstStringSource.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstStringSource.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstStringSource.java
deleted file mode 100644
index 52bdf52..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ConstStringSource.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import org.apache.lucene.queries.function.valuesource.LiteralValueSource;
-import org.apache.solr.analytics.util.AnalyticsParams;
-
-/**
- * <code>ConstStringSource</code> returns a constant string for all documents
- */
-public class ConstStringSource extends LiteralValueSource {
-  public final static String NAME = AnalyticsParams.CONSTANT_STRING;
-
-  public ConstStringSource(String string) {
-    super(string);
-  }
-
-  @Override
-  public String description() {
-    return name()+"(" + string + ")";
-  }
-
-  protected String name() {
-    return NAME;
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) return true;
-    if (!(o instanceof ConstStringSource)) return false;
-    ConstStringSource that = (ConstStringSource) o;
-
-    return getValue().equals(that.getValue());
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DateFieldSource.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DateFieldSource.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DateFieldSource.java
deleted file mode 100644
index 803d8e0..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DateFieldSource.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-import java.time.Instant;
-import java.util.Date;
-import java.util.Map;
-
-import org.apache.lucene.index.DocValues;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.NumericDocValues;
-import org.apache.solr.legacy.LegacyNumericUtils;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.docvalues.LongDocValues;
-import org.apache.lucene.queries.function.valuesource.LongFieldSource;
-import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.mutable.MutableValue;
-import org.apache.lucene.util.mutable.MutableValueDate;
-
-/**
- * Extends {@link LongFieldSource} to have a field source that takes in 
- * and returns {@link Date} values while working with long values internally.
- */
-public class DateFieldSource extends LongFieldSource {
-
-  public DateFieldSource(String field) {
-    super(field);
-  }
-
-  public long externalToLong(String extVal) {
-    return LegacyNumericUtils.prefixCodedToLong(new BytesRef(extVal));
-  }
-
-  public Object longToObject(long val) {
-    return new Date(val);
-  }
-
-  @SuppressWarnings("deprecation")
-  public String longToString(long val) {
-    return Instant.ofEpochMilli(val).toString();
-  }
-
-  @Override
-  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
-    final NumericDocValues arr = DocValues.getNumeric(readerContext.reader(), field);
-    return new LongDocValues(this) {
-
-      private long getDocValue(int doc) throws IOException {
-        int arrDocID = arr.docID();
-        if (arrDocID < doc) {
-          arrDocID = arr.advance(doc);
-        }
-        if (arrDocID == doc) {
-          return arr.longValue();
-        } else {
-          return 0;
-        }
-      }
-      
-      @Override
-      public long longVal(int doc) throws IOException {
-        return getDocValue(doc);
-      }
-
-      @Override
-      public boolean exists(int doc) throws IOException {
-        getDocValue(doc);
-        return arr.docID() == doc;
-      }
-
-      @Override
-      public Object objectVal(int doc) throws IOException {
-        return exists(doc) ? longToObject(getDocValue(doc)) : null;
-      }
-
-      @Override
-      public String strVal(int doc) throws IOException {
-        return exists(doc) ? longToString(getDocValue(doc)) : null;
-      }
-
-      @Override
-      public ValueFiller getValueFiller() {
-        return new ValueFiller() {
-          private final MutableValueDate mval = new MutableValueDate();
-
-          @Override
-          public MutableValue getValue() {
-            return mval;
-          }
-
-          @Override
-          public void fillValue(int doc) throws IOException {
-            mval.value = getDocValue(doc);
-            mval.exists = exists(doc);
-          }
-        };
-      }
-
-    };
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (o.getClass() != this.getClass()) return false;
-    DateFieldSource other = (DateFieldSource) o;
-    return field.equals(other.field);
-  }
-
-  @Override
-  public int hashCode() {
-    int h = this.getClass().hashCode();
-    h += super.hashCode();
-    return h;
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DateMathFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DateMathFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DateMathFunction.java
deleted file mode 100644
index 77b172a..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DateMathFunction.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-import java.text.ParseException;
-import java.util.Date;
-
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.queries.function.valuesource.BytesRefFieldSource;
-import org.apache.solr.analytics.util.AnalyticsParams;
-import org.apache.solr.util.DateMathParser;
-
-/**
- * <code>DateMathFunction</code> returns a start date modified by a list of DateMath operations.
- */
-public class DateMathFunction extends MultiDateFunction {
-  public final static String NAME = AnalyticsParams.DATE_MATH;
-  final private DateMathParser parser;
-  
-  /**
-   * @param sources A list of ValueSource objects. The first element in the list
-   * should be a {@link DateFieldSource} or {@link ConstDateSource} object which
-   * represents the starting date. The rest of the field should be {@link BytesRefFieldSource}
-   * or {@link ConstStringSource} objects which contain the DateMath operations to perform on 
-   * the start date.
-   */
-  public DateMathFunction(ValueSource[] sources) {
-    super(sources);
-    parser = new DateMathParser();
-  }
-
-  @Override
-  protected String name() {
-    return NAME;
-  }
-
-  @Override
-  protected long func(int doc, FunctionValues[] valsArr) throws IOException {
-    long time = 0;
-    Date date = (Date)valsArr[0].objectVal(doc);
-    try {
-      parser.setNow(date);
-      for (int count = 1; count < valsArr.length; count++) {
-          date = parser.parseMath(valsArr[count].strVal(doc));
-        parser.setNow(date);
-      }
-      time = parser.getNow().getTime();
-    } catch (ParseException e) {
-      e.printStackTrace();
-      time = date.getTime();
-    }
-    return time;
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DivDoubleFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DivDoubleFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DivDoubleFunction.java
deleted file mode 100644
index 25c4c96..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DivDoubleFunction.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.solr.analytics.util.AnalyticsParams;
-
-/**
- * <code>DivDoubleFunction</code> returns the quotient of 'a' and 'b'.
- */
-public class DivDoubleFunction extends DualDoubleFunction {
-  public final static String NAME = AnalyticsParams.DIVIDE;
-
-  /**
-    * @param   a  the numerator.
-    * @param   b  the denominator.
-    */
-  public DivDoubleFunction(ValueSource a, ValueSource b) {
-    super(a, b);
-  }
-
-  protected String name() {
-    return NAME;
-  }
-
-  @Override
-  protected double func(int doc, FunctionValues aVals, FunctionValues bVals) throws IOException {
-    return aVals.doubleVal(doc)/bVals.doubleVal(doc);
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DualDoubleFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DualDoubleFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DualDoubleFunction.java
deleted file mode 100644
index 97b8af7..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/DualDoubleFunction.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-import java.util.Map;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
-import org.apache.lucene.search.IndexSearcher;
-
-/**
- * Abstract {@link ValueSource} implementation which wraps two ValueSources
- * and applies an extendible double function to their values.
- **/
-public abstract class DualDoubleFunction extends ValueSource {
-  protected final ValueSource a;
-  protected final ValueSource b;
-
-  public DualDoubleFunction(ValueSource a, ValueSource b) {
-    this.a = a;
-    this.b = b;
-  }
-
-  protected abstract String name();
-  protected abstract double func(int doc, FunctionValues aVals, FunctionValues bVals) throws IOException;
-
-  @Override
-  public String description() {
-    return name() + "(" + a.description() + "," + b.description() + ")";
-  }
-
-  @Override
-  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
-    final FunctionValues aVals =  a.getValues(context, readerContext);
-    final FunctionValues bVals =  b.getValues(context, readerContext);
-    return new DoubleDocValues(this) {
-      @Override
-      public double doubleVal(int doc) throws IOException {
-        return func(doc, aVals, bVals);
-      }
-      
-      @Override
-      public boolean exists(int doc) throws IOException {
-        return aVals.exists(doc) & bVals.exists(doc);
-      }
-
-      @Override
-      public String toString(int doc) throws IOException {
-        return name() + '(' + aVals.toString(doc) + ',' + bVals.toString(doc) + ')';
-      }
-    };
-  }
-
-  @Override
-  public void createWeight(Map context, IndexSearcher searcher) throws IOException {
-    a.createWeight(context,searcher);
-    b.createWeight(context,searcher);
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (getClass() != o.getClass()) return false;
-    DualDoubleFunction other = (DualDoubleFunction)o;
-    return this.a.equals(other.a)
-        && this.b.equals(other.b);
-  }
-
-  @Override
-  public int hashCode() {
-    int h = a.hashCode();
-    h ^= (h << 13) | (h >>> 20);
-    h += b.hashCode();
-    h ^= (h << 23) | (h >>> 10);
-    h += name().hashCode();
-    return h;
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/FilterFieldSource.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/FilterFieldSource.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/FilterFieldSource.java
deleted file mode 100644
index 3f374dc..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/FilterFieldSource.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-import java.util.Date;
-import java.util.Map;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.util.mutable.MutableValue;
-import org.apache.solr.analytics.util.AnalyticsParams;
-
-/**
- * <code>DefaultIsMissingFieldSource</code> wraps a field source to return missing values 
- * if the value is equal to the default value.
- */
-public class FilterFieldSource extends ValueSource {
-  public final static String NAME = AnalyticsParams.FILTER;
-  public final Object missValue;
-  protected final ValueSource source;
-  
-  public FilterFieldSource(ValueSource source, Object missValue) {
-    this.source = source;
-    this.missValue = missValue;
-  }
-
-  protected String name() {
-    return NAME;
-  }
-
-  @SuppressWarnings("deprecation")
-  @Override
-  public String description() {
-    if (missValue.getClass().equals(Date.class)) {
-      return name()+"("+source.description()+","+ ((Date)missValue).toInstant() +")";
-    } else {
-      return name()+"("+source.description()+","+missValue.toString()+")";
-    }
-  }
-
-  @Override
-  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
-    final FunctionValues vals =  source.getValues(context, readerContext);
-    return new FunctionValues() {
-
-      @Override
-      public byte byteVal(int doc) throws IOException {
-        return vals.byteVal(doc);
-      }
-
-      @Override
-      public short shortVal(int doc) throws IOException {
-        return vals.shortVal(doc);
-      }
-
-      @Override
-      public float floatVal(int doc) throws IOException {
-        return vals.floatVal(doc);
-      }
-
-      @Override
-      public int intVal(int doc) throws IOException {
-        return vals.intVal(doc);
-      }
-
-      @Override
-      public long longVal(int doc) throws IOException {
-        return vals.longVal(doc);
-      }
-
-      @Override
-      public double doubleVal(int doc) throws IOException {
-        return vals.doubleVal(doc);
-      }
-
-      @Override
-      public String strVal(int doc) throws IOException {
-        return vals.strVal(doc);
-      }
-
-      @Override
-      public Object objectVal(int doc) throws IOException {
-        return exists(doc)? vals.objectVal(doc) : null;
-      }
-
-      @Override
-      public boolean exists(int doc) throws IOException {
-        Object other = vals.objectVal(doc);
-        return other!=null&&!missValue.equals(other);
-      }
-
-      @Override
-      public String toString(int doc) throws IOException {
-        return NAME + '(' + vals.toString(doc) + ')';
-      }
-
-      @Override
-      public ValueFiller getValueFiller() {
-        return new ValueFiller() {
-          private final ValueFiller delegateFiller = vals.getValueFiller();
-          private final MutableValue mval = delegateFiller.getValue();
-
-          @Override
-          public MutableValue getValue() {
-            return mval;
-          }
-
-          @Override
-          public void fillValue(int doc) throws IOException {
-            delegateFiller.fillValue(doc);
-            mval.exists = exists(doc);
-          }
-        };
-      }
-    };
-  }
-  
-  public ValueSource getRootSource() {
-    if (source instanceof FilterFieldSource) {
-      return ((FilterFieldSource)source).getRootSource();
-    } else {
-      return source;
-    }
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (getClass() != o.getClass()) return false;
-    FilterFieldSource other = (FilterFieldSource)o;
-    return this.source.equals(other.source) && this.missValue.equals(other.missValue);
-  }
-
-  @Override
-  public int hashCode() {
-    return source.hashCode()+name().hashCode();
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/LogDoubleFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/LogDoubleFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/LogDoubleFunction.java
deleted file mode 100644
index 41a6b67..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/LogDoubleFunction.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.solr.analytics.util.AnalyticsParams;
-
-/**
- * <code>LogDoubleFunction</code> returns the log of a double value with a given base.
- */
-public class LogDoubleFunction extends DualDoubleFunction {
-  public final static String NAME = AnalyticsParams.LOG;
-  
-  public LogDoubleFunction(ValueSource a, ValueSource b) {
-    super(a,b);
-  }
-
-  protected String name() {
-    return NAME;
-  }
-
-  @Override
-  protected double func(int doc, FunctionValues aVals, FunctionValues bVals) throws IOException {
-    return Math.log(aVals.doubleVal(doc))/Math.log(bVals.doubleVal(doc));
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/MultiDateFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/MultiDateFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/MultiDateFunction.java
deleted file mode 100644
index beeaaa7..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/MultiDateFunction.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Map;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.queries.function.docvalues.LongDocValues;
-import org.apache.lucene.util.mutable.MutableValue;
-import org.apache.lucene.util.mutable.MutableValueDate;
-
-/**
- * Abstract {@link ValueSource} implementation which wraps multiple ValueSources
- * and applies an extendible date function to their values.
- **/
-public abstract class MultiDateFunction extends ValueSource {
-  protected final ValueSource[] sources;
-  
-  public MultiDateFunction(ValueSource[] sources) {
-    this.sources = sources;
-  }
-
-  abstract protected String name();
-  abstract protected long func(int doc, FunctionValues[] valsArr) throws IOException;
-
-  @Override
-  public String description() {
-    StringBuilder sb = new StringBuilder();
-    sb.append(name()).append('(');
-    boolean firstTime=true;
-    for (ValueSource source : sources) {
-      if (firstTime) {
-        firstTime=false;
-      } else {
-        sb.append(',');
-      }
-      sb.append(source);
-    }
-    sb.append(')');
-    return sb.toString();
-  }
-
-  @Override
-  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
-    final FunctionValues[] valsArr = new FunctionValues[sources.length];
-    for (int i=0; i<sources.length; i++) {
-      valsArr[i] = sources[i].getValues(context, readerContext);
-    }
-
-    return new LongDocValues(this) {
-      @Override
-      public long longVal(int doc) throws IOException {
-        return func(doc, valsArr);
-      }
-      
-      @Override
-      public boolean exists(int doc) throws IOException {
-        boolean exists = true;
-        for (FunctionValues val : valsArr) {
-          exists = exists & val.exists(doc);
-        }
-        return exists;
-      }
-      
-      @Override
-      public String toString(int doc) throws IOException {
-        StringBuilder sb = new StringBuilder();
-        sb.append(name()).append('(');
-        boolean firstTime=true;
-        for (FunctionValues vals : valsArr) {
-          if (firstTime) {
-            firstTime=false;
-          } else {
-            sb.append(',');
-          }
-          sb.append(vals.toString(doc));
-        }
-        sb.append(')');
-        return sb.toString();
-      }
-
-      @Override
-      public ValueFiller getValueFiller() {
-        return new ValueFiller() {
-          private final MutableValueDate mval = new MutableValueDate();
-
-          @Override
-          public MutableValue getValue() {
-            return mval;
-          }
-
-          @Override
-          public void fillValue(int doc) throws IOException {
-            mval.value = longVal(doc);
-            mval.exists = exists(doc);
-          }
-        };
-      }
-    };
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (getClass() != o.getClass()) return false;
-    MultiDateFunction other = (MultiDateFunction)o;
-    return this.name().equals(other.name())
-            && Arrays.equals(this.sources, other.sources);
-  }
-
-  @Override
-  public int hashCode() {
-    return Arrays.hashCode(sources) + name().hashCode();
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/MultiDoubleFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/MultiDoubleFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/MultiDoubleFunction.java
deleted file mode 100644
index 09956f2..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/MultiDoubleFunction.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Map;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
-import org.apache.lucene.search.IndexSearcher;
-
-/**
- * Abstract {@link ValueSource} implementation which wraps multiple ValueSources
- * and applies an extendible double function to their values.
- **/
-public abstract class MultiDoubleFunction extends ValueSource {
-  protected final ValueSource[] sources;
-
-  public MultiDoubleFunction(ValueSource[] sources) {
-    this.sources = sources;
-  }
-
-  abstract protected String name();
-  abstract protected double func(int doc, FunctionValues[] valsArr) throws IOException;
-
-  @Override
-  public String description() {
-    StringBuilder sb = new StringBuilder();
-    sb.append(name()).append('(');
-    boolean firstTime=true;
-    for (ValueSource source : sources) {
-      if (firstTime) {
-        firstTime=false;
-      } else {
-        sb.append(',');
-      }
-      sb.append(source);
-    }
-    sb.append(')');
-    return sb.toString();
-  }
-
-  @Override
-  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
-    final FunctionValues[] valsArr = new FunctionValues[sources.length];
-    for (int i=0; i<sources.length; i++) {
-      valsArr[i] = sources[i].getValues(context, readerContext);
-    }
-
-    return new DoubleDocValues(this) {
-      @Override
-      public double doubleVal(int doc) throws IOException {
-        return func(doc, valsArr);
-      }
-      
-      @Override
-      public boolean exists(int doc) throws IOException {
-        boolean exists = true;
-        for (FunctionValues val : valsArr) {
-          exists = exists & val.exists(doc);
-        }
-        return exists;
-      }
-       
-      @Override
-      public String toString(int doc) throws IOException {
-        StringBuilder sb = new StringBuilder();
-        sb.append(name()).append('(');
-        boolean firstTime=true;
-        for (FunctionValues vals : valsArr) {
-          if (firstTime) {
-            firstTime=false;
-          } else {
-            sb.append(',');
-          }
-          sb.append(vals.toString(doc));
-        }
-        sb.append(')');
-        return sb.toString();
-      }
-    };
-  }
-
-  @Override
-  public void createWeight(Map context, IndexSearcher searcher) throws IOException {
-    for (ValueSource source : sources)
-      source.createWeight(context, searcher);
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (getClass() != o.getClass()) return false;
-    MultiDoubleFunction other = (MultiDoubleFunction)o;
-    return this.name().equals(other.name())
-            && Arrays.equals(this.sources, other.sources);
-  }
-
-  @Override
-  public int hashCode() {
-    return Arrays.hashCode(sources) + name().hashCode();
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/MultiplyDoubleFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/MultiplyDoubleFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/MultiplyDoubleFunction.java
deleted file mode 100644
index 83f26f0..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/MultiplyDoubleFunction.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.solr.analytics.util.AnalyticsParams;
-
-/**
- * <code>MultiplyDoubleFunction</code> returns the product of its components.
- */
-public class MultiplyDoubleFunction extends MultiDoubleFunction {
-  public final static String NAME = AnalyticsParams.MULTIPLY;
-
-  public MultiplyDoubleFunction(ValueSource[] sources) {
-    super(sources);
-  }
-
-  @Override
-  protected String name() {
-    return NAME;
-  }
-
-  @Override
-  protected double func(int doc, FunctionValues[] valsArr) throws IOException {
-    double product = 1d;
-    for (FunctionValues val : valsArr) {
-      product *= val.doubleVal(doc);
-    }
-    return product;
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/NegateDoubleFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/NegateDoubleFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/NegateDoubleFunction.java
deleted file mode 100644
index 9c8445d..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/NegateDoubleFunction.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.solr.analytics.util.AnalyticsParams;
-
-/**
- * <code>NegateDoubleFunction</code> negates the double value of the source it contains.
- */
-public class NegateDoubleFunction extends SingleDoubleFunction {
-  public final static String NAME = AnalyticsParams.NEGATE;
-  
-  public NegateDoubleFunction(ValueSource source) {
-    super(source);
-  }
-
-  protected String name() {
-    return NAME;
-  }
-
-  @Override
-  public String description() {
-    return name()+"("+source.description()+")";
-  }
-
-  protected double func(int doc, FunctionValues vals) throws IOException {
-    return vals.doubleVal(doc)*-1;
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (getClass() != o.getClass()) return false;
-    NegateDoubleFunction other = (NegateDoubleFunction)o;
-    return this.source.equals(other.source);
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/PowDoubleFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/PowDoubleFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/PowDoubleFunction.java
deleted file mode 100644
index 043313b..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/PowDoubleFunction.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.solr.analytics.util.AnalyticsParams;
-
-/**
- * <code>PowDoubleFunction</code> returns 'a' raised to the power of 'b'.
- */
-public class PowDoubleFunction extends DualDoubleFunction {
-  public final static String NAME = AnalyticsParams.POWER;
-
-  /**
-    * @param   a  the base.
-    * @param   b  the exponent.
-    */
-  public PowDoubleFunction(ValueSource a, ValueSource b) {
-    super(a, b);
-  }
-
-  @Override
-  protected String name() {
-    return NAME;
-  }
-
-  @Override
-  protected double func(int doc, FunctionValues aVals, FunctionValues bVals) throws IOException {
-    return Math.pow(aVals.doubleVal(doc), bVals.doubleVal(doc));
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ReverseStringFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ReverseStringFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ReverseStringFunction.java
deleted file mode 100644
index 4fa0f99..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/ReverseStringFunction.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-
-import org.apache.commons.lang.StringUtils;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.solr.analytics.util.AnalyticsParams;
-
-/**
- * <code>ReverseStringFunction</code> reverses the string value of the source it contains.
- */
-public class ReverseStringFunction extends SingleStringFunction {
-  public final static String NAME = AnalyticsParams.REVERSE;
-  
-  public ReverseStringFunction(ValueSource source) {
-    super(source);
-  }
-
-  protected String name() {
-    return NAME;
-  }
-
-  protected CharSequence func(int doc, FunctionValues vals) throws IOException {
-    String val = vals.strVal(doc);
-    return val != null ? StringUtils.reverse(val) : null;
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/SingleDoubleFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/SingleDoubleFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/SingleDoubleFunction.java
deleted file mode 100644
index 04f79ed..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/SingleDoubleFunction.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-import java.util.Map;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
-
-/**
- * Abstract {@link ValueSource} implementation which wraps one ValueSource
- * and applies an extendible double function to its values.
- */
-public abstract class SingleDoubleFunction extends ValueSource {
-  protected final ValueSource source;
-  
-  public SingleDoubleFunction(ValueSource source) {
-    this.source = source;
-  }
-
-  @Override
-  public String description() {
-    return name()+"("+source.description()+")";
-  }
-
-  abstract String name();
-  abstract double func(int doc, FunctionValues vals) throws IOException;
-
-  @Override
-  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
-    final FunctionValues vals =  source.getValues(context, readerContext);
-    return new DoubleDocValues(this) {
-      @Override
-      public double doubleVal(int doc) throws IOException {
-        return func(doc, vals);
-      }
-      
-      @Override
-      public boolean exists(int doc) throws IOException {
-        return vals.exists(doc);
-      }
-
-      @Override
-      public String toString(int doc) throws IOException {
-        return name() + '(' + vals.toString(doc) + ')';
-      }
-    };
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (getClass() != o.getClass()) return false;
-    SingleDoubleFunction other = (SingleDoubleFunction)o;
-    return this.source.equals(other.source);
-  }
-
-  @Override
-  public int hashCode() {
-    return source.hashCode()+name().hashCode();
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/SingleStringFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/SingleStringFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/SingleStringFunction.java
deleted file mode 100644
index b3357f9..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/SingleStringFunction.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.solr.analytics.util.valuesource;
-
-import java.io.IOException;
-import java.util.Map;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.queries.function.FunctionValues;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.queries.function.docvalues.StrDocValues;
-import org.apache.lucene.util.BytesRefBuilder;
-import org.apache.lucene.util.mutable.MutableValue;
-import org.apache.lucene.util.mutable.MutableValueStr;
-
-/**
- * Abstract {@link ValueSource} implementation which wraps one ValueSource
- * and applies an extendible string function to its values.
- */
-public abstract class SingleStringFunction extends ValueSource {
-  protected final ValueSource source;
-  
-  public SingleStringFunction(ValueSource source) {
-    this.source = source;
-  }
-
-  @Override
-  public String description() {
-    return name()+"("+source.description()+")";
-  }
-
-  abstract String name();
-  abstract CharSequence func(int doc, FunctionValues vals) throws IOException;
-
-  @Override
-  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
-    final FunctionValues vals =  source.getValues(context, readerContext);
-    return new StrDocValues(this) {
-      @Override
-      public String strVal(int doc) throws IOException {
-        CharSequence cs = func(doc, vals);
-        return cs != null ? cs.toString() : null;
-      }
-      
-      @Override
-      public boolean bytesVal(int doc, BytesRefBuilder bytes) throws IOException {
-        CharSequence cs = func(doc, vals);
-        if( cs != null ){
-          bytes.copyChars(func(doc,vals));
-          return true;
-        } else {
-          bytes.clear();
-          return false;
-        }
-      }
-
-      @Override
-      public Object objectVal(int doc) throws IOException {
-        return strVal(doc);
-      }
-      
-      @Override
-      public boolean exists(int doc) throws IOException {
-        return vals.exists(doc);
-      }
-
-      @Override
-      public String toString(int doc) throws IOException {
-        return name() + '(' + strVal(doc) + ')';
-      }
-
-      @Override
-      public ValueFiller getValueFiller() {
-        return new ValueFiller() {
-          private final MutableValueStr mval = new MutableValueStr();
-
-          @Override
-          public MutableValue getValue() {
-            return mval;
-          }
-
-          @Override
-          public void fillValue(int doc) throws IOException {
-            mval.exists = bytesVal(doc, mval.value);
-          }
-        };
-      }
-    };
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (getClass() != o.getClass()) return false;
-    SingleStringFunction other = (SingleStringFunction)o;
-    return this.source.equals(other.source);
-  }
-
-  @Override
-  public int hashCode() {
-    return source.hashCode()+name().hashCode();
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/package-info.java
deleted file mode 100644
index 72a23d2..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/util/valuesource/package-info.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.
- */
- 
-/** 
- * ValueSource function/sources used by analytics component
- */
-package org.apache.solr.analytics.util.valuesource;
-
-
-


[14/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LambdaFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LambdaFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LambdaFunction.java
new file mode 100644
index 0000000..ff8a1a6
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LambdaFunction.java
@@ -0,0 +1,3220 @@
+/*
+ * 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.solr.analytics.function.mapping;
+
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.function.mapping.LambdaFunction.BoolInBoolOutLambda;
+import org.apache.solr.analytics.function.mapping.LambdaFunction.DoubleInDoubleOutLambda;
+import org.apache.solr.analytics.function.mapping.LambdaFunction.FloatInFloatOutLambda;
+import org.apache.solr.analytics.function.mapping.LambdaFunction.IntInIntOutLambda;
+import org.apache.solr.analytics.function.mapping.LambdaFunction.LongInLongOutLambda;
+import org.apache.solr.analytics.function.mapping.LambdaFunction.StringInStringOutLambda;
+import org.apache.solr.analytics.function.mapping.LambdaFunction.TwoBoolInBoolOutLambda;
+import org.apache.solr.analytics.function.mapping.LambdaFunction.TwoDoubleInDoubleOutLambda;
+import org.apache.solr.analytics.function.mapping.LambdaFunction.TwoFloatInFloatOutLambda;
+import org.apache.solr.analytics.function.mapping.LambdaFunction.TwoIntInIntOutLambda;
+import org.apache.solr.analytics.function.mapping.LambdaFunction.TwoLongInLongOutLambda;
+import org.apache.solr.analytics.function.mapping.LambdaFunction.TwoStringInStringOutLambda;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.BooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValue;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValue;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.BooleanValue.AbstractBooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream.AbstractBooleanValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DateValueStream.AbstractDateValueStream;
+import org.apache.solr.analytics.value.DoubleValue.AbstractDoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream.AbstractDoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue.AbstractFloatValue;
+import org.apache.solr.analytics.value.FloatValueStream.AbstractFloatValueStream;
+import org.apache.solr.analytics.value.IntValue.AbstractIntValue;
+import org.apache.solr.analytics.value.IntValueStream.AbstractIntValueStream;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.value.LongValueStream.AbstractLongValueStream;
+import org.apache.solr.analytics.value.StringValue.AbstractStringValue;
+import org.apache.solr.analytics.value.StringValueStream.AbstractStringValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * Lambda Functions are used to easily create basic mapping functions.
+ * <p>
+ * There are lambda functions for all types: boolean, int, long, float, double, date and string.
+ * Lambda functions must have parameters of all the same type, which will be the same type as the returned Value or ValueStream.
+ * <p>
+ * The types of functions that are accepted are: (multi = multi-valued expression, single = single-valued expression)
+ * <ul>
+ * <li> {@code func(single) -> single}
+ * <li> {@code func(multi) -> multi}
+ * <li> {@code func(single,single) -> single}
+ * <li> {@code func(multi,single) -> multi}
+ * <li> {@code func(single,multi) -> multi}
+ * <li> {@code func(multi) -> single}
+ * <li> {@code func(single,single,...) -> single}
+ * (You can also specify whether all parameters must exist, or at least one must exist for the returned value to exist)
+ * </ul>
+ * <p>
+ * NOTE: The combination of name and parameters MUST be unique for an expression.
+ * <br>
+ * For example consider if {@code join(fieldA, ',')} and {@code join(fieldA, ';')} are both called. If the JoinFunction uses:
+ * <br>
+ * {@code LambdaFunction.createStringLambdaFunction("join", (a,b) -> a + sep + b, (StringValueStream)params[0])}
+ * <br>
+ * then both the name "join" and single parameter fieldA will be used for two DIFFERENT expressions.
+ * This does not meet the uniqueness requirmenet and will break the query.
+ * <br>
+ * A solution to this is to name the function using the missing information:
+ * <br>
+ * {@code LambdaFunction.createStringLambdaFunction("join(" + sep + ")", (a,b) -> a + sep + b, (StringValueStream)params[0])}
+ * <br>
+ * Therefore both expressions will have fieldA as the only parameter, but the names {@code "join(,)"} and {@code "join(;)"} will be different.
+ * This meets the uniqueness requirement for lambda functions.
+ */
+public class LambdaFunction {
+  private static final boolean defaultMultiExistsMethod = true;
+  
+  /* *********************
+   * 
+   *  Boolean Functions
+   * 
+   * *********************/
+  
+  /**
+   * Creates a function that takes in either a single or multi valued boolean expression and returns the same type of expression with
+   * the given lambda function applied to every value.
+   * 
+   * @param name name for the function
+   * @param lambda the function to be applied to every value: {@code (boolean) -> boolean}
+   * @param param the expression to apply the lambda to
+   * @return an expression the same type as was given with the lambda applied
+   */
+  public static BooleanValueStream createBooleanLambdaFunction(String name, BoolInBoolOutLambda lambda, BooleanValueStream param) {
+    if (param instanceof BooleanValue) {
+      return new BooleanValueInBooleanValueOutFunction(name,lambda,(BooleanValue)param);
+    } else {
+      return new BooleanStreamInBooleanStreamOutFunction(name,lambda,param);
+    }
+  }
+  /**
+   * Creates a function that takes in a multi-valued boolean expression and returns a single-valued boolean expression.
+   * The given lambda is used to associatively (order not guaranteed) reduce all values for a document down to a single value. 
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (boolean, boolean) -> boolean}
+   * @param param the expression to be reduced per-document
+   * @return a single-valued expression which has been reduced for every document
+   */
+  public static BooleanValue createBooleanLambdaFunction(String name, TwoBoolInBoolOutLambda lambda, BooleanValueStream param) {
+    return new BooleanStreamInBooleanValueOutFunction(name,lambda,param);
+  }
+  /**
+   * Creates a function that maps two booleans to a single boolean.
+   * This can take the following shapes:
+   * <ul>
+   * <li> Taking in two single-valued expressions and returning a single-valued expression which represents the lambda combination of the inputs.
+   * <li> Taking in a single-valued expression and a multi-valued expression and returning a multi-valued expression which
+   * represents the lambda combination of the single-value input with each of the values of the multi-value input.
+   * <br>
+   * The inputs can be either {@code func(single,multi)} or {@code func(multi,single)}.
+   * </ul>
+   * <p>
+   * 
+   * @param name name for the function
+   * @param lambda the function to be applied to every value: {@code (boolean,boolean) -> boolean}
+   * @param param1 the first parameter in the lambda
+   * @param param2 the second parameter in the lambda
+   * @return a single or multi valued expression combining the two parameters with the given lambda
+   * @throws SolrException if neither parameter is single-valued
+   */
+  public static BooleanValueStream createBooleanLambdaFunction(String name, TwoBoolInBoolOutLambda lambda, BooleanValueStream param1, BooleanValueStream param2) throws SolrException {
+    if (param1 instanceof BooleanValue && param2 instanceof BooleanValue) {
+      return new TwoBooleanValueInBooleanValueOutFunction(name,lambda,(BooleanValue)param1,(BooleanValue)param2);
+    } else if (param1 instanceof BooleanValue) {
+      return new BooleanValueBooleanStreamInBooleanStreamOutFunction(name,lambda,(BooleanValue)param1,param2);
+    } else if (param2 instanceof BooleanValue) {
+      return new BooleanStreamBooleanValueInBooleanStreamOutFunction(name,lambda,param1,(BooleanValue)param2);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires at least 1 single-valued parameter.");
+    }
+  }
+  /**
+   * Forwards the creation of the function to {@link #createBooleanLambdaFunction(String, TwoBoolInBoolOutLambda, BooleanValue[], boolean)},
+   * using {@value #defaultMultiExistsMethod} for the last argument ({@code allMustExist}).
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (boolean, boolean) -> boolean}
+   * @param params the expressions to reduce
+   * @return a single-value expression that reduces the parameters with the given lambda
+   */
+  public static BooleanValue createBooleanLambdaFunction(String name, TwoBoolInBoolOutLambda lambda, BooleanValue[] params) {
+    return createBooleanLambdaFunction(name,lambda,params,defaultMultiExistsMethod);
+  }
+  /**
+   * Creates a function that associatively (order is guaranteed) reduces multiple
+   * single-value boolean expressions into a single-value boolean expression for each document.
+   * <br>
+   * For a document, every parameter's value must exist for the resulting value to exist if {@code allMustExist} is true.
+   * If {@code allMustExist} is false, only one of the parameters' values must exist.
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (boolean, boolean) -> boolean}
+   * @param params the expressions to reduce
+   * @param allMustExist whether all parameters are required to exist
+   * @return a single-value expression that reduces the parameters with the given lambda
+   */
+  public static BooleanValue createBooleanLambdaFunction(String name, TwoBoolInBoolOutLambda lambda, BooleanValue[] params, boolean allMustExist) {
+    if (allMustExist) {
+      return new MultiBooleanValueInBooleanValueOutRequireAllFunction(name,lambda,params);
+    } else {
+      return new MultiBooleanValueInBooleanValueOutRequireOneFunction(name,lambda,params);
+    }
+  }
+  
+  /* *********************
+   * 
+   *  Integer Functions
+   * 
+   * *********************/
+  
+  /**
+   * Creates a function that takes in either a single or multi valued integer expression and returns the same type of expression with
+   * the given lambda function applied to every value.
+   * 
+   * @param name name for the function
+   * @param lambda the function to be applied to every value: {@code (integer) -> integer}
+   * @param param the expression to apply the lambda to
+   * @return an expression the same type as was given with the lambda applied
+   */
+  public static IntValueStream createIntLambdaFunction(String name, IntInIntOutLambda lambda, IntValueStream param) {
+    if (param instanceof IntValue) {
+      return new IntValueInIntValueOutFunction(name,lambda,(IntValue)param);
+    } else {
+      return new IntStreamInIntStreamOutFunction(name,lambda,param);
+    }
+  }
+  /**
+   * Creates a function that takes in a multi-valued integer expression and returns a single-valued integer expression.
+   * The given lambda is used to associatively (order not guaranteed) reduce all values for a document down to a single value. 
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (integer, integer) -> integer}
+   * @param param the expression to be reduced per-document
+   * @return a single-valued expression which has been reduced for every document
+   */
+  public static IntValue createIntLambdaFunction(String name, TwoIntInIntOutLambda lambda, IntValueStream param) {
+    return new IntStreamInIntValueOutFunction(name,lambda,param);
+  }
+  /**
+   * Creates a function that maps two integers to a single integer.
+   * This can take the following shapes:
+   * <ul>
+   * <li> Taking in two single-valued expressions and returning a single-valued expression which represents the lambda combination of the inputs.
+   * <li> Taking in a single-valued expression and a multi-valued expression and returning a multi-valued expression which
+   * represents the lambda combination of the single-value input with each of the values of the multi-value input.
+   * <br>
+   * The inputs can be either {@code func(single,multi)} or {@code func(multi,single)}.
+   * </ul>
+   * <p>
+   * 
+   * @param name name for the function
+   * @param lambda the function to be applied to every value: {@code (integer,integer) -> integer}
+   * @param param1 the first parameter in the lambda
+   * @param param2 the second parameter in the lambda
+   * @return a single or multi valued expression combining the two parameters with the given lambda
+   * @throws SolrException if neither parameter is single-valued
+   */
+  public static IntValueStream createIntLambdaFunction(String name, TwoIntInIntOutLambda lambda, IntValueStream param1, IntValueStream param2) throws SolrException {
+    if (param1 instanceof IntValue && param2 instanceof IntValue) {
+      return new TwoIntValueInIntValueOutFunction(name,lambda,(IntValue)param1,(IntValue)param2);
+    } else if (param1 instanceof IntValue) {
+      return new IntValueIntStreamInIntStreamOutFunction(name,lambda,(IntValue)param1,param2);
+    } else if (param2 instanceof IntValue) {
+      return new IntStreamIntValueInIntStreamOutFunction(name,lambda,param1,(IntValue)param2);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires at least 1 single-valued parameter.");
+    }
+  }
+  /**
+   * Forwards the creation of the function to {@link #createIntLambdaFunction(String, TwoIntInIntOutLambda, IntValue[], boolean)},
+   * using {@value #defaultMultiExistsMethod} for the last argument ({@code allMustExist}).
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (boolean, boolean) -> boolean}
+   * @param params the expressions to reduce
+   * @return a single-value expression that reduces the parameters with the given lambda
+   */
+  public static IntValue createIntLambdaFunction(String name, TwoIntInIntOutLambda lambda, IntValue[] params) {
+    return createIntLambdaFunction(name,lambda,params,defaultMultiExistsMethod);
+  }
+  /**
+   * Creates a function that associatively (order is guaranteed) reduces multiple
+   * single-value integer expressions into a single-value integer expression for each document.
+   * <br>
+   * For a document, every parameter's value must exist for the resulting value to exist if {@code allMustExist} is true.
+   * If {@code allMustExist} is false, only one of the parameters' values must exist.
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (integer, integer) -> integer}
+   * @param params the expressions to reduce
+   * @param allMustExist whether all parameters are required to exist
+   * @return a single-value expression that reduces the parameters with the given lambda
+   */
+  public static IntValue createIntLambdaFunction(String name, TwoIntInIntOutLambda lambda, IntValue[] params, boolean allMustExist) {
+    if (allMustExist) {
+      return new MultiIntValueInIntValueOutRequireAllFunction(name,lambda,params);
+    } else {
+      return new MultiIntValueInIntValueOutRequireOneFunction(name,lambda,params);
+    }
+  }
+  
+  /* *********************
+   * 
+   *  Long Functions
+   * 
+   * *********************/
+  
+  /**
+   * Creates a function that takes in either a single or multi valued long expression and returns the same type of expression with
+   * the given lambda function applied to every value.
+   * 
+   * @param name name for the function
+   * @param lambda the function to be applied to every value: {@code (long) -> long}
+   * @param param the expression to apply the lambda to
+   * @return an expression the same type as was given with the lambda applied
+   */
+  public static LongValueStream createLongLambdaFunction(String name, LongInLongOutLambda lambda, LongValueStream param) {
+    if (param instanceof LongValue) {
+      return new LongValueInLongValueOutFunction(name,lambda,(LongValue)param);
+    } else {
+      return new LongStreamInLongStreamOutFunction(name,lambda,param);
+    }
+  }
+  /**
+   * Creates a function that takes in a multi-valued long expression and returns a single-valued long expression.
+   * The given lambda is used to associatively (order not guaranteed) reduce all values for a document down to a single value. 
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (boolean, boolean) -> boolean}
+   * @param param the expression to be reduced per-document
+   * @return a single-valued expression which has been reduced for every document
+   */
+  public static LongValue createLongLambdaFunction(String name, TwoLongInLongOutLambda lambda, LongValueStream param) {
+    return new LongStreamInLongValueOutFunction(name,lambda,param);
+  }
+  /**
+   * Creates a function that maps two longs to a single long.
+   * This can take the following shapes:
+   * <ul>
+   * <li> Taking in two single-valued expressions and returning a single-valued expression which represents the lambda combination of the inputs.
+   * <li> Taking in a single-valued expression and a multi-valued expression and returning a multi-valued expression which
+   * represents the lambda combination of the single-value input with each of the values of the multi-value input.
+   * <br>
+   * The inputs can be either {@code func(single,multi)} or {@code func(multi,single)}.
+   * </ul>
+   * <p>
+   * 
+   * @param name name for the function
+   * @param lambda the function to be applied to every value: {@code (long,long) -> long}
+   * @param param1 the first parameter in the lambda
+   * @param param2 the second parameter in the lambda
+   * @return a single or multi valued expression combining the two parameters with the given lambda
+   * @throws SolrException if neither parameter is single-valued
+   */
+  public static LongValueStream createLongLambdaFunction(String name, TwoLongInLongOutLambda lambda, LongValueStream param1, LongValueStream param2) throws SolrException {
+    if (param1 instanceof LongValue && param2 instanceof LongValue) {
+      return new TwoLongValueInLongValueOutFunction(name,lambda,(LongValue)param1,(LongValue)param2);
+    } else if (param1 instanceof LongValue) {
+      return new LongValueLongStreamInLongStreamOutFunction(name,lambda,(LongValue)param1,param2);
+    } else if (param2 instanceof LongValue) {
+      return new LongStreamLongValueInLongStreamOutFunction(name,lambda,param1,(LongValue)param2);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires at least 1 single-valued parameter.");
+    }
+  }
+  /**
+   * Forwards the creation of the function to {@link #createLongLambdaFunction(String, TwoLongInLongOutLambda, LongValue[], boolean)},
+   * using {@value #defaultMultiExistsMethod} for the last argument ({@code allMustExist}).
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (boolean, boolean) -> boolean}
+   * @param params the expressions to reduce
+   * @return a single-value expression that reduces the parameters with the given lambda
+   */
+  public static LongValue createLongLambdaFunction(String name, TwoLongInLongOutLambda lambda, LongValue[] params) {
+    return createLongLambdaFunction(name,lambda,params,defaultMultiExistsMethod);
+  }
+  /**
+   * Creates a function that associatively (order is guaranteed) reduces multiple
+   * single-value long expressions into a single-value long expression for each document.
+   * <br>
+   * For a document, every parameter's value must exist for the resulting value to exist if {@code allMustExist} is true.
+   * If {@code allMustExist} is false, only one of the parameters' values must exist.
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (long, long) -> long}
+   * @param params the expressions to reduce
+   * @param allMustExist whether all parameters are required to exist
+   * @return a single-value expression that reduces the parameters with the given lambda
+   */
+  public static LongValue createLongLambdaFunction(String name, TwoLongInLongOutLambda lambda, LongValue[] params, boolean allMustExist) {
+    if (allMustExist) {
+      return new MultiLongValueInLongValueOutRequireAllFunction(name,lambda,params);
+    } else {
+      return new MultiLongValueInLongValueOutRequireOneFunction(name,lambda,params);
+    }
+  }
+  
+  /* *********************
+   * 
+   *  Float Functions
+   * 
+   * *********************/
+  
+  /**
+   * Creates a function that takes in either a single or multi valued float expression and returns the same type of expression with
+   * the given lambda function applied to every value.
+   * 
+   * @param name name for the function
+   * @param lambda the function to be applied to every value: {@code (float) -> float}
+   * @param param the expression to apply the lambda to
+   * @return an expression the same type as was given with the lambda applied
+   */
+  public static FloatValueStream createFloatLambdaFunction(String name, FloatInFloatOutLambda lambda, FloatValueStream param) {
+    if (param instanceof FloatValue) {
+      return new FloatValueInFloatValueOutFunction(name,lambda,(FloatValue)param);
+    } else {
+      return new FloatStreamInFloatStreamOutFunction(name,lambda,param);
+    }
+  }
+  /**
+   * Creates a function that takes in a multi-valued float expression and returns a single-valued float expression.
+   * The given lambda is used to associatively (order not guaranteed) reduce all values for a document down to a single value. 
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (float, float) -> float}
+   * @param param the expression to be reduced per-document
+   * @return a single-valued expression which has been reduced for every document
+   */
+  public static FloatValue createFloatLambdaFunction(String name, TwoFloatInFloatOutLambda lambda, FloatValueStream param) {
+    return new FloatStreamInFloatValueOutFunction(name,lambda,param);
+  }
+  /**
+   * Creates a function that maps two floats to a single float.
+   * This can take the following shapes:
+   * <ul>
+   * <li> Taking in two single-valued expressions and returning a single-valued expression which represents the lambda combination of the inputs.
+   * <li> Taking in a single-valued expression and a multi-valued expression and returning a multi-valued expression which
+   * represents the lambda combination of the single-value input with each of the values of the multi-value input.
+   * <br>
+   * The inputs can be either {@code func(single,multi)} or {@code func(multi,single)}.
+   * </ul>
+   * <p>
+   * 
+   * @param name name for the function
+   * @param lambda the function to be applied to every value: {@code (float,float) -> float}
+   * @param param1 the first parameter in the lambda
+   * @param param2 the second parameter in the lambda
+   * @return a single or multi valued expression combining the two parameters with the given lambda
+   * @throws SolrException if neither parameter is single-valued
+   */
+  public static FloatValueStream createFloatLambdaFunction(String name, TwoFloatInFloatOutLambda lambda, FloatValueStream param1, FloatValueStream param2) throws SolrException {
+    if (param1 instanceof FloatValue && param2 instanceof FloatValue) {
+      return new TwoFloatValueInFloatValueOutFunction(name,lambda,(FloatValue)param1,(FloatValue)param2);
+    } else if (param1 instanceof FloatValue) {
+      return new FloatValueFloatStreamInFloatStreamOutFunction(name,lambda,(FloatValue)param1,param2);
+    } else if (param2 instanceof FloatValue) {
+      return new FloatStreamFloatValueInFloatStreamOutFunction(name,lambda,param1,(FloatValue)param2);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires at least 1 single-valued parameter.");
+    }
+  }
+  /**
+   * Forwards the creation of the function to {@link #createFloatLambdaFunction(String, TwoFloatInFloatOutLambda, FloatValue[], boolean)},
+   * using {@value #defaultMultiExistsMethod} for the last argument ({@code allMustExist}).
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (boolean, boolean) -> boolean}
+   * @param params the expressions to reduce
+   * @return a single-value expression that reduces the parameters with the given lambda
+   */
+  public static FloatValue createFloatLambdaFunction(String name, TwoFloatInFloatOutLambda lambda, FloatValue[] params) {
+    return createFloatLambdaFunction(name,lambda,params,defaultMultiExistsMethod);
+  }
+  /**
+   * Creates a function that associatively (order is guaranteed) reduces multiple
+   * single-value float expressions into a single-value float expression for each document.
+   * <br>
+   * For a document, every parameter's value must exist for the resulting value to exist if {@code allMustExist} is true.
+   * If {@code allMustExist} is false, only one of the parameters' values must exist.
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (float, float) -> float}
+   * @param params the expressions to reduce
+   * @param allMustExist whether all parameters are required to exist
+   * @return a single-value expression that reduces the parameters with the given lambda
+   */
+  public static FloatValue createFloatLambdaFunction(String name, TwoFloatInFloatOutLambda lambda, FloatValue[] params, boolean allMustExist) {
+    if (allMustExist) {
+      return new MultiFloatValueInFloatValueOutRequireAllFunction(name,lambda,params);
+    } else {
+      return new MultiFloatValueInFloatValueOutRequireOneFunction(name,lambda,params);
+    }
+  }
+  
+  /* *********************
+   * 
+   *  Double Functions
+   * 
+   * *********************/
+  
+  /**
+   * Creates a function that takes in either a single or multi valued double expression and returns the same type of expression with
+   * the given lambda function applied to every value.
+   * 
+   * @param name name for the function
+   * @param lambda the function to be applied to every value: {@code (double) -> double}
+   * @param param the expression to apply the lambda to
+   * @return an expression the same type as was given with the lambda applied
+   */
+  public static DoubleValueStream createDoubleLambdaFunction(String name, DoubleInDoubleOutLambda lambda, DoubleValueStream param) {
+    if (param instanceof DoubleValue) {
+      return new DoubleValueInDoubleValueOutFunction(name,lambda,(DoubleValue)param);
+    } else {
+      return new DoubleStreamInDoubleStreamOutFunction(name,lambda,param);
+    }
+  }
+  /**
+   * Creates a function that takes in a multi-valued double expression and returns a single-valued double expression.
+   * The given lambda is used to associatively (order not guaranteed) reduce all values for a document down to a single value. 
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (double, double) -> double}
+   * @param param the expression to be reduced per-document
+   * @return a single-valued expression which has been reduced for every document
+   */
+  public static DoubleValue createDoubleLambdaFunction(String name, TwoDoubleInDoubleOutLambda lambda, DoubleValueStream param) {
+    return new DoubleStreamInDoubleValueOutFunction(name,lambda,param);
+  }
+  /**
+   * Creates a function that maps two doubles to a single double.
+   * This can take the following shapes:
+   * <ul>
+   * <li> Taking in two single-valued expressions and returning a single-valued expression which represents the lambda combination of the inputs.
+   * <li> Taking in a single-valued expression and a multi-valued expression and returning a multi-valued expression which
+   * represents the lambda combination of the single-value input with each of the values of the multi-value input.
+   * <br>
+   * The inputs can be either {@code func(single,multi)} or {@code func(multi,single)}.
+   * </ul>
+   * <p>
+   * 
+   * @param name name for the function
+   * @param lambda the function to be applied to every value: {@code (double,double) -> double}
+   * @param param1 the first parameter in the lambda
+   * @param param2 the second parameter in the lambda
+   * @return a single or multi valued expression combining the two parameters with the given lambda
+   * @throws SolrException if neither parameter is single-valued
+   */
+  public static DoubleValueStream createDoubleLambdaFunction(String name, TwoDoubleInDoubleOutLambda lambda, DoubleValueStream param1, DoubleValueStream param2) throws SolrException {
+    if (param1 instanceof DoubleValue && param2 instanceof DoubleValue) {
+      return new TwoDoubleValueInDoubleValueOutFunction(name,lambda,(DoubleValue)param1,(DoubleValue)param2);
+    } else if (param1 instanceof DoubleValue) {
+      return new DoubleValueDoubleStreamInDoubleStreamOutFunction(name,lambda,(DoubleValue)param1,param2);
+    } else if (param2 instanceof DoubleValue) {
+      return new DoubleStreamDoubleValueInDoubleStreamOutFunction(name,lambda,param1,(DoubleValue)param2);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires at least 1 single-valued parameter.");
+    }
+  }
+  /**
+   * Forwards the creation of the function to {@link #createDoubleLambdaFunction(String, TwoDoubleInDoubleOutLambda, DoubleValue[], boolean)},
+   * using {@value #defaultMultiExistsMethod} for the last argument ({@code allMustExist}).
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (boolean, boolean) -> boolean}
+   * @param params the expressions to reduce
+   * @return a single-value expression that reduces the parameters with the given lambda
+   */
+  public static DoubleValue createDoubleLambdaFunction(String name, TwoDoubleInDoubleOutLambda lambda, DoubleValue[] params) {
+    return createDoubleLambdaFunction(name,lambda,params,defaultMultiExistsMethod);
+  }
+  /**
+   * Creates a function that associatively (order is guaranteed) reduces multiple
+   * single-value double expressions into a single-value double expression for each document.
+   * <br>
+   * For a document, every parameter's value must exist for the resulting value to exist if {@code allMustExist} is true.
+   * If {@code allMustExist} is false, only one of the parameters' values must exist.
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (double, double) -> double}
+   * @param params the expressions to reduce
+   * @param allMustExist whether all parameters are required to exist
+   * @return a single-value expression that reduces the parameters with the given lambda
+   */
+  public static DoubleValue createDoubleLambdaFunction(String name, TwoDoubleInDoubleOutLambda lambda, DoubleValue[] params, boolean allMustExist) {
+    if (allMustExist) {
+      return new MultiDoubleValueInDoubleValueOutRequireAllFunction(name,lambda,params);
+    } else {
+      return new MultiDoubleValueInDoubleValueOutRequireOneFunction(name,lambda,params);
+    }
+  }
+  
+  /* *********************
+   * 
+   *  Date Functions
+   * 
+   * *********************/
+  
+  /**
+   * Creates a function that takes in either a single or multi valued date expression and returns the same type of expression with
+   * the given lambda function applied to every value. 
+   * 
+   * <p>
+   * NOTE: The lambda must work on longs, not Date objects
+   * 
+   * @param name name for the function
+   * @param lambda the function to be applied to every value: {@code (long) -> long}
+   * @param param the expression to apply the lambda to
+   * @return an expression the same type as was given with the lambda applied
+   */
+  public static DateValueStream createDateLambdaFunction(String name, LongInLongOutLambda lambda, DateValueStream param) {
+    if (param instanceof DateValue) {
+      return new DateValueInDateValueOutFunction(name,lambda,(DateValue)param);
+    } else {
+      return new DateStreamInDateStreamOutFunction(name,lambda,param);
+    }
+  }
+  /**
+   * Creates a function that takes in a multi-valued date expression and returns a single-valued date expression.
+   * The given lambda is used to associatively (order not guaranteed) reduce all values for a document down to a single value. 
+   * 
+   * <p>
+   * NOTE: The lambda must work on longs, not Date objects
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (long, long) -> long}
+   * @param param the expression to be reduced per-document
+   * @return a single-valued expression which has been reduced for every document
+   */
+  public static DateValue createDateLambdaFunction(String name, TwoLongInLongOutLambda lambda, DateValueStream param) {
+    return new DateStreamInDateValueOutFunction(name,lambda,param);
+  }
+  /**
+   * Creates a function that maps two dates to a single date.
+   * This can take the following shapes:
+   * <ul>
+   * <li> Taking in two single-valued expressions and returning a single-valued expression which represents the lambda combination of the inputs.
+   * <li> Taking in a single-valued expression and a multi-valued expression and returning a multi-valued expression which
+   * represents the lambda combination of the single-value input with each of the values of the multi-value input.
+   * <br>
+   * The inputs can be either {@code func(single,multi)} or {@code func(multi,single)}.
+   * </ul>
+   * 
+   * <p>
+   * NOTE: The lambda must work on longs, not Date objects
+   * 
+   * @param name name for the function
+   * @param lambda the function to be applied to every value: {@code (long,long) -> long}
+   * @param param1 the first parameter in the lambda
+   * @param param2 the second parameter in the lambda
+   * @return a single or multi valued expression combining the two parameters with the given lambda
+   * @throws SolrException if neither parameter is single-valued
+   */
+  public static DateValueStream createDateLambdaFunction(String name, TwoLongInLongOutLambda lambda, DateValueStream param1, DateValueStream param2) throws SolrException {
+    if (param1 instanceof DateValue && param2 instanceof DateValue) {
+      return new TwoDateValueInDateValueOutFunction(name,lambda,(DateValue)param1,(DateValue)param2);
+    } else if (param1 instanceof DateValue) {
+      return new DateValueDateStreamInDateStreamOutFunction(name,lambda,(DateValue)param1,param2);
+    } else if (param2 instanceof DateValue) {
+      return new DateStreamDateValueInDateStreamOutFunction(name,lambda,param1,(DateValue)param2);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires at least 1 single-valued parameter.");
+    }
+  }
+  /**
+   * Forwards the creation of the function to {@link #createDateLambdaFunction(String, TwoLongInLongOutLambda, DateValue[], boolean)},
+   * using {@value #defaultMultiExistsMethod} for the last argument ({@code allMustExist}).
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (boolean, boolean) -> boolean}
+   * @param params the expressions to reduce
+   * @return a single-value expression that reduces the parameters with the given lambda
+   */
+  public static DateValue createDateLambdaFunction(String name, TwoLongInLongOutLambda lambda, DateValue[] params) {
+    return createDateLambdaFunction(name,lambda,params,defaultMultiExistsMethod);
+  }
+  /**
+   * Creates a function that associatively (order is guaranteed) reduces multiple
+   * single-value date expressions into a single-value date expression for each document.
+   * <br>
+   * For a document, every parameter's value must exist for the resulting value to exist if {@code allMustExist} is true.
+   * If {@code allMustExist} is false, only one of the parameters' values must exist.
+   * 
+   * <p>
+   * NOTE: The lambda must work on longs, not Date objects
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (long, long) -> long}
+   * @param params the expressions to reduce
+   * @param allMustExist whether all parameters are required to exist
+   * @return a single-value expression that reduces the parameters with the given lambda
+   */
+  public static DateValue createDateLambdaFunction(String name, TwoLongInLongOutLambda lambda, DateValue[] params, boolean allMustExist) {
+    if (allMustExist) {
+      return new MultiDateValueInDateValueOutRequireAllFunction(name,lambda,params);
+    } else {
+      return new MultiDateValueInDateValueOutRequireOneFunction(name,lambda,params);
+    }
+  }
+  
+  /* *********************
+   * 
+   *  String Functions
+   * 
+   * *********************/
+  
+  /**
+   * Creates a function that takes in either a single or multi valued string expression and returns the same type of expression with
+   * the given lambda function applied to every value.
+   * 
+   * @param name name for the function
+   * @param lambda the function to be applied to every value: {@code (String) -> String}
+   * @param param the expression to apply the lambda to
+   * @return an expression the same type as was given with the lambda applied
+   */
+  public static StringValueStream createStringLambdaFunction(String name, StringInStringOutLambda lambda, StringValueStream param) {
+    if (param instanceof StringValue) {
+      return new StringValueInStringValueOutFunction(name,lambda,(StringValue)param);
+    } else {
+      return new StringStreamInStringStreamOutFunction(name,lambda,param);
+    }
+  }
+  /**
+   * Creates a function that takes in a multi-valued string expression and returns a single-valued string expression.
+   * The given lambda is used to associatively (order not guaranteed) reduce all values for a document down to a single value. 
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (String, String) -> String}
+   * @param param the expression to be reduced per-document
+   * @return a single-valued expression which has been reduced for every document
+   */
+  public static StringValue createStringLambdaFunction(String name, TwoStringInStringOutLambda lambda, StringValueStream param) {
+    return new StringStreamInStringValueOutFunction(name,lambda,param);
+  }
+  /**
+   * Creates a function that maps two strings to a single string.
+   * This can take the following shapes:
+   * <ul>
+   * <li> Taking in two single-valued expressions and returning a single-valued expression which represents the lambda combination of the inputs.
+   * <li> Taking in a single-valued expression and a multi-valued expression and returning a multi-valued expression which
+   * represents the lambda combination of the single-value input with each of the values of the multi-value input.
+   * <br>
+   * The inputs can be either {@code func(single,multi)} or {@code func(multi,single)}.
+   * </ul>
+   * <p>
+   * 
+   * @param name name for the function
+   * @param lambda the function to be applied to every value: {@code (String,String) -> String}
+   * @param param1 the first parameter in the lambda
+   * @param param2 the second parameter in the lambda
+   * @return a single or multi valued expression combining the two parameters with the given lambda
+   * @throws SolrException if neither parameter is single-valued
+   */
+  public static StringValueStream createStringLambdaFunction(String name, TwoStringInStringOutLambda lambda, StringValueStream param1, StringValueStream param2) throws SolrException {
+    if (param1 instanceof StringValue && param2 instanceof StringValue) {
+      return new TwoStringValueInStringValueOutFunction(name,lambda,(StringValue)param1,(StringValue)param2);
+    } else if (param1 instanceof StringValue) {
+      return new StringValueStringStreamInStringStreamOutFunction(name,lambda,(StringValue)param1,param2);
+    } else if (param2 instanceof StringValue) {
+      return new StringStreamStringValueInStringStreamOutFunction(name,lambda,param1,(StringValue)param2);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires at least 1 single-valued parameter.");
+    }
+  }
+  /**
+   * Forwards the creation of the function to {@link #createStringLambdaFunction(String, TwoStringInStringOutLambda, StringValue[], boolean)},
+   * using {@value #defaultMultiExistsMethod} for the last argument ({@code allMustExist}).
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (boolean, boolean) -> boolean}
+   * @param params the expressions to reduce
+   * @return a single-value expression that reduces the parameters with the given lambda
+   */
+  public static StringValue createStringLambdaFunction(String name, TwoStringInStringOutLambda lambda, StringValue[] params) {
+    return createStringLambdaFunction(name,lambda,params,defaultMultiExistsMethod);
+  }
+  /**
+   * Creates a function that associatively (order is guaranteed) reduces multiple
+   * single-value string expressions into a single-value string expression for each document.
+   * <br>
+   * For a document, every parameter's value must exist for the resulting value to exist if {@code allMustExist} is true.
+   * If {@code allMustExist} is false, only one of the parameters' values must exist.
+   * 
+   * @param name name for the function
+   * @param lambda the associative function used to reduce the values: {@code (String, String) -> String}
+   * @param params the expressions to reduce
+   * @param allMustExist whether all parameters are required to exist
+   * @return a single-value expression that reduces the parameters with the given lambda
+   */
+  public static StringValue createStringLambdaFunction(String name, TwoStringInStringOutLambda lambda, StringValue[] params, boolean allMustExist) {
+    if (allMustExist) {
+      return new MultiStringValueInStringValueOutRequireAllFunction(name,lambda,params);
+    } else {
+      return new MultiStringValueInStringValueOutRequireOneFunction(name,lambda,params);
+    }
+  }
+  
+
+  /*
+   * Single Parameter
+   */
+  // Boolean Out
+  @FunctionalInterface
+  public static interface BoolInBoolOutLambda   { boolean apply(boolean a); }
+  @FunctionalInterface
+  public static interface IntInBoolOutLambda    { boolean apply(int     a); }
+  @FunctionalInterface
+  public static interface LongInBoolOutLambda   { boolean apply(long    a); }
+  @FunctionalInterface
+  public static interface FloatInBoolOutLambda  { boolean apply(float   a); }
+  @FunctionalInterface
+  public static interface DoubleInBoolOutLambda { boolean apply(double  a); }
+  @FunctionalInterface
+  public static interface StringInBoolOutLambda { boolean apply(double  a); }
+  // Int Out
+  @FunctionalInterface
+  public static interface BoolInIntOutLambda   { int apply(boolean a); }
+  @FunctionalInterface
+  public static interface IntInIntOutLambda    { int apply(int     a); }
+  @FunctionalInterface
+  public static interface LongInIntOutLambda   { int apply(long    a); }
+  @FunctionalInterface
+  public static interface FloatInIntOutLambda  { int apply(float   a); }
+  @FunctionalInterface
+  public static interface DoubleInIntOutLambda { int apply(double  a); }
+  @FunctionalInterface
+  public static interface StringInIntOutLambda { int apply(double  a); }
+  // Long Out
+  @FunctionalInterface
+  public static interface BoolInLongOutLambda   { long apply(boolean a); }
+  @FunctionalInterface
+  public static interface IntInLongOutLambda    { long apply(int     a); }
+  @FunctionalInterface
+  public static interface LongInLongOutLambda   { long apply(long    a); }
+  @FunctionalInterface
+  public static interface FloatInLongOutLambda  { long apply(float   a); }
+  @FunctionalInterface
+  public static interface DoubleInLongOutLambda { long apply(double  a); }
+  @FunctionalInterface
+  public static interface StringInLongOutLambda { long apply(double  a); }
+  // Float Out
+  @FunctionalInterface
+  public static interface BoolInFloatOutLambda   { float apply(boolean a); }
+  @FunctionalInterface
+  public static interface IntInFloatOutLambda    { float apply(int     a); }
+  @FunctionalInterface
+  public static interface LongInFloatOutLambda   { float apply(long    a); }
+  @FunctionalInterface
+  public static interface FloatInFloatOutLambda  { float apply(float   a); }
+  @FunctionalInterface
+  public static interface DoubleInFloatOutLambda { float apply(double  a); }
+  @FunctionalInterface
+  public static interface StringInFloatOutLambda { float apply(String  a); }
+  //Double Out
+  @FunctionalInterface
+  public static interface BoolInDoubleOutLambda   { double apply(boolean a); }
+  @FunctionalInterface
+  public static interface IntInDoubleOutLambda    { double apply(int     a); }
+  @FunctionalInterface
+  public static interface LongInDoubleOutLambda   { double apply(long    a); }
+  @FunctionalInterface
+  public static interface FloatInDoubleOutLambda  { double apply(float   a); }
+  @FunctionalInterface
+  public static interface DoubleInDoubleOutLambda { double apply(double  a); }
+  @FunctionalInterface
+  public static interface StringInDoubleOutLambda { double apply(String  a); }
+  //String Out
+  @FunctionalInterface
+  public static interface BoolInStringOutLambda   { String apply(boolean a); }
+  @FunctionalInterface
+  public static interface IntInStringOutLambda    { String apply(int     a); }
+  @FunctionalInterface
+  public static interface LongInStringOutLambda   { String apply(long    a); }
+  @FunctionalInterface
+  public static interface FloatInStringOutLambda  { String apply(float   a); }
+  @FunctionalInterface
+  public static interface DoubleInStringOutLambda { String apply(double  a); }
+  @FunctionalInterface
+  public static interface StringInStringOutLambda { String apply(String  a); }
+  
+  /*
+   * Two Parameters
+   */
+  //Boolean Out
+  @FunctionalInterface
+  public static interface TwoBoolInBoolOutLambda   { boolean apply(boolean a, boolean b); }
+  @FunctionalInterface
+  public static interface TwoIntInBoolOutLambda    { boolean apply(int     a, int     b); }
+  @FunctionalInterface
+  public static interface TwoLongInBoolOutLambda   { boolean apply(long    a, long    b); }
+  @FunctionalInterface
+  public static interface TwoFloatInBoolOutLambda  { boolean apply(float   a, float   b); }
+  @FunctionalInterface
+  public static interface TwoDoubleInBoolOutLambda { boolean apply(double  a, double  b); }
+  @FunctionalInterface
+  public static interface TwoStringInBoolOutLambda { boolean apply(double  a, double  b); }
+  //Int Out
+  @FunctionalInterface
+  public static interface TwoBoolInIntOutLambda   { int apply(boolean a, boolean b); }
+  @FunctionalInterface
+  public static interface TwoIntInIntOutLambda    { int apply(int     a, int     b); }
+  @FunctionalInterface
+  public static interface TwoLongInIntOutLambda   { int apply(long    a, long    b); }
+  @FunctionalInterface
+  public static interface TwoFloatInIntOutLambda  { int apply(float   a, float   b); }
+  @FunctionalInterface
+  public static interface TwoDoubleInIntOutLambda { int apply(double  a, double  b); }
+  @FunctionalInterface
+  public static interface TwoStringInIntOutLambda { int apply(double  a, double  b); }
+  //Long Out
+  @FunctionalInterface
+  public static interface TwoBoolInLongOutLambda   { long apply(boolean a, boolean b); }
+  @FunctionalInterface
+  public static interface TwoIntInLongOutLambda    { long apply(int     a, int     b); }
+  @FunctionalInterface
+  public static interface TwoLongInLongOutLambda   { long apply(long    a, long    b); }
+  @FunctionalInterface
+  public static interface TwoFloatInLongOutLambda  { long apply(float   a, float   b); }
+  @FunctionalInterface
+  public static interface TwoDoubleInLongOutLambda { long apply(double  a, double  b); }
+  @FunctionalInterface
+  public static interface TwoStringInLongOutLambda { long apply(double  a, double  b); }
+  //Float Out
+  @FunctionalInterface
+  public static interface TwoBoolInFloatOutLambda   { float apply(boolean a, boolean b); }
+  @FunctionalInterface
+  public static interface TwoIntInFloatOutLambda    { float apply(int     a, int     b); }
+  @FunctionalInterface
+  public static interface TwoLongInFloatOutLambda   { float apply(long    a, long    b); }
+  @FunctionalInterface
+  public static interface TwoFloatInFloatOutLambda  { float apply(float   a, float   b); }
+  @FunctionalInterface
+  public static interface TwoDoubleInFloatOutLambda { float apply(double  a, double  b); }
+  @FunctionalInterface
+  public static interface TwoStringInFloatOutLambda { float apply(String  a, String  b); }
+  //Double Out
+  @FunctionalInterface
+  public static interface TwoBoolInDoubleOutLambda   { double apply(boolean a, boolean b); }
+  @FunctionalInterface
+  public static interface TwoIntInDoubleOutLambda    { double apply(int     a, int     b); }
+  @FunctionalInterface
+  public static interface TwoLongInDoubleOutLambda   { double apply(long    a, long    b); }
+  @FunctionalInterface
+  public static interface TwoFloatInDoubleOutLambda  { double apply(float   a, float   b); }
+  @FunctionalInterface
+  public static interface TwoDoubleInDoubleOutLambda { double apply(double  a, double  b); }
+  @FunctionalInterface
+  public static interface TwoStringInDoubleOutLambda { double apply(String  a, String  b); }
+  //String Out
+  @FunctionalInterface
+  public static interface TwoBoolInStringOutLambda   { String apply(boolean a, boolean b); }
+  @FunctionalInterface
+  public static interface TwoIntInStringOutLambda    { String apply(int     a, int     b); }
+  @FunctionalInterface
+  public static interface TwoLongInStringOutLambda   { String apply(long    a, long    b); }
+  @FunctionalInterface
+  public static interface TwoFloatInStringOutLambda  { String apply(float   a, float   b); }
+  @FunctionalInterface
+  public static interface TwoDoubleInStringOutLambda { String apply(double  a, double  b); }
+  @FunctionalInterface
+  public static interface TwoStringInStringOutLambda { String apply(String  a, String  b); }
+} 
+class BooleanValueInBooleanValueOutFunction extends AbstractBooleanValue {
+  private final BooleanValue param;
+  private final BoolInBoolOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanValueInBooleanValueOutFunction(String name, BoolInBoolOutLambda lambda, BooleanValue param) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  private boolean exists = false;
+
+  @Override
+  public boolean getBoolean() {
+    boolean value = lambda.apply(param.getBoolean());
+    exists = param.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanStreamInBooleanStreamOutFunction extends AbstractBooleanValueStream {
+  private final BooleanValueStream param;
+  private final BoolInBoolOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanStreamInBooleanStreamOutFunction(String name, BoolInBoolOutLambda lambda, BooleanValueStream param) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    param.streamBooleans(value -> cons.accept(lambda.apply(value)));
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanStreamInBooleanValueOutFunction extends AbstractBooleanValue implements BooleanConsumer {
+  private final BooleanValueStream param;
+  private final TwoBoolInBoolOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanStreamInBooleanValueOutFunction(String name, TwoBoolInBoolOutLambda lambda, BooleanValueStream param) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  private boolean exists = false;
+  private boolean value;
+
+  @Override
+  public boolean getBoolean() {
+    exists = false;
+    param.streamBooleans(this);
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  public void accept(boolean paramValue) {
+    if (!exists) {
+      exists = true;
+      value = paramValue;
+    } else {
+      value = lambda.apply(value, paramValue);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class TwoBooleanValueInBooleanValueOutFunction extends AbstractBooleanValue {
+  private final BooleanValue param1;
+  private final BooleanValue param2;
+  private final TwoBoolInBoolOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public TwoBooleanValueInBooleanValueOutFunction(String name, TwoBoolInBoolOutLambda lambda, BooleanValue param1, BooleanValue param2) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param1 = param1;
+    this.param2 = param2;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param1,param2);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param1,param2);
+  }
+  
+  private boolean exists = false;
+
+  @Override
+  public boolean getBoolean() {
+    boolean value = lambda.apply(param1.getBoolean(), param2.getBoolean());
+    exists = param1.exists() && param2.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanValueBooleanStreamInBooleanStreamOutFunction extends AbstractBooleanValueStream {
+  private final BooleanValue param1;
+  private final BooleanValueStream param2;
+  private final TwoBoolInBoolOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanValueBooleanStreamInBooleanStreamOutFunction(String name, TwoBoolInBoolOutLambda lambda, BooleanValue param1, BooleanValueStream param2) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param1 = param1;
+    this.param2 = param2;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param1,param2);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param1,param2);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    boolean value1 = param1.getBoolean();
+    if (param1.exists()) {
+      param2.streamBooleans(value2 -> cons.accept(lambda.apply(value1,value2)));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanStreamBooleanValueInBooleanStreamOutFunction extends AbstractBooleanValueStream {
+  private final BooleanValueStream param1;
+  private final BooleanValue param2;
+  private final TwoBoolInBoolOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanStreamBooleanValueInBooleanStreamOutFunction(String name, TwoBoolInBoolOutLambda lambda, BooleanValueStream param1, BooleanValue param2) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param1 = param1;
+    this.param2 = param2;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param1,param2);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param1,param2);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    boolean value2 = param2.getBoolean();
+    if (param2.exists()) {
+      param1.streamBooleans(value1 -> cons.accept(lambda.apply(value1,value2)));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+abstract class MultiBooleanValueInBooleanValueOutFunction extends AbstractBooleanValue {
+  protected final BooleanValue[] params;
+  protected final TwoBoolInBoolOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public MultiBooleanValueInBooleanValueOutFunction(String name, TwoBoolInBoolOutLambda lambda, BooleanValue[] params) {
+    this.name = name;
+    this.lambda = lambda;
+    this.params = params;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,params);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,params);
+  }
+  
+  protected boolean exists = false;
+  protected boolean temp;
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class MultiBooleanValueInBooleanValueOutRequireAllFunction extends MultiBooleanValueInBooleanValueOutFunction {
+  
+  public MultiBooleanValueInBooleanValueOutRequireAllFunction(String name, TwoBoolInBoolOutLambda lambda, BooleanValue[] params) {
+    super(name, lambda, params);
+  }
+  
+  @Override
+  public boolean getBoolean() {
+    boolean value = params[0].getBoolean();
+    exists = params[0].exists();
+    for (int i = 1; i < params.length && exists; ++i) {
+      value = lambda.apply(value, params[i].getBoolean());
+      exists = params[i].exists();
+    }
+    return value;
+  }
+}
+class MultiBooleanValueInBooleanValueOutRequireOneFunction extends MultiBooleanValueInBooleanValueOutFunction {
+  
+  public MultiBooleanValueInBooleanValueOutRequireOneFunction(String name, TwoBoolInBoolOutLambda lambda, BooleanValue[] params) {
+    super(name, lambda, params);
+  }
+  
+  @Override
+  public boolean getBoolean() {
+    int i = -1;
+    boolean value = false;
+    exists = false;
+    while (++i < params.length) {
+      value = params[i].getBoolean();
+      exists = params[i].exists();
+      if (exists) {
+        break;
+      }
+    }
+    while (++i < params.length) {
+      temp = params[i].getBoolean();
+      if (params[i].exists()) {
+        value = lambda.apply(value, temp);
+      }
+    }
+    return value;
+  }
+}
+class IntValueInIntValueOutFunction extends AbstractIntValue {
+  private final IntValue param;
+  private final IntInIntOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntValueInIntValueOutFunction(String name, IntInIntOutLambda lambda, IntValue param) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  private boolean exists = false;
+
+  @Override
+  public int getInt() {
+    int value = lambda.apply(param.getInt());
+    exists = param.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntStreamInIntStreamOutFunction extends AbstractIntValueStream {
+  private final IntValueStream param;
+  private final IntInIntOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntStreamInIntStreamOutFunction(String name, IntInIntOutLambda lambda, IntValueStream param) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  @Override
+  public void streamInts(IntConsumer cons) {
+    param.streamInts(value -> cons.accept(lambda.apply(value)));
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntStreamInIntValueOutFunction extends AbstractIntValue implements IntConsumer {
+  private final IntValueStream param;
+  private final TwoIntInIntOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntStreamInIntValueOutFunction(String name, TwoIntInIntOutLambda lambda, IntValueStream param) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  private boolean exists = false;
+  private int value;
+
+  @Override
+  public int getInt() {
+    exists = false;
+    param.streamInts(this);
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  public void accept(int paramValue) {
+    if (!exists) {
+      exists = true;
+      value = paramValue;
+    } else {
+      value = lambda.apply(value, paramValue);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class TwoIntValueInIntValueOutFunction extends AbstractIntValue {
+  private final IntValue param1;
+  private final IntValue param2;
+  private final TwoIntInIntOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public TwoIntValueInIntValueOutFunction(String name, TwoIntInIntOutLambda lambda, IntValue param1, IntValue param2) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param1 = param1;
+    this.param2 = param2;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param1,param2);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param1,param2);
+  }
+  
+  private boolean exists = false;
+
+  @Override
+  public int getInt() {
+    int value = lambda.apply(param1.getInt(), param2.getInt());
+    exists = param1.exists() && param2.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntValueIntStreamInIntStreamOutFunction extends AbstractIntValueStream {
+  private final IntValue param1;
+  private final IntValueStream param2;
+  private final TwoIntInIntOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntValueIntStreamInIntStreamOutFunction(String name, TwoIntInIntOutLambda lambda, IntValue param1, IntValueStream param2) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param1 = param1;
+    this.param2 = param2;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param1,param2);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param1,param2);
+  }
+
+  @Override
+  public void streamInts(IntConsumer cons) {
+    int value1 = param1.getInt();
+    if (param1.exists()) {
+      param2.streamInts(value2 -> cons.accept(lambda.apply(value1,value2)));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntStreamIntValueInIntStreamOutFunction extends AbstractIntValueStream {
+  private final IntValueStream param1;
+  private final IntValue param2;
+  private final TwoIntInIntOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntStreamIntValueInIntStreamOutFunction(String name, TwoIntInIntOutLambda lambda, IntValueStream param1, IntValue param2) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param1 = param1;
+    this.param2 = param2;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param1,param2);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param1,param2);
+  }
+
+  @Override
+  public void streamInts(IntConsumer cons) {
+    int value2 = param2.getInt();
+    if (param2.exists()) {
+      param1.streamInts(value1 -> cons.accept(lambda.apply(value1,value2)));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+abstract class MultiIntValueInIntValueOutFunction extends AbstractIntValue {
+  protected final IntValue[] params;
+  protected final TwoIntInIntOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public MultiIntValueInIntValueOutFunction(String name, TwoIntInIntOutLambda lambda, IntValue[] params) {
+    this.name = name;
+    this.lambda = lambda;
+    this.params = params;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,params);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,params);
+  }
+  
+  protected boolean exists = false;
+  protected int temp;
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class MultiIntValueInIntValueOutRequireAllFunction extends MultiIntValueInIntValueOutFunction {
+  
+  public MultiIntValueInIntValueOutRequireAllFunction(String name, TwoIntInIntOutLambda lambda, IntValue[] params) {
+    super(name, lambda, params);
+  }
+  
+  @Override
+  public int getInt() {
+    int value = params[0].getInt();
+    exists = params[0].exists();
+    for (int i = 1; i < params.length && exists; ++i) {
+      value = lambda.apply(value, params[i].getInt());
+      exists = params[i].exists();
+    }
+    return value;
+  }
+}
+class MultiIntValueInIntValueOutRequireOneFunction extends MultiIntValueInIntValueOutFunction {
+  
+  public MultiIntValueInIntValueOutRequireOneFunction(String name, TwoIntInIntOutLambda lambda, IntValue[] params) {
+    super(name, lambda, params);
+  }
+  
+  @Override
+  public int getInt() {
+    int i = -1;
+    int value = 0;
+    exists = false;
+    while (++i < params.length) {
+      value = params[i].getInt();
+      exists = params[i].exists();
+      if (exists) {
+        break;
+      }
+    }
+    while (++i < params.length) {
+      temp = params[i].getInt();
+      if (params[i].exists()) {
+        value = lambda.apply(value, temp);
+      }
+    }
+    return value;
+  }
+}
+class LongValueInLongValueOutFunction extends AbstractLongValue {
+  private final LongValue param;
+  private final LongInLongOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongValueInLongValueOutFunction(String name, LongInLongOutLambda lambda, LongValue param) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  private boolean exists = false;
+
+  @Override
+  public long getLong() {
+    long value = lambda.apply(param.getLong());
+    exists = param.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongStreamInLongStreamOutFunction extends AbstractLongValueStream {
+  private final LongValueStream param;
+  private final LongInLongOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongStreamInLongStreamOutFunction(String name, LongInLongOutLambda lambda, LongValueStream param) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    param.streamLongs(value -> cons.accept(lambda.apply(value)));
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongStreamInLongValueOutFunction extends AbstractLongValue implements LongConsumer {
+  private final LongValueStream param;
+  private final TwoLongInLongOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongStreamInLongValueOutFunction(String name, TwoLongInLongOutLambda lambda, LongValueStream param) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  private boolean exists = false;
+  private long value;
+
+  @Override
+  public long getLong() {
+    exists = false;
+    param.streamLongs(this);
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  public void accept(long paramValue) {
+    if (!exists) {
+      exists = true;
+      value = paramValue;
+    } else {
+      value = lambda.apply(value, paramValue);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class TwoLongValueInLongValueOutFunction extends AbstractLongValue {
+  private final LongValue param1;
+  private final LongValue param2;
+  private final TwoLongInLongOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public TwoLongValueInLongValueOutFunction(String name, TwoLongInLongOutLambda lambda, LongValue param1, LongValue param2) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param1 = param1;
+    this.param2 = param2;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param1,param2);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param1,param2);
+  }
+  
+  private boolean exists = false;
+
+  @Override
+  public long getLong() {
+    long value = lambda.apply(param1.getLong(), param2.getLong());
+    exists = param1.exists() && param2.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongValueLongStreamInLongStreamOutFunction extends AbstractLongValueStream {
+  private final LongValue param1;
+  private final LongValueStream param2;
+  private final TwoLongInLongOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongValueLongStreamInLongStreamOutFunction(String name, TwoLongInLongOutLambda lambda, LongValue param1, LongValueStream param2) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param1 = param1;
+    this.param2 = param2;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param1,param2);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param1,param2);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    long value1 = param1.getLong();
+    if (param1.exists()) {
+      param2.streamLongs(value2 -> cons.accept(lambda.apply(value1,value2)));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongStreamLongValueInLongStreamOutFunction extends AbstractLongValueStream {
+  private final LongValueStream param1;
+  private final LongValue param2;
+  private final TwoLongInLongOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongStreamLongValueInLongStreamOutFunction(String name, TwoLongInLongOutLambda lambda, LongValueStream param1, LongValue param2) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param1 = param1;
+    this.param2 = param2;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param1,param2);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param1,param2);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    long value2 = param2.getLong();
+    if (param2.exists()) {
+      param1.streamLongs(value1 -> cons.accept(lambda.apply(value1,value2)));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+abstract class MultiLongValueInLongValueOutFunction extends AbstractLongValue {
+  protected final LongValue[] params;
+  protected final TwoLongInLongOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public MultiLongValueInLongValueOutFunction(String name, TwoLongInLongOutLambda lambda, LongValue[] params) {
+    this.name = name;
+    this.lambda = lambda;
+    this.params = params;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,params);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,params);
+  }
+  
+  protected boolean exists = false;
+  protected long temp;
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class MultiLongValueInLongValueOutRequireAllFunction extends MultiLongValueInLongValueOutFunction {
+  
+  public MultiLongValueInLongValueOutRequireAllFunction(String name, TwoLongInLongOutLambda lambda, LongValue[] params) {
+    super(name, lambda, params);
+  }
+  
+  @Override
+  public long getLong() {
+    long value = params[0].getLong();
+    exists = params[0].exists();
+    for (int i = 1; i < params.length && exists; ++i) {
+      value = lambda.apply(value, params[i].getLong());
+      exists = params[i].exists();
+    }
+    return value;
+  }
+}
+class MultiLongValueInLongValueOutRequireOneFunction extends MultiLongValueInLongValueOutFunction {
+  
+  public MultiLongValueInLongValueOutRequireOneFunction(String name, TwoLongInLongOutLambda lambda, LongValue[] params) {
+    super(name, lambda, params);
+  }
+  
+  @Override
+  public long getLong() {
+    int i = -1;
+    long value = 0;
+    exists = false;
+    while (++i < params.length) {
+      value = params[i].getLong();
+      exists = params[i].exists();
+      if (exists) {
+        break;
+      }
+    }
+    while (++i < params.length) {
+      temp = params[i].getLong();
+      if (params[i].exists()) {
+        value = lambda.apply(value, temp);
+      }
+    }
+    return value;
+  }
+}
+class FloatValueInFloatValueOutFunction extends AbstractFloatValue {
+  private final FloatValue param;
+  private final FloatInFloatOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatValueInFloatValueOutFunction(String name, FloatInFloatOutLambda lambda, FloatValue param) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  private boolean exists = false;
+
+  @Override
+  public float getFloat() {
+    float value = lambda.apply(param.getFloat());
+    exists = param.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatStreamInFloatStreamOutFunction extends AbstractFloatValueStream {
+  private final FloatValueStream param;
+  private final FloatInFloatOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatStreamInFloatStreamOutFunction(String name, FloatInFloatOutLambda lambda, FloatValueStream param) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    param.streamFloats(value -> cons.accept(lambda.apply(value)));
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatStreamInFloatValueOutFunction extends AbstractFloatValue implements FloatConsumer {
+  private final FloatValueStream param;
+  private final TwoFloatInFloatOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatStreamInFloatValueOutFunction(String name, TwoFloatInFloatOutLambda lambda, FloatValueStream param) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  private boolean exists = false;
+  private float value;
+
+  @Override
+  public float getFloat() {
+    exists = false;
+    param.streamFloats(this);
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  public void accept(float paramValue) {
+    if (!exists) {
+      exists = true;
+      value = paramValue;
+    } else {
+      value = lambda.apply(value, paramValue);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class TwoFloatValueInFloatValueOutFunction extends AbstractFloatValue {
+  private final FloatValue param1;
+  private final FloatValue param2;
+  private final TwoFloatInFloatOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public TwoFloatValueInFloatValueOutFunction(String name, TwoFloatInFloatOutLambda lambda, FloatValue param1, FloatValue param2) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param1 = param1;
+    this.param2 = param2;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param1,param2);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param1,param2);
+  }
+  
+  private boolean exists = false;
+
+  @Override
+  public float getFloat() {
+    float value = lambda.apply(param1.getFloat(), param2.getFloat());
+    exists = param1.exists() && param2.exists();
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatValueFloatStreamInFloatStreamOutFunction extends AbstractFloatValueStream {
+  private final FloatValue param1;
+  private final FloatValueStream param2;
+  private final TwoFloatInFloatOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatValueFloatStreamInFloatStreamOutFunction(String name, TwoFloatInFloatOutLambda lambda, FloatValue param1, FloatValueStream param2) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param1 = param1;
+    this.param2 = param2;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param1,param2);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param1,param2);
+  }
+
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    float value1 = param1.getFloat();
+    if (param1.exists()) {
+      param2.streamFloats(value2 -> cons.accept(lambda.apply(value1,value2)));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatStreamFloatValueInFloatStreamOutFunction extends AbstractFloatValueStream {
+  private final FloatValueStream param1;
+  private final FloatValue param2;
+  private final TwoFloatInFloatOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatStreamFloatValueInFloatStreamOutFunction(String name, TwoFloatInFloatOutLambda lambda, FloatValueStream param1, FloatValue param2) {
+    this.name = name;
+    this.lambda = lambda;
+    this.param1 = param1;
+    this.param2 = param2;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param1,param2);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param1,param2);
+  }
+
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    float value2 = param2.getFloat();
+    if (param2.exists()) {
+      param1.streamFloats(value1 -> cons.accept(lambda.apply(value1,value2)));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+abstract class MultiFloatValueInFloatValueOutFunction extends AbstractFloatValue {
+  protected final FloatValue[] params;
+  protected final TwoFloatInFloatOutLambda lambda;
+  private final String name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public MultiFloatValueInFloatValueOutFunction(String name, TwoFloatInFloatOutLambda lambda, FloatValue[] params) {
+    this.name = name;
+    this.lambda = lambda;
+    this.params = params;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,params);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,params);
+  }
+  
+  protected boolean exists = false;
+  protected float temp;
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  p

<TRUNCATED>

[08/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/ReductionCheckedDataReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/ReductionCheckedDataReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/ReductionCheckedDataReservation.java
new file mode 100644
index 0000000..73287fe
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/ReductionCheckedDataReservation.java
@@ -0,0 +1,35 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.util.function.BooleanSupplier;
+
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+
+/**
+ * A reservation allows a {@link ReductionDataCollector} to specify a piece of data it needs to export from the shard.
+ * This data may, or may not, exist. The {@link ReductionDataCollector} need not check while importing/exporting since
+ * the Reader/Writers handle all checking.
+ */
+public abstract class ReductionCheckedDataReservation<A, E> extends ReductionDataReservation<A, E> {
+  protected final BooleanSupplier exists;
+  
+  protected ReductionCheckedDataReservation(A applier, E extractor, BooleanSupplier exists) {
+    super(applier, extractor);
+    this.exists = exists;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/ReductionDataArrayReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/ReductionDataArrayReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/ReductionDataArrayReservation.java
new file mode 100644
index 0000000..cbeece6
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/ReductionDataArrayReservation.java
@@ -0,0 +1,36 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.util.function.IntConsumer;
+import java.util.function.IntSupplier;
+
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+
+/**
+ * A reservation allows a {@link ReductionDataCollector} to specify an array of data it needs to export from the shard.
+ */
+public abstract class ReductionDataArrayReservation<A, E> extends ReductionDataReservation<A, E> {
+  protected final IntConsumer sizeApplier;
+  protected final IntSupplier sizeExtractor;
+  
+  protected ReductionDataArrayReservation(A applier, IntConsumer sizeApplier, E extractor, IntSupplier sizeExtractor) {
+    super(applier, extractor);
+    this.sizeApplier = sizeApplier;
+    this.sizeExtractor = sizeExtractor;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/ReductionDataReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/ReductionDataReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/ReductionDataReservation.java
new file mode 100644
index 0000000..903016e
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/ReductionDataReservation.java
@@ -0,0 +1,53 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.analytics.stream.reservation.read.ReductionDataReader;
+import org.apache.solr.analytics.stream.reservation.write.ReductionDataWriter;
+
+/**
+ * A reservation allows a {@link ReductionDataCollector} to specify a piece of data it needs to export from the shard.
+ */
+public abstract class ReductionDataReservation<A, E> {
+  protected final A applier;
+  protected final E extractor;
+  
+  protected ReductionDataReservation(A applier, E extractor) {
+    this.applier = applier;
+    this.extractor = extractor;
+  }
+  
+  /**
+   * Generate a {@link ReductionDataReader} that merges the set of data this reservation represents.
+   * 
+   * @param input the shard input stream
+   * @return a reader from the given input
+   */
+  public abstract ReductionDataReader<A> createReadStream(DataInput input);
+  
+  /**
+   * Generate a {@link ReductionDataWriter} that exports the set of data this reservation represents.
+   * 
+   * @param output the shard output stream
+   * @return a writer to the given output
+   */
+  public abstract ReductionDataWriter<E> createWriteStream(DataOutput output);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/StringArrayReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/StringArrayReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/StringArrayReservation.java
new file mode 100644
index 0000000..c3c6989
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/StringArrayReservation.java
@@ -0,0 +1,45 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+import org.apache.solr.analytics.stream.reservation.read.StringDataArrayReader;
+import org.apache.solr.analytics.stream.reservation.write.StringDataArrayWriter;
+
+import java.util.function.IntConsumer;
+import java.util.function.IntSupplier;
+
+public class StringArrayReservation extends ReductionDataArrayReservation<Consumer<String>, Supplier<String>> {
+  
+  public StringArrayReservation(Consumer<String> applier, IntConsumer sizeApplier, Supplier<String> extractor, IntSupplier sizeExtractor) {
+    super(applier, sizeApplier, extractor, sizeExtractor);
+  }
+
+  @Override
+  public StringDataArrayReader createReadStream(DataInput input) {
+    return new StringDataArrayReader(input, applier, sizeApplier);
+  }
+
+  @Override
+  public StringDataArrayWriter createWriteStream(DataOutput output) {
+    return new StringDataArrayWriter(output, extractor, sizeExtractor);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/StringCheckedReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/StringCheckedReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/StringCheckedReservation.java
new file mode 100644
index 0000000..29c3614
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/StringCheckedReservation.java
@@ -0,0 +1,44 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.Supplier;
+
+import org.apache.solr.analytics.stream.reservation.read.StringCheckedDataReader;
+import org.apache.solr.analytics.stream.reservation.write.StringCheckedDataWriter;
+
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+
+public class StringCheckedReservation extends ReductionCheckedDataReservation<Consumer<String>, Supplier<String>> {
+  
+  public StringCheckedReservation(Consumer<String> applier, Supplier<String> extractor, BooleanSupplier exists) {
+    super(applier, extractor, exists);
+  }
+
+  @Override
+  public StringCheckedDataReader createReadStream(DataInput input) {
+    return new StringCheckedDataReader(input, applier);
+  }
+
+  @Override
+  public StringCheckedDataWriter createWriteStream(DataOutput output) {
+    return new StringCheckedDataWriter(output, extractor, exists);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/StringReservation.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/StringReservation.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/StringReservation.java
new file mode 100644
index 0000000..2601874
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/StringReservation.java
@@ -0,0 +1,43 @@
+/*
+ * 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.solr.analytics.stream.reservation;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.util.function.Supplier;
+
+import org.apache.solr.analytics.stream.reservation.read.StringDataReader;
+import org.apache.solr.analytics.stream.reservation.write.StringDataWriter;
+
+import java.util.function.Consumer;
+
+public class StringReservation extends ReductionDataReservation<Consumer<String>, Supplier<String>> {
+  
+  public StringReservation(Consumer<String> applier, Supplier<String> extractor) {
+    super(applier, extractor);
+  }
+
+  @Override
+  public StringDataReader createReadStream(DataInput input) {
+    return new StringDataReader(input, applier);
+  }
+
+  @Override
+  public StringDataWriter createWriteStream(DataOutput output) {
+    return new StringDataWriter(output, extractor);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/package-info.java
new file mode 100644
index 0000000..bfd832d
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+ 
+/** 
+ * Reservation classes for a single type of data being stored by one Reduction Data Collector.
+ * These reservations are imported/exported between shards during the streaming process.
+ */
+package org.apache.solr.analytics.stream.reservation;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/BooleanCheckedDataReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/BooleanCheckedDataReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/BooleanCheckedDataReader.java
new file mode 100644
index 0000000..49f6f00
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/BooleanCheckedDataReader.java
@@ -0,0 +1,33 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+
+public class BooleanCheckedDataReader extends ReductionCheckedDataReader<BooleanConsumer> {
+  
+  public BooleanCheckedDataReader(DataInput inputStream, BooleanConsumer applier) {
+    super(inputStream, applier);
+  }
+  @Override
+  public void checkedRead() throws IOException {
+    applier.accept(inputStream.readBoolean());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/BooleanDataArrayReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/BooleanDataArrayReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/BooleanDataArrayReader.java
new file mode 100644
index 0000000..ee929f4
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/BooleanDataArrayReader.java
@@ -0,0 +1,36 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.function.IntConsumer;
+
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+
+public class BooleanDataArrayReader extends ReductionDataArrayReader<BooleanConsumer> {
+  
+  public BooleanDataArrayReader(DataInput inputStream, BooleanConsumer applier, IntConsumer signal) {
+    super(inputStream, applier, signal);
+  }
+  @Override
+  public void read(int size) throws IOException {
+    for (int i = 0; i < size; ++i) {
+      applier.accept(inputStream.readBoolean());
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/BooleanDataReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/BooleanDataReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/BooleanDataReader.java
new file mode 100644
index 0000000..98e116d
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/BooleanDataReader.java
@@ -0,0 +1,33 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+
+public class BooleanDataReader extends ReductionDataReader<BooleanConsumer> {
+  
+  public BooleanDataReader(DataInput inputStream, BooleanConsumer applier) {
+    super(inputStream, applier);
+  }
+  @Override
+  public void read() throws IOException {
+    applier.accept(inputStream.readBoolean());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/DoubleCheckedDataReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/DoubleCheckedDataReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/DoubleCheckedDataReader.java
new file mode 100644
index 0000000..ec6a6e4
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/DoubleCheckedDataReader.java
@@ -0,0 +1,32 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.function.DoubleConsumer;
+
+public class DoubleCheckedDataReader extends ReductionCheckedDataReader<DoubleConsumer> {
+  
+  public DoubleCheckedDataReader(DataInput inputStream, DoubleConsumer applier) {
+    super(inputStream, applier);
+  }
+  @Override
+  public void checkedRead() throws IOException {
+    applier.accept(inputStream.readDouble());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/DoubleDataArrayReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/DoubleDataArrayReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/DoubleDataArrayReader.java
new file mode 100644
index 0000000..5690a45
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/DoubleDataArrayReader.java
@@ -0,0 +1,35 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+
+public class DoubleDataArrayReader extends ReductionDataArrayReader<DoubleConsumer> {
+  
+  public DoubleDataArrayReader(DataInput inputStream, DoubleConsumer applier, IntConsumer signal) {
+    super(inputStream, applier, signal);
+  }
+  @Override
+  public void read(int size) throws IOException {
+    for (int i = 0; i < size; ++i) {
+      applier.accept(inputStream.readDouble());
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/DoubleDataReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/DoubleDataReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/DoubleDataReader.java
new file mode 100644
index 0000000..42de9bf
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/DoubleDataReader.java
@@ -0,0 +1,32 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.function.DoubleConsumer;
+
+public class DoubleDataReader extends ReductionDataReader<DoubleConsumer> {
+  
+  public DoubleDataReader(DataInput inputStream, DoubleConsumer applier) {
+    super(inputStream, applier);
+  }
+  @Override
+  public void read() throws IOException {
+    applier.accept(inputStream.readDouble());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/FloatCheckedDataReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/FloatCheckedDataReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/FloatCheckedDataReader.java
new file mode 100644
index 0000000..8139cfb
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/FloatCheckedDataReader.java
@@ -0,0 +1,33 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+
+import org.apache.solr.analytics.util.function.FloatConsumer;
+
+public class FloatCheckedDataReader extends ReductionCheckedDataReader<FloatConsumer> {
+  
+  public FloatCheckedDataReader(DataInput inputStream, FloatConsumer applier) {
+    super(inputStream, applier);
+  }
+  @Override
+  public void checkedRead() throws IOException {
+    applier.accept(inputStream.readFloat());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/FloatDataArrayReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/FloatDataArrayReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/FloatDataArrayReader.java
new file mode 100644
index 0000000..1157a11
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/FloatDataArrayReader.java
@@ -0,0 +1,36 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.function.IntConsumer;
+
+import org.apache.solr.analytics.util.function.FloatConsumer;
+
+public class FloatDataArrayReader extends ReductionDataArrayReader<FloatConsumer> {
+  
+  public FloatDataArrayReader(DataInput inputStream, FloatConsumer applier, IntConsumer signal) {
+    super(inputStream, applier, signal);
+  }
+  @Override
+  public void read(int size) throws IOException {
+    for (int i = 0; i < size; ++i) {
+      applier.accept(inputStream.readFloat());
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/FloatDataReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/FloatDataReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/FloatDataReader.java
new file mode 100644
index 0000000..63067cd
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/FloatDataReader.java
@@ -0,0 +1,33 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+
+import org.apache.solr.analytics.util.function.FloatConsumer;
+
+public class FloatDataReader extends ReductionDataReader<FloatConsumer> {
+  
+  public FloatDataReader(DataInput inputStream, FloatConsumer applier) {
+    super(inputStream, applier);
+  }
+  @Override
+  public void read() throws IOException {
+    applier.accept(inputStream.readFloat());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/IntCheckedDataReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/IntCheckedDataReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/IntCheckedDataReader.java
new file mode 100644
index 0000000..e926e3e
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/IntCheckedDataReader.java
@@ -0,0 +1,32 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.function.IntConsumer;
+
+public class IntCheckedDataReader extends ReductionCheckedDataReader<IntConsumer> {
+  
+  public IntCheckedDataReader(DataInput inputStream, IntConsumer applier) {
+    super(inputStream, applier);
+  }
+  @Override
+  public void checkedRead() throws IOException {
+    applier.accept(inputStream.readInt());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/IntDataArrayReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/IntDataArrayReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/IntDataArrayReader.java
new file mode 100644
index 0000000..a0c1d86
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/IntDataArrayReader.java
@@ -0,0 +1,34 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.function.IntConsumer;
+
+public class IntDataArrayReader extends ReductionDataArrayReader<IntConsumer> {
+  
+  public IntDataArrayReader(DataInput inputStream, IntConsumer applier, IntConsumer signal) {
+    super(inputStream, applier, signal);
+  }
+  @Override
+  public void read(int size) throws IOException {
+    for (int i = 0; i < size; ++i) {
+      applier.accept(inputStream.readInt());
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/IntDataReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/IntDataReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/IntDataReader.java
new file mode 100644
index 0000000..5b65055
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/IntDataReader.java
@@ -0,0 +1,32 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.function.IntConsumer;
+
+public class IntDataReader extends ReductionDataReader<IntConsumer> {
+  
+  public IntDataReader(DataInput inputStream, IntConsumer applier) {
+    super(inputStream, applier);
+  }
+  @Override
+  public void read() throws IOException {
+    applier.accept(inputStream.readInt());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/LongCheckedDataReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/LongCheckedDataReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/LongCheckedDataReader.java
new file mode 100644
index 0000000..7d9a7ff
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/LongCheckedDataReader.java
@@ -0,0 +1,32 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.function.LongConsumer;
+
+public class LongCheckedDataReader extends ReductionCheckedDataReader<LongConsumer> {
+  
+  public LongCheckedDataReader(DataInput inputStream, LongConsumer applier) {
+    super(inputStream, applier);
+  }
+  @Override
+  public void checkedRead() throws IOException {
+    applier.accept(inputStream.readLong());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/LongDataArrayReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/LongDataArrayReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/LongDataArrayReader.java
new file mode 100644
index 0000000..6c09f46
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/LongDataArrayReader.java
@@ -0,0 +1,35 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+public class LongDataArrayReader extends ReductionDataArrayReader<LongConsumer> {
+  
+  public LongDataArrayReader(DataInput inputStream, LongConsumer applier, IntConsumer signal) {
+    super(inputStream, applier, signal);
+  }
+  @Override
+  public void read(int size) throws IOException {
+    for (int i = 0; i < size; ++i) {
+      applier.accept(inputStream.readLong());
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/LongDataReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/LongDataReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/LongDataReader.java
new file mode 100644
index 0000000..bf4057d
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/LongDataReader.java
@@ -0,0 +1,32 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.function.LongConsumer;
+
+public class LongDataReader extends ReductionDataReader<LongConsumer> {
+  
+  public LongDataReader(DataInput inputStream, LongConsumer applier) {
+    super(inputStream, applier);
+  }
+  @Override
+  public void read() throws IOException {
+    applier.accept(inputStream.readLong());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/ReductionCheckedDataReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/ReductionCheckedDataReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/ReductionCheckedDataReader.java
new file mode 100644
index 0000000..ac98987
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/ReductionCheckedDataReader.java
@@ -0,0 +1,54 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+
+/**
+ * Abstract class to manage the reading and application of data from a {@link DataInput} stream.
+ * The data being read may not exist, so the reader first checks before reading.
+ */
+public abstract class ReductionCheckedDataReader<A> extends ReductionDataReader<A> {
+  
+  public ReductionCheckedDataReader(DataInput inputStream, A applier) {
+    super(inputStream, applier);
+  }
+  
+  @Override
+  /**
+   * Read a piece of data from the input stream and feed it to the applier.
+   * <br>
+   * First checks that the piece of data exists before reading.
+   * 
+   * @throws IOException if an exception occurs while reading from the input stream
+   */
+  public void read() throws IOException {
+    if (inputStream.readBoolean()) {
+      checkedRead();
+    }
+  }
+  
+  /**
+   * Read a piece of data from the input stream and feed it to the applier.
+   * <br>
+   * This piece of data is guaranteed to be there.
+   * 
+   * @throws IOException if an exception occurs while reading from the input stream
+   */
+  protected abstract void checkedRead() throws IOException;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/ReductionDataArrayReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/ReductionDataArrayReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/ReductionDataArrayReader.java
new file mode 100644
index 0000000..2a696d7
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/ReductionDataArrayReader.java
@@ -0,0 +1,54 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.function.IntConsumer;
+
+/**
+ * Abstract class to manage the reading and application of array data from a {@link DataInput} stream.
+ */
+public abstract class ReductionDataArrayReader<A> extends ReductionDataReader<A> {
+  protected final IntConsumer signal;
+  
+  public ReductionDataArrayReader(DataInput inputStream, A applier, IntConsumer signal) {
+    super(inputStream, applier);
+    
+    this.signal = signal;
+  }
+  
+  @Override
+  /**
+   * Read an array of data from the input stream and feed it to the applier, first signaling the size of the array.
+   * 
+   * @throws IOException if an exception occurs while reading from the input stream
+   */
+  public void read() throws IOException {
+    int size = inputStream.readInt();
+    signal.accept(size);
+    read(size);
+  }
+  
+  /**
+   * Read an array from the input stream, feeding each member to the applier.
+   * 
+   * @param size length of the array to read
+   * @throws IOException if an exception occurs while reading from the input stream
+   */
+  protected abstract void read(int size) throws IOException;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/ReductionDataReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/ReductionDataReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/ReductionDataReader.java
new file mode 100644
index 0000000..ec99621
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/ReductionDataReader.java
@@ -0,0 +1,40 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+
+/**
+ * Abstract class to manage the reading and application of data from a {@link DataInput} stream.
+ */
+public abstract class ReductionDataReader<A> {
+  protected final DataInput inputStream;
+  protected final A applier;
+  
+  public ReductionDataReader(DataInput inputStream, A applier) {
+    this.inputStream = inputStream;
+    this.applier = applier;
+  }
+  
+  /**
+   * Read a piece of data from the input stream and feed it to the applier.
+   * 
+   * @throws IOException if an exception occurs while reading from the input stream
+   */
+  public abstract void read() throws IOException;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/StringCheckedDataReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/StringCheckedDataReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/StringCheckedDataReader.java
new file mode 100644
index 0000000..400e990
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/StringCheckedDataReader.java
@@ -0,0 +1,32 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.function.Consumer;
+
+public class StringCheckedDataReader extends ReductionCheckedDataReader<Consumer<String>> {
+  
+  public StringCheckedDataReader(DataInput inputStream, Consumer<String> applier) {
+    super(inputStream, applier);
+  }
+  @Override
+  public void checkedRead() throws IOException {
+    applier.accept(inputStream.readUTF());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/StringDataArrayReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/StringDataArrayReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/StringDataArrayReader.java
new file mode 100644
index 0000000..048b84f
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/StringDataArrayReader.java
@@ -0,0 +1,35 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.IntConsumer;
+
+public class StringDataArrayReader extends ReductionDataArrayReader<Consumer<String>> {
+  
+  public StringDataArrayReader(DataInput inputStream, Consumer<String> applier, IntConsumer signal) {
+    super(inputStream, applier, signal);
+  }
+  @Override
+  public void read(int size) throws IOException {
+    for (int i = 0; i < size; ++i) {
+      applier.accept(inputStream.readUTF());
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/StringDataReader.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/StringDataReader.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/StringDataReader.java
new file mode 100644
index 0000000..d4d6bc4
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/StringDataReader.java
@@ -0,0 +1,34 @@
+/*
+ * 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.solr.analytics.stream.reservation.read;
+
+import java.io.DataInput;
+import java.io.IOException;
+import java.util.function.Consumer;
+
+public class StringDataReader extends ReductionDataReader<Consumer<String>> {
+  
+  public StringDataReader(DataInput inputStream, Consumer<String> applier) {
+    super(inputStream, applier);
+  }
+  @Override
+  public void read() throws IOException {
+    if (inputStream.readBoolean()) {
+      applier.accept(inputStream.readUTF());
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/package-info.java
new file mode 100644
index 0000000..26bb7c5
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/read/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+ 
+/** 
+ * Reading classes for a single type of data being stored by one Reduction Data Collector.
+ * These writers are used to import data from shards during the streaming process.
+ */
+package org.apache.solr.analytics.stream.reservation.read;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/BooleanCheckedDataWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/BooleanCheckedDataWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/BooleanCheckedDataWriter.java
new file mode 100644
index 0000000..6b4c1b7
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/BooleanCheckedDataWriter.java
@@ -0,0 +1,33 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.BooleanSupplier;
+
+public class BooleanCheckedDataWriter extends ReductionCheckedDataWriter<BooleanSupplier> {
+  
+  public BooleanCheckedDataWriter(DataOutput output, BooleanSupplier extractor, BooleanSupplier existsSupplier) {
+    super(output, extractor, existsSupplier);
+  }
+
+  @Override
+  public void checkedWrite() throws IOException {
+    output.writeBoolean(extractor.getAsBoolean());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/BooleanDataArrayWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/BooleanDataArrayWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/BooleanDataArrayWriter.java
new file mode 100644
index 0000000..c188770
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/BooleanDataArrayWriter.java
@@ -0,0 +1,36 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.BooleanSupplier;
+import java.util.function.IntSupplier;
+
+public class BooleanDataArrayWriter extends ReductionDataArrayWriter<BooleanSupplier> {
+
+  public BooleanDataArrayWriter(DataOutput output, BooleanSupplier extractor, IntSupplier sizeSupplier) {
+    super(output, extractor, sizeSupplier);
+  }
+  
+  @Override
+  public void write(int size) throws IOException {
+    for (int i = 0; i < size; ++i) {
+      output.writeBoolean(extractor.getAsBoolean());
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/BooleanDataWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/BooleanDataWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/BooleanDataWriter.java
new file mode 100644
index 0000000..f921a5d
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/BooleanDataWriter.java
@@ -0,0 +1,33 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.BooleanSupplier;
+
+public class BooleanDataWriter extends ReductionDataWriter<BooleanSupplier> {
+  
+  public BooleanDataWriter(DataOutput output, BooleanSupplier extractor) {
+    super(output, extractor);
+  }
+
+  @Override
+  public void write() throws IOException {
+    output.writeBoolean(extractor.getAsBoolean());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/DoubleCheckedDataWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/DoubleCheckedDataWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/DoubleCheckedDataWriter.java
new file mode 100644
index 0000000..376afee
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/DoubleCheckedDataWriter.java
@@ -0,0 +1,34 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.BooleanSupplier;
+import java.util.function.DoubleSupplier;
+
+public class DoubleCheckedDataWriter extends ReductionCheckedDataWriter<DoubleSupplier> {
+  
+  public DoubleCheckedDataWriter(DataOutput output, DoubleSupplier extractor, BooleanSupplier existsSupplier) {
+    super(output, extractor, existsSupplier);
+  }
+
+  @Override
+  public void checkedWrite() throws IOException {
+    output.writeDouble(extractor.getAsDouble());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/DoubleDataArrayWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/DoubleDataArrayWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/DoubleDataArrayWriter.java
new file mode 100644
index 0000000..830ceee
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/DoubleDataArrayWriter.java
@@ -0,0 +1,36 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.DoubleSupplier;
+import java.util.function.IntSupplier;
+
+public class DoubleDataArrayWriter extends ReductionDataArrayWriter<DoubleSupplier> {
+
+  public DoubleDataArrayWriter(DataOutput output, DoubleSupplier extractor, IntSupplier sizeSupplier) {
+    super(output, extractor, sizeSupplier);
+  }
+  
+  @Override
+  public void write(int size) throws IOException {
+    for (int i = 0; i < size; ++i) {
+      output.writeDouble(extractor.getAsDouble());
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/DoubleDataWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/DoubleDataWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/DoubleDataWriter.java
new file mode 100644
index 0000000..074b859
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/DoubleDataWriter.java
@@ -0,0 +1,33 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.DoubleSupplier;
+
+public class DoubleDataWriter extends ReductionDataWriter<DoubleSupplier> {
+  
+  public DoubleDataWriter(DataOutput output, DoubleSupplier extractor) {
+    super(output, extractor);
+  }
+
+  @Override
+  public void write() throws IOException {
+    output.writeDouble(extractor.getAsDouble());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/FloatCheckedDataWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/FloatCheckedDataWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/FloatCheckedDataWriter.java
new file mode 100644
index 0000000..69c74b9
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/FloatCheckedDataWriter.java
@@ -0,0 +1,35 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.BooleanSupplier;
+
+import org.apache.solr.analytics.util.function.FloatSupplier;
+
+public class FloatCheckedDataWriter extends ReductionCheckedDataWriter<FloatSupplier> {
+  
+  public FloatCheckedDataWriter(DataOutput output, FloatSupplier extractor, BooleanSupplier existsSupplier) {
+    super(output, extractor, existsSupplier);
+  }
+
+  @Override
+  public void checkedWrite() throws IOException {
+    output.writeFloat(extractor.getAsFloat());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/FloatDataArrayWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/FloatDataArrayWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/FloatDataArrayWriter.java
new file mode 100644
index 0000000..0fd0d25
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/FloatDataArrayWriter.java
@@ -0,0 +1,37 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.IntSupplier;
+
+import org.apache.solr.analytics.util.function.FloatSupplier;
+
+public class FloatDataArrayWriter extends ReductionDataArrayWriter<FloatSupplier> {
+
+  public FloatDataArrayWriter(DataOutput output, FloatSupplier extractor, IntSupplier sizeSupplier) {
+    super(output, extractor, sizeSupplier);
+  }
+  
+  @Override
+  public void write(int size) throws IOException {
+    for (int i = 0; i < size; ++i) {
+      output.writeFloat(extractor.getAsFloat());
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/FloatDataWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/FloatDataWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/FloatDataWriter.java
new file mode 100644
index 0000000..bc23f21
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/FloatDataWriter.java
@@ -0,0 +1,34 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+
+import org.apache.solr.analytics.util.function.FloatSupplier;
+
+public class FloatDataWriter extends ReductionDataWriter<FloatSupplier> {
+  
+  public FloatDataWriter(DataOutput output, FloatSupplier extractor) {
+    super(output, extractor);
+  }
+
+  @Override
+  public void write() throws IOException {
+    output.writeFloat(extractor.getAsFloat());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/IntCheckedDataWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/IntCheckedDataWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/IntCheckedDataWriter.java
new file mode 100644
index 0000000..fd55cf7
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/IntCheckedDataWriter.java
@@ -0,0 +1,34 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.BooleanSupplier;
+import java.util.function.IntSupplier;
+
+public class IntCheckedDataWriter extends ReductionCheckedDataWriter<IntSupplier> {
+  
+  public IntCheckedDataWriter(DataOutput output, IntSupplier extractor, BooleanSupplier existsSupplier) {
+    super(output, extractor, existsSupplier);
+  }
+
+  @Override
+  public void checkedWrite() throws IOException {
+    output.writeInt(extractor.getAsInt());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/IntDataArrayWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/IntDataArrayWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/IntDataArrayWriter.java
new file mode 100644
index 0000000..144b27f
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/IntDataArrayWriter.java
@@ -0,0 +1,35 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.IntSupplier;
+
+public class IntDataArrayWriter extends ReductionDataArrayWriter<IntSupplier> {
+
+  public IntDataArrayWriter(DataOutput output, IntSupplier extractor, IntSupplier sizeSupplier) {
+    super(output, extractor, sizeSupplier);
+  }
+  
+  @Override
+  public void write(int size) throws IOException {
+    for (int i = 0; i < size; ++i) {
+      output.writeInt(extractor.getAsInt());
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/IntDataWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/IntDataWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/IntDataWriter.java
new file mode 100644
index 0000000..fd2420a
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/IntDataWriter.java
@@ -0,0 +1,33 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.IntSupplier;
+
+public class IntDataWriter extends ReductionDataWriter<IntSupplier> {
+  
+  public IntDataWriter(DataOutput output, IntSupplier extractor) {
+    super(output, extractor);
+  }
+
+  @Override
+  public void write() throws IOException {
+    output.writeInt(extractor.getAsInt());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/LongCheckedDataWriter.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/LongCheckedDataWriter.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/LongCheckedDataWriter.java
new file mode 100644
index 0000000..e148e40
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/stream/reservation/write/LongCheckedDataWriter.java
@@ -0,0 +1,34 @@
+/*
+ * 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.solr.analytics.stream.reservation.write;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.function.BooleanSupplier;
+import java.util.function.LongSupplier;
+
+public class LongCheckedDataWriter extends ReductionCheckedDataWriter<LongSupplier> {
+  
+  public LongCheckedDataWriter(DataOutput output, LongSupplier extractor, BooleanSupplier existsSupplier) {
+    super(output, extractor, existsSupplier);
+  }
+
+  @Override
+  public void checkedWrite() throws IOException {
+    output.writeLong(extractor.getAsLong());
+  }
+}
\ No newline at end of file


[20/20] lucene-solr:master: SOLR-10123: Upgraded the Analytics Component to version 2.0

Posted by dp...@apache.org.
SOLR-10123: Upgraded the Analytics Component to version 2.0


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/d5963beb
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/d5963beb
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/d5963beb

Branch: refs/heads/master
Commit: d5963bebc43bdb712f2f1e9a29f944370f079b5f
Parents: 85a27a2
Author: Dennis Gove <dp...@gmail.com>
Authored: Wed Jun 28 09:39:47 2017 -0400
Committer: Dennis Gove <dp...@gmail.com>
Committed: Wed Jun 28 14:13:37 2017 -0400

----------------------------------------------------------------------
 solr/CHANGES.txt                                |    8 +
 .../apache/solr/analytics/AnalyticsDriver.java  |   82 +
 .../solr/analytics/AnalyticsExpression.java     |   64 +
 .../analytics/AnalyticsGroupingManager.java     |  239 ++
 .../solr/analytics/AnalyticsRequestManager.java |  279 ++
 .../solr/analytics/AnalyticsRequestParser.java  |  549 +++
 .../solr/analytics/ExpressionFactory.java       |  821 +++++
 .../analytics/accumulator/BasicAccumulator.java |  173 -
 .../accumulator/FacetingAccumulator.java        |  730 ----
 .../analytics/accumulator/ValueAccumulator.java |   40 -
 .../facet/FacetValueAccumulator.java            |   35 -
 .../facet/FieldFacetAccumulator.java            |  153 -
 .../facet/QueryFacetAccumulator.java            |   72 -
 .../facet/RangeFacetAccumulator.java            |   49 -
 .../accumulator/facet/package-info.java         |   24 -
 .../analytics/accumulator/package-info.java     |   23 -
 .../analytics/expression/BaseExpression.java    |   88 -
 .../expression/DualDelegateExpression.java      |   99 -
 .../solr/analytics/expression/Expression.java   |   39 -
 .../analytics/expression/ExpressionFactory.java |  175 -
 .../expression/MultiDelegateExpression.java     |  131 -
 .../expression/SingleDelegateExpression.java    |   88 -
 .../solr/analytics/expression/package-info.java |   23 -
 .../analytics/facet/AbstractSolrQueryFacet.java |  104 +
 .../solr/analytics/facet/AnalyticsFacet.java    |  166 +
 .../apache/solr/analytics/facet/PivotFacet.java |  114 +
 .../apache/solr/analytics/facet/PivotNode.java  |  263 ++
 .../apache/solr/analytics/facet/QueryFacet.java |   64 +
 .../apache/solr/analytics/facet/RangeFacet.java |  119 +
 .../solr/analytics/facet/SortableFacet.java     |  178 +
 .../solr/analytics/facet/StreamingFacet.java    |   32 +
 .../apache/solr/analytics/facet/ValueFacet.java |   60 +
 .../facet/compare/ConstantComparator.java       |   30 +
 .../facet/compare/DelegatingComparator.java     |   62 +
 .../facet/compare/ExpressionComparator.java     |   46 +
 .../facet/compare/FacetResultsComparator.java   |   52 +
 .../facet/compare/FacetValueComparator.java     |   37 +
 .../analytics/facet/compare/package-info.java   |   23 +
 .../solr/analytics/facet/package-info.java      |   23 +
 .../function/ExpressionCalculator.java          |   71 +
 .../MergingReductionCollectionManager.java      |   46 +
 .../function/ReductionCollectionManager.java    |  320 ++
 .../analytics/function/ReductionFunction.java   |   37 +
 .../function/field/AnalyticsField.java          |   69 +
 .../analytics/function/field/BooleanField.java  |  111 +
 .../function/field/BooleanMultiField.java       |  101 +
 .../analytics/function/field/DateField.java     |  108 +
 .../function/field/DateMultiField.java          |   47 +
 .../function/field/DateMultiPointField.java     |   47 +
 .../analytics/function/field/DoubleField.java   |   97 +
 .../function/field/DoubleMultiField.java        |   85 +
 .../function/field/DoubleMultiPointField.java   |   81 +
 .../analytics/function/field/FloatField.java    |  108 +
 .../function/field/FloatMultiField.java         |   91 +
 .../function/field/FloatMultiPointField.java    |   87 +
 .../solr/analytics/function/field/IntField.java |  129 +
 .../analytics/function/field/IntMultiField.java |  100 +
 .../function/field/IntMultiPointField.java      |   96 +
 .../analytics/function/field/LongField.java     |  107 +
 .../function/field/LongMultiField.java          |   89 +
 .../function/field/LongMultiPointField.java     |   86 +
 .../analytics/function/field/StringField.java   |   85 +
 .../function/field/StringMultiField.java        |   66 +
 .../analytics/function/field/package-info.java  |   23 +
 .../function/mapping/AbsoluteValueFunction.java |   54 +
 .../analytics/function/mapping/AddFunction.java |   68 +
 .../function/mapping/BottomFunction.java        |  163 +
 .../function/mapping/CompareFunction.java       |  614 ++++
 .../function/mapping/ConcatFunction.java        |   78 +
 .../function/mapping/DateMathFunction.java      |  156 +
 .../function/mapping/DateParseFunction.java     |  210 ++
 .../function/mapping/DivideFunction.java        |   51 +
 .../function/mapping/FillMissingFunction.java   |  842 +++++
 .../function/mapping/FilterFunction.java        |  722 ++++
 .../analytics/function/mapping/IfFunction.java  |  892 +++++
 .../function/mapping/JoinFunction.java          |   57 +
 .../function/mapping/LambdaFunction.java        | 3220 ++++++++++++++++++
 .../analytics/function/mapping/LogFunction.java |   51 +
 .../function/mapping/LogicFunction.java         |   90 +
 .../function/mapping/MultFunction.java          |   68 +
 .../function/mapping/NegateFunction.java        |   58 +
 .../mapping/NumericConvertFunction.java         |  256 ++
 .../function/mapping/PowerFunction.java         |   51 +
 .../function/mapping/RemoveFunction.java        |  796 +++++
 .../function/mapping/ReplaceFunction.java       |  914 +++++
 .../function/mapping/StringCastFunction.java    |   42 +
 .../function/mapping/SubtractFunction.java      |   51 +
 .../analytics/function/mapping/TopFunction.java |  163 +
 .../function/mapping/package-info.java          |   23 +
 .../solr/analytics/function/package-info.java   |   23 +
 .../function/reduction/CountFunction.java       |   87 +
 .../function/reduction/DocCountFunction.java    |   87 +
 .../function/reduction/MaxFunction.java         |  298 ++
 .../function/reduction/MedianFunction.java      |  200 ++
 .../function/reduction/MinFunction.java         |  298 ++
 .../function/reduction/MissingFunction.java     |   76 +
 .../function/reduction/OrdinalFunction.java     |  386 +++
 .../function/reduction/PercentileFunction.java  |  337 ++
 .../function/reduction/SumFunction.java         |   92 +
 .../function/reduction/UniqueFunction.java      |  101 +
 .../function/reduction/data/CountCollector.java |  188 +
 .../function/reduction/data/MaxCollector.java   |  476 +++
 .../function/reduction/data/MinCollector.java   |  476 +++
 .../function/reduction/data/ReductionData.java  |   24 +
 .../reduction/data/ReductionDataCollector.java  |  183 +
 .../reduction/data/SortedListCollector.java     |  363 ++
 .../function/reduction/data/SumCollector.java   |  124 +
 .../reduction/data/UniqueCollector.java         |  241 ++
 .../function/reduction/data/package-info.java   |   24 +
 .../function/reduction/package-info.java        |   23 +
 .../org/apache/solr/analytics/package-info.java |   23 +
 .../request/AbstractFieldFacetRequest.java      |   42 -
 .../request/AnalyticsContentHandler.java        |  314 --
 .../analytics/request/AnalyticsRequest.java     |  114 -
 .../request/AnalyticsRequestFactory.java        |  308 --
 .../solr/analytics/request/AnalyticsStats.java  |  138 -
 .../analytics/request/ExpressionRequest.java    |   72 -
 .../solr/analytics/request/FacetRequest.java    |   26 -
 .../analytics/request/FieldFacetRequest.java    |  172 -
 .../analytics/request/QueryFacetRequest.java    |   74 -
 .../analytics/request/RangeFacetRequest.java    |  129 -
 .../solr/analytics/request/package-info.java    |   24 -
 .../AbstractDelegatingStatsCollector.java       |   74 -
 .../statistics/MedianStatsCollector.java        |   76 -
 .../statistics/MinMaxStatsCollector.java        |  114 -
 .../statistics/NumericStatsCollector.java       |   68 -
 .../statistics/PercentileStatsCollector.java    |   80 -
 .../analytics/statistics/StatsCollector.java    |   69 -
 .../StatsCollectorSupplierFactory.java          |  646 ----
 .../statistics/UniqueStatsCollector.java        |   53 -
 .../solr/analytics/statistics/package-info.java |   24 -
 .../stream/AnalyticsShardRequestManager.java    |  245 ++
 .../stream/AnalyticsShardResponseParser.java    |   89 +
 .../solr/analytics/stream/package-info.java     |   23 +
 .../reservation/BooleanArrayReservation.java    |   44 +
 .../reservation/BooleanCheckedReservation.java  |   42 +
 .../stream/reservation/BooleanReservation.java  |   42 +
 .../reservation/DoubleArrayReservation.java     |   44 +
 .../reservation/DoubleCheckedReservation.java   |   43 +
 .../stream/reservation/DoubleReservation.java   |   42 +
 .../reservation/FloatArrayReservation.java      |   44 +
 .../reservation/FloatCheckedReservation.java    |   43 +
 .../stream/reservation/FloatReservation.java    |   42 +
 .../stream/reservation/IntArrayReservation.java |   42 +
 .../reservation/IntCheckedReservation.java      |   43 +
 .../stream/reservation/IntReservation.java      |   42 +
 .../reservation/LongArrayReservation.java       |   45 +
 .../reservation/LongCheckedReservation.java     |   43 +
 .../stream/reservation/LongReservation.java     |   42 +
 .../ReductionCheckedDataReservation.java        |   35 +
 .../ReductionDataArrayReservation.java          |   36 +
 .../reservation/ReductionDataReservation.java   |   53 +
 .../reservation/StringArrayReservation.java     |   45 +
 .../reservation/StringCheckedReservation.java   |   44 +
 .../stream/reservation/StringReservation.java   |   43 +
 .../stream/reservation/package-info.java        |   24 +
 .../read/BooleanCheckedDataReader.java          |   33 +
 .../read/BooleanDataArrayReader.java            |   36 +
 .../reservation/read/BooleanDataReader.java     |   33 +
 .../read/DoubleCheckedDataReader.java           |   32 +
 .../reservation/read/DoubleDataArrayReader.java |   35 +
 .../reservation/read/DoubleDataReader.java      |   32 +
 .../read/FloatCheckedDataReader.java            |   33 +
 .../reservation/read/FloatDataArrayReader.java  |   36 +
 .../reservation/read/FloatDataReader.java       |   33 +
 .../reservation/read/IntCheckedDataReader.java  |   32 +
 .../reservation/read/IntDataArrayReader.java    |   34 +
 .../stream/reservation/read/IntDataReader.java  |   32 +
 .../reservation/read/LongCheckedDataReader.java |   32 +
 .../reservation/read/LongDataArrayReader.java   |   35 +
 .../stream/reservation/read/LongDataReader.java |   32 +
 .../read/ReductionCheckedDataReader.java        |   54 +
 .../read/ReductionDataArrayReader.java          |   54 +
 .../reservation/read/ReductionDataReader.java   |   40 +
 .../read/StringCheckedDataReader.java           |   32 +
 .../reservation/read/StringDataArrayReader.java |   35 +
 .../reservation/read/StringDataReader.java      |   34 +
 .../stream/reservation/read/package-info.java   |   24 +
 .../write/BooleanCheckedDataWriter.java         |   33 +
 .../write/BooleanDataArrayWriter.java           |   36 +
 .../reservation/write/BooleanDataWriter.java    |   33 +
 .../write/DoubleCheckedDataWriter.java          |   34 +
 .../write/DoubleDataArrayWriter.java            |   36 +
 .../reservation/write/DoubleDataWriter.java     |   33 +
 .../write/FloatCheckedDataWriter.java           |   35 +
 .../reservation/write/FloatDataArrayWriter.java |   37 +
 .../reservation/write/FloatDataWriter.java      |   34 +
 .../reservation/write/IntCheckedDataWriter.java |   34 +
 .../reservation/write/IntDataArrayWriter.java   |   35 +
 .../stream/reservation/write/IntDataWriter.java |   33 +
 .../write/LongCheckedDataWriter.java            |   34 +
 .../reservation/write/LongDataArrayWriter.java  |   36 +
 .../reservation/write/LongDataWriter.java       |   33 +
 .../write/ReductionCheckedDataWriter.java       |   60 +
 .../write/ReductionDataArrayWriter.java         |   53 +
 .../reservation/write/ReductionDataWriter.java  |   40 +
 .../write/StringCheckedDataWriter.java          |   34 +
 .../write/StringDataArrayWriter.java            |   36 +
 .../reservation/write/StringDataWriter.java     |   37 +
 .../stream/reservation/write/package-info.java  |   24 +
 .../solr/analytics/util/AnalyticsParams.java    |  114 -
 .../solr/analytics/util/AnalyticsParsers.java   |  171 -
 .../util/AnalyticsResponseHeadings.java         |   36 +
 .../analytics/util/FacetRangeGenerator.java     |  356 ++
 .../solr/analytics/util/MedianCalculator.java   |    4 +
 .../solr/analytics/util/OldAnalyticsParams.java |  177 +
 .../util/OldAnalyticsRequestConverter.java      |  177 +
 .../solr/analytics/util/OrdinalCalculator.java  |  173 +
 .../analytics/util/PercentileCalculator.java    |  176 -
 .../analytics/util/RangeEndpointCalculator.java |  354 --
 .../util/function/BooleanConsumer.java          |   59 +
 .../analytics/util/function/FloatConsumer.java  |   59 +
 .../analytics/util/function/FloatSupplier.java  |   41 +
 .../analytics/util/function/package-info.java   |   23 +
 .../solr/analytics/util/package-info.java       |    3 +-
 .../AbsoluteValueDoubleFunction.java            |   60 -
 .../util/valuesource/AddDoubleFunction.java     |   49 -
 .../util/valuesource/ConstDateSource.java       |  112 -
 .../util/valuesource/ConstDoubleSource.java     |  104 -
 .../util/valuesource/ConstStringSource.java     |   50 -
 .../util/valuesource/DateFieldSource.java       |  131 -
 .../util/valuesource/DateMathFunction.java      |   71 -
 .../util/valuesource/DivDoubleFunction.java     |   48 -
 .../util/valuesource/DualDoubleFunction.java    |   94 -
 .../util/valuesource/FilterFieldSource.java     |  154 -
 .../util/valuesource/LogDoubleFunction.java     |   43 -
 .../util/valuesource/MultiDateFunction.java     |  133 -
 .../util/valuesource/MultiDoubleFunction.java   |  119 -
 .../valuesource/MultiplyDoubleFunction.java     |   49 -
 .../util/valuesource/NegateDoubleFunction.java  |   55 -
 .../util/valuesource/PowDoubleFunction.java     |   48 -
 .../util/valuesource/ReverseStringFunction.java |   45 -
 .../util/valuesource/SingleDoubleFunction.java  |   79 -
 .../util/valuesource/SingleStringFunction.java  |  117 -
 .../util/valuesource/package-info.java          |   24 -
 .../solr/analytics/value/AnalyticsValue.java    |   55 +
 .../analytics/value/AnalyticsValueStream.java   |  133 +
 .../solr/analytics/value/BooleanValue.java      |   85 +
 .../analytics/value/BooleanValueStream.java     |   55 +
 .../solr/analytics/value/ComparableValue.java   |   32 +
 .../apache/solr/analytics/value/DateValue.java  |  102 +
 .../solr/analytics/value/DateValueStream.java   |   62 +
 .../solr/analytics/value/DoubleValue.java       |   86 +
 .../solr/analytics/value/DoubleValueStream.java |   54 +
 .../apache/solr/analytics/value/FloatValue.java |   97 +
 .../solr/analytics/value/FloatValueStream.java  |   60 +
 .../apache/solr/analytics/value/IntValue.java   |  121 +
 .../solr/analytics/value/IntValueStream.java    |   71 +
 .../apache/solr/analytics/value/LongValue.java  |   97 +
 .../solr/analytics/value/LongValueStream.java   |   60 +
 .../solr/analytics/value/StringValue.java       |   71 +
 .../solr/analytics/value/StringValueStream.java |   49 +
 .../value/constant/ConstantBooleanValue.java    |   91 +
 .../value/constant/ConstantDateValue.java       |  103 +
 .../value/constant/ConstantDoubleValue.java     |   90 +
 .../value/constant/ConstantFloatValue.java      |   99 +
 .../value/constant/ConstantIntValue.java        |  118 +
 .../value/constant/ConstantLongValue.java       |  100 +
 .../value/constant/ConstantStringValue.java     |   79 +
 .../analytics/value/constant/ConstantValue.java |  128 +
 .../analytics/value/constant/package-info.java  |   23 +
 .../solr/analytics/value/package-info.java      |   23 +
 .../apache/solr/handler/AnalyticsHandler.java   |  147 +
 .../handler/component/AnalyticsComponent.java   |  119 +-
 .../java/org/apache/solr/handler/package.html   |   28 +
 .../response/AnalyticsShardResponseWriter.java  |   91 +
 .../java/org/apache/solr/response/package.html  |   28 +
 .../analytics/requestFiles/expressions.txt      |   70 -
 .../analytics/requestFiles/fieldFacetExtras.txt |   66 -
 .../analytics/requestFiles/fieldFacets.txt      |  132 -
 .../analytics/requestFiles/functions.txt        |   62 -
 .../analytics/requestFiles/noFacets.txt         |   74 -
 .../analytics/requestFiles/queryFacets.txt      |   45 -
 .../analytics/requestFiles/rangeFacets.txt      |  170 -
 .../analytics/requestXMLFiles/expressions.xml   |  285 --
 .../requestXMLFiles/fieldFacetExtras.xml        |  101 -
 .../analytics/requestXMLFiles/fieldFacets.xml   |  496 ---
 .../analytics/requestXMLFiles/functions.xml     |  246 --
 .../analytics/requestXMLFiles/noFacets.xml      |  310 --
 .../analytics/requestXMLFiles/queryFacets.xml   |   94 -
 .../analytics/requestXMLFiles/rangeFacets.xml   |  319 --
 .../test-files/solr/analytics/expressions.txt   |   65 +
 .../test-files/solr/analytics/facetSorting.txt  |    4 +
 .../solr/analytics/fieldFacetExtras.txt         |   66 +
 .../test-files/solr/analytics/fieldFacets.txt   |  132 +
 .../src/test-files/solr/analytics/functions.txt |   57 +
 .../src/test-files/solr/analytics/noFacets.txt  |   74 +
 .../test-files/solr/analytics/queryFacets.txt   |   27 +
 .../test-files/solr/analytics/rangeFacets.txt   |  161 +
 .../solr/collection1/conf/schema-analytics.xml  |    7 +-
 .../collection1/conf/solrconfig-analytics.xml   |   42 +
 .../solr/collection1/conf/solrconfig-basic.xml  |   40 -
 .../configsets/cloud-analytics/conf/schema.xml  |   63 +
 .../cloud-analytics/conf/solrconfig.xml         |   59 +
 .../AbstractAnalyticsStatsCloudTest.java        |  187 +
 .../analytics/AbstractAnalyticsStatsTest.java   |   14 +-
 .../apache/solr/analytics/NoFacetCloudTest.java |  557 +++
 .../org/apache/solr/analytics/NoFacetTest.java  |   46 +-
 .../analytics/expression/ExpressionTest.java    |   70 +-
 .../solr/analytics/expression/FunctionTest.java |  221 ++
 .../facet/AbstractAnalyticsFacetCloudTest.java  |  284 ++
 .../facet/AbstractAnalyticsFacetTest.java       |   37 +-
 .../solr/analytics/facet/FacetSortingTest.java  |   53 +
 .../analytics/facet/FieldFacetCloudTest.java    | 1214 +++++++
 .../facet/FieldFacetExtrasCloudTest.java        |  253 ++
 .../analytics/facet/FieldFacetExtrasTest.java   |    6 +-
 .../solr/analytics/facet/FieldFacetTest.java    |   63 +-
 .../analytics/facet/QueryFacetCloudTest.java    |  159 +
 .../solr/analytics/facet/QueryFacetTest.java    |   10 +-
 .../analytics/facet/RangeFacetCloudTest.java    |  588 ++++
 .../solr/analytics/facet/RangeFacetTest.java    |   43 +-
 .../util/valuesource/FunctionTest.java          |  233 --
 .../solr/handler/component/ResponseBuilder.java |    3 +
 313 files changed, 30374 insertions(+), 9933 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 208512c..9578698 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -116,6 +116,10 @@ Upgrading from Solr 6.x
      curl http://host:8983/solr/mycollection/config -d '{"set-user-property": {"update.autoCreateFields":"false"}}'
   Please see SOLR-10574 for details.
 
+* SOLR-10123: The Analytics Component has been upgraded to support distributed collections, expressions over multivalued
+  fields, a new JSON request language, and more. DocValues are now required for any field used in the analytics expression
+  whereas previously docValues was not required. Please see SOLR-10123 for details.
+
 New Features
 ----------------------
 * SOLR-9857, SOLR-9858: Collect aggregated metrics from nodes and shard leaders in overseer. (ab)
@@ -175,6 +179,10 @@ New Features
 
 * SOLR-10272: Use _default config set if no collection.configName is specified with CREATE (Ishan Chattopadhyaya)
 
+* SOLR-10123: Upgraded the Analytics Component to version 2.0 which now supports distributed collections, expressions over 
+  multivalued fields, a new JSON request language, and more. DocValues are now required for any field used in the analytics 
+  expression  whereas previously docValues was not required. Please see SOLR-10123 for details. (Houston Putman)
+
 Bug Fixes
 ----------------------
 * SOLR-9262: Connection and read timeouts are being ignored by UpdateShardHandler after SOLR-4509.

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsDriver.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsDriver.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsDriver.java
new file mode 100644
index 0000000..21b053f
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsDriver.java
@@ -0,0 +1,82 @@
+/*
+ * 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.solr.analytics;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.search.DocIdSet;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.solr.analytics.AnalyticsRequestManager.StreamingInfo;
+import org.apache.solr.analytics.facet.AbstractSolrQueryFacet.FacetValueQueryExecuter;
+import org.apache.solr.analytics.facet.StreamingFacet;
+import org.apache.solr.analytics.function.ReductionCollectionManager;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.search.Filter;
+import org.apache.solr.search.SolrIndexSearcher;
+
+public class AnalyticsDriver {
+  
+  /**
+   * Drive the collection of reduction data. This includes overall data as well as faceted data.
+   * 
+   * @param manager of the request to drive
+   * @param searcher the results of the query
+   * @param filter that represents the overall query
+   * @param queryRequest used for the search request
+   * @throws IOException if an error occurs while reading from Solr
+   */
+  public static void drive(AnalyticsRequestManager manager, SolrIndexSearcher searcher, Filter filter, SolrQueryRequest queryRequest) throws IOException {
+    StreamingInfo streamingInfo = manager.getStreamingFacetInfo();
+    Iterable<StreamingFacet> streamingFacets = streamingInfo.streamingFacets;
+    ReductionCollectionManager collectionManager = streamingInfo.streamingCollectionManager;
+    
+    Iterable<FacetValueQueryExecuter> facetExecuters = manager.getFacetExecuters(filter, queryRequest);
+    
+    // Streaming phase (Overall results & Value/Pivot Facets)
+    // Loop through all documents and collect reduction data for streaming facets and overall results
+    if (collectionManager.needsCollection()) {
+      List<LeafReaderContext> contexts = searcher.getTopReaderContext().leaves();
+      for (int leafNum = 0; leafNum < contexts.size(); leafNum++) {
+        LeafReaderContext context = contexts.get(leafNum);
+        DocIdSet dis = filter.getDocIdSet(context, null); // solr docsets already exclude any deleted docs
+        if (dis == null) {
+          continue;
+        }
+        DocIdSetIterator disi = dis.iterator();
+        if (disi != null) {
+          collectionManager.doSetNextReader(context);
+          int doc = disi.nextDoc();
+          while( doc != DocIdSetIterator.NO_MORE_DOCS){
+            // Add a document to the statistics being generated
+            collectionManager.collect(doc);
+            streamingFacets.forEach( facet -> facet.addFacetValueCollectionTargets() );
+            collectionManager.apply();
+            doc = disi.nextDoc();
+          }
+        }
+      }
+    }
+    
+    // Executing phase (Query/Range Facets)
+    // Send additional Solr Queries to compute facet values
+    for (FacetValueQueryExecuter executer : facetExecuters) {
+      executer.execute(searcher);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsExpression.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsExpression.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsExpression.java
new file mode 100644
index 0000000..044e371
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsExpression.java
@@ -0,0 +1,64 @@
+/*
+ * 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.solr.analytics;
+
+import org.apache.solr.analytics.function.ReductionCollectionManager;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.analytics.value.AnalyticsValue;
+
+/**
+ * A wrapper for a top-level analytics expression.
+ * The expression must have a name and be single valued.
+ */
+public class AnalyticsExpression {
+  private final AnalyticsValue expression;
+  private final String name;
+  
+  public AnalyticsExpression(String name, AnalyticsValue expression) {
+    this.name = name;
+    this.expression = expression;
+  }
+  
+  public String getName() {
+    return name;
+  }
+  
+  public AnalyticsValue getExpression() {
+    return expression;
+  }
+  
+  /**
+   * Get the current value of the expression.
+   * This method can, and will, be called multiple times to return different values.
+   * The value returned is based on the {@link ReductionDataCollection} given
+   * to the {@link ReductionCollectionManager#setData} method.
+   * 
+   * @return the current value of the expression
+   */
+  public Object toObject() {
+    return expression.getObject();
+  }
+  
+  /**
+   * NOTE: Must be called after {@link #toObject()} is called, otherwise the value is not guaranteed to be correct.
+   * 
+   * @return whether the current value of the expression exists.
+   */
+  public boolean exists() {
+    return expression.exists();
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsGroupingManager.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsGroupingManager.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsGroupingManager.java
new file mode 100644
index 0000000..a95a451
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsGroupingManager.java
@@ -0,0 +1,239 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file inputtributed 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
+ * inputtributed under the License is inputtributed 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.solr.analytics;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.facet.AnalyticsFacet;
+import org.apache.solr.analytics.facet.PivotFacet;
+import org.apache.solr.analytics.facet.AbstractSolrQueryFacet;
+import org.apache.solr.analytics.facet.QueryFacet;
+import org.apache.solr.analytics.facet.RangeFacet;
+import org.apache.solr.analytics.facet.StreamingFacet;
+import org.apache.solr.analytics.facet.ValueFacet;
+import org.apache.solr.analytics.facet.AbstractSolrQueryFacet.FacetValueQueryExecuter;
+import org.apache.solr.analytics.function.ExpressionCalculator;
+import org.apache.solr.analytics.function.ReductionCollectionManager;
+import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.search.Filter;
+
+/**
+ * The manager for faceted analytics. This class manages one grouping of facets and expressions to compute
+ * over those facets.
+ * 
+ * <p>
+ * This class will only manage generating faceted results, not overall results.
+ */
+public class AnalyticsGroupingManager {
+  private final String name;
+  private final ReductionCollectionManager reductionCollectionManager;
+
+  private final Collection<AnalyticsExpression> topLevelExpressions;
+  private final ExpressionCalculator expressionCalculator;
+  
+  private final Map<String, AnalyticsFacet> facets;
+  
+  public AnalyticsGroupingManager(String name,
+                                  ReductionCollectionManager reductionCollectionManager,
+                                  Collection<AnalyticsExpression> topLevelExpressions) {
+    this.name = name;
+    this.reductionCollectionManager = reductionCollectionManager;
+
+    this.topLevelExpressions = topLevelExpressions;
+    this.expressionCalculator = new ExpressionCalculator(topLevelExpressions);
+
+    this.facets = new HashMap<>();
+  }
+
+  // This is outside of the method, since it is used in the lambda and cannot be a local non-final variable
+  private boolean hasStreamingFacets;
+  
+  /**
+   * Get the {@link StreamingFacet}s (e.g. {@link ValueFacet} and {@link PivotFacet}) contained within this grouping,
+   * returning them through the given consumer.
+   * 
+   * @param cons where the streaming facets are passed to
+   * @return whether the grouping contains streaming facets
+   */
+  public boolean getStreamingFacets(Consumer<StreamingFacet> cons) {
+    hasStreamingFacets = false;
+    facets.forEach( (name, facet) -> {
+      if (facet instanceof StreamingFacet) {
+        cons.accept((StreamingFacet)facet);
+        hasStreamingFacets = true;
+      }
+    });
+    return hasStreamingFacets;
+  }
+
+  /**
+   * Create the {@link FacetValueQueryExecuter}s for all {@link AbstractSolrQueryFacet}s
+   * (e.g. {@link QueryFacet} and {@link RangeFacet}) contained within this grouping.
+   * The executers are returned through the given consumer.
+   * 
+   * <p>
+   * One {@link FacetValueQueryExecuter} is created for each facet value to be returned for a facet.
+   * Since every {@link AbstractSolrQueryFacet} has discrete and user-defined facet values,
+   * unlike {@link StreamingFacet}s, a discrete number of {@link FacetValueQueryExecuter}s are created and returned.
+   * 
+   * @param filter representing the overall Solr Query of the request,
+   * will be combined with the facet value queries
+   * @param queryRequest from the overall search request
+   * @param cons where the executers are passed to
+   */
+  public void getFacetExecuters(Filter filter, SolrQueryRequest queryRequest, Consumer<FacetValueQueryExecuter> cons) {
+    facets.forEach( (name, facet) -> {
+      if (facet instanceof AbstractSolrQueryFacet) {
+        ((AbstractSolrQueryFacet)facet).createFacetValueExecuters(filter, queryRequest, cons);
+      }
+    });
+  }
+  
+  /**
+   * Add a facet to the grouping. All expressions in this grouping will be computed over the facet.
+   * 
+   * @param facet to compute expressions over
+   */
+  public void addFacet(AnalyticsFacet facet) {
+    facet.setExpressionCalculator(expressionCalculator);
+    facet.setReductionCollectionManager(reductionCollectionManager);
+    facets.put(facet.getName(), facet);
+  }
+  
+  /**
+   * Import the shard data for this grouping from a bit-stream,
+   * exported by the {@link #exportShardData} method in the each of the collection's shards.
+   * 
+   * @param input The bit-stream to import the grouping data from
+   * @throws IOException if an exception occurs while reading from the {@link DataInput}
+   */
+  public void importShardData(DataInput input) throws IOException {
+    // This allows mergeData() to import from the same input everytime it is called
+    // while the facets are importing.
+    reductionCollectionManager.setShardInput(input);
+    
+    int sz = input.readInt();
+    for (int i = 0; i < sz; ++i) {
+      facets.get(input.readUTF()).importShardData(input);
+    }
+  }
+  
+  /**
+   * Export the shard data for this grouping through a bit-stream,
+   * to be imported by the {@link #importShardData} method in the originating shard.
+   * 
+   * @param output The bit-stream to output the grouping data through
+   * @throws IOException if an exception occurs while writing to the {@link DataOutput}
+   */
+  public void exportShardData(DataOutput output) throws IOException {
+    // This allows exportData() to export to the same output everytime it is called
+    // while the facets are exporting.
+    reductionCollectionManager.setShardOutput(output);
+    
+    output.writeInt(facets.size());
+    for (Entry<String,AnalyticsFacet> facet : facets.entrySet()) {
+      output.writeUTF(facet.getKey());
+      facet.getValue().exportShardData(output);
+    }
+  }
+  
+  /**
+   * Get the {@link ReductionCollectionManager} that manages the collection of reduction data for the expressions
+   * contained within this grouping. 
+   * 
+   * @return the grouping's reduction manager
+   */
+  public ReductionCollectionManager getReductionManager() {
+    return reductionCollectionManager;
+  }
+
+  /**
+   * Create the response for this grouping, a mapping from each of it's facets' names to the facet's response.
+   * 
+   * @return the named list representation of the response
+   */
+  public Map<String,Object> createResponse() {
+    Map<String,Object> response = new HashMap<>();
+    
+    // Add the value facet buckets to the output
+    facets.forEach( (name, facet) -> response.put(name, facet.createResponse()) );
+
+    return response;
+  }
+  
+  /**
+   * Create the response for this grouping, but in the old style of response.
+   * This response has a bucket for the following if they are contained in the grouping:
+   * FieldFacets, RangeFacets and QueryFacets.
+   * Each facet's name and response are put into the bucket corresponding to its type.
+   * <p>
+   * Since groupings in the old notation must also return overall results, the overall results are
+   * passed in and the values are used to populate the grouping response.
+   * 
+   * @param overallResults of the expressions to add to the grouping response
+   * @return the named list representation of the response
+   */
+  public NamedList<Object> createOldResponse(Map<String,Object> overallResults) {
+    NamedList<Object> response = new NamedList<>();
+    
+    topLevelExpressions.forEach( expression -> response.add(expression.getName(), overallResults.get(name + expression.getName())));
+
+    NamedList<Object> fieldFacetResults = new NamedList<>();
+    NamedList<Object> rangeFacetResults = new NamedList<>();
+    NamedList<Object> queryFacetResults = new NamedList<>();
+    // Add the field facet buckets to the output
+    facets.forEach( (name, facet) -> {
+      // The old style of request only accepts field facets
+      // So we can assume that all value facets are field facets
+      if (facet instanceof ValueFacet) {
+        fieldFacetResults.add(name, facet.createOldResponse());
+      } else if (facet instanceof RangeFacet) {
+        rangeFacetResults.add(name, facet.createOldResponse());
+      } else if (facet instanceof QueryFacet) {
+        queryFacetResults.add(name, facet.createOldResponse());
+      }
+    });
+    if (fieldFacetResults.size() > 0) {
+      response.add(AnalyticsResponseHeadings.FIELD_FACETS, fieldFacetResults);
+    }
+    if (rangeFacetResults.size() > 0) {
+      response.add(AnalyticsResponseHeadings.RANGE_FACETS, rangeFacetResults);
+    }
+    if (queryFacetResults.size() > 0) {
+      response.add(AnalyticsResponseHeadings.QUERY_FACETS, queryFacetResults);
+    }
+    return response;
+  }
+
+  /**
+   * Get the name of the grouping.
+   * 
+   * @return the grouping name
+   */
+  public String getName() {
+    return name;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestManager.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestManager.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestManager.java
new file mode 100644
index 0000000..45b958f
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestManager.java
@@ -0,0 +1,279 @@
+/*
+ * 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.solr.analytics;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.solr.analytics.facet.AbstractSolrQueryFacet;
+import org.apache.solr.analytics.facet.AbstractSolrQueryFacet.FacetValueQueryExecuter;
+import org.apache.solr.analytics.facet.StreamingFacet;
+import org.apache.solr.analytics.function.ExpressionCalculator;
+import org.apache.solr.analytics.function.ReductionCollectionManager;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.analytics.stream.AnalyticsShardRequestManager;
+import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.search.Filter;
+
+/**
+ * The manager of an entire analytics request.
+ */
+public class AnalyticsRequestManager {
+  private final ReductionCollectionManager ungroupedReductionManager;
+  private ReductionDataCollection ungroupedData;
+  
+  private final Map<String, AnalyticsGroupingManager> groupingManagers;
+  
+  private final Collection<AnalyticsExpression> ungroupedExpressions;
+  private final ExpressionCalculator ungroupedExpressionCalculator;
+  
+  /**
+   * If the request is distributed, the manager for shard requests.
+   */
+  public String analyticsRequest;
+  public AnalyticsShardRequestManager shardStream;
+  public boolean sendShards; 
+  
+  /**
+   * Create an manager with the given ungrouped expressions. This is straightforward in the new
+   * style of request, however in the old olap-style requests all groupings' expressions are expected
+   * to be ungrouped as well.
+   * 
+   * 
+   * @param ungroupedReductionManager to manage the reduction collection for all ungrouped expressions
+   * @param ungroupedExpressions to compute overall results for
+   */
+  public AnalyticsRequestManager(ReductionCollectionManager ungroupedReductionManager,
+                                 Collection<AnalyticsExpression> ungroupedExpressions) {
+    this.ungroupedReductionManager = ungroupedReductionManager;
+    this.ungroupedData = ungroupedReductionManager.newDataCollection();
+    this.ungroupedReductionManager.addLastingCollectTarget(ungroupedData);
+    
+    this.ungroupedExpressions = ungroupedExpressions;
+    this.ungroupedExpressionCalculator = new ExpressionCalculator(ungroupedExpressions);
+    this.groupingManagers = new HashMap<>();
+  }
+  
+  /**
+   * Get the collection manager for ungrouped expressions, including grouped expressions if
+   * the old request notation is used.
+   * 
+   * @return the collection manager for the ungrouped expressions
+   */
+  public ReductionCollectionManager getUngroupedCollectionManager() {
+    return ungroupedReductionManager;
+  }
+  
+  /**
+   * Get the collection manager for all ungrouped expressions, including grouped expressions if
+   * the old request notation is used.
+   * 
+   * @return the collection manager for the ungrouped expressions
+   */
+  public ReductionDataCollection getUngroupedData() {
+    return ungroupedData;
+  }
+  
+  /**
+   * Return all ungrouped expressions, including grouped expressions if
+   * the old request notation is used.
+   * 
+   * @return an {@link Iterable} of the ungrouped expressions
+   */
+  public Iterable<AnalyticsExpression> getUngroupedExpressions() {
+    return ungroupedExpressions;
+  }
+  
+  /**
+   * Generate the results of all ungrouped expressions, including grouped expressions if
+   * the old request notation is used.
+   * 
+   * @param response the response to add the ungrouped results to.
+   */
+  public void addUngroupedResults(Map<String,Object> response) {
+    ungroupedReductionManager.setData(ungroupedData);
+    ungroupedExpressionCalculator.addResults(response);
+  }
+  
+  /**
+   * Generate the results of all ungrouped expressions, including grouped expressions if
+   * the old request notation is used.
+   * 
+   * @return the map containing the ungrouped results
+   */
+  public Map<String,Object> getUngroupedResults() {
+    ungroupedReductionManager.setData(ungroupedData);
+    return ungroupedExpressionCalculator.getResults();
+  }
+  
+  /**
+   * Add a grouping to the request.
+   * 
+   * @param groupingManager that manages the grouping
+   */
+  public void addGrouping(AnalyticsGroupingManager groupingManager) {
+    groupingManagers.put(groupingManager.getName(), groupingManager);
+  }
+  
+  /**
+   * Import the shard data for this request from a bit-stream,
+   * exported by the {@link #exportShardData} method in the each of the collection's shards.
+   * <p>
+   * First the overall data is imported, then the grouping data is imported.
+   * 
+   * @param input The bit-stream to import the shard data from
+   * @throws IOException if an exception occurs while reading from the {@link DataInput}
+   */
+  public synchronized void importShardData(DataInput input) throws IOException {
+    ungroupedReductionManager.setShardInput(input);
+    
+    // The ungroupedData will not exist for the first shard imported
+    if (ungroupedData == null) {
+      ungroupedData = ungroupedReductionManager.newDataCollectionIO();
+    } else {
+      ungroupedReductionManager.prepareReductionDataIO(ungroupedData);
+    }
+    ungroupedReductionManager.mergeData();
+    
+    int size = input.readInt();
+    while (--size >= 0) {
+      String groupingName = input.readUTF();
+      groupingManagers.get(groupingName).importShardData(input);
+    }
+  }
+  
+  /**
+   * Export the shard data for this request through a bit-stream,
+   * to be imported by the {@link #importShardData} method in the originating shard.
+   * <p>
+   * First the overall data is exported, then the grouping data is exported.
+   * 
+   * @param output The bit-stream to output the shard data through
+   * @throws IOException if an exception occurs while writing to the {@link DataOutput}
+   */
+  public void exportShardData(DataOutput output) throws IOException {
+    ungroupedReductionManager.setShardOutput(output);
+    
+    ungroupedReductionManager.prepareReductionDataIO(ungroupedData);
+    ungroupedReductionManager.exportData();
+    
+    output.writeInt(groupingManagers.size());
+    for (String groupingName : groupingManagers.keySet()) {
+      output.writeUTF(groupingName);
+      groupingManagers.get(groupingName).exportShardData(output);
+    }
+  }
+
+  /**
+   * Consolidate the information of all {@link StreamingFacet}s contained within the request, since
+   * they need to be collected along with the overall results during the streaming phase of the 
+   * {@link AnalyticsDriver}.
+   * 
+   * @return the info for all {@link StreamingFacet}s
+   */
+  public StreamingInfo getStreamingFacetInfo() {
+    StreamingInfo streamingInfo = new StreamingInfo();
+    ArrayList<ReductionCollectionManager> groupingCollectors = new ArrayList<>();
+    groupingManagers.values().forEach( grouping -> {
+      // If a grouping has streaming facets, then that groupings expressions
+      // must be collected during the streaming phase. 
+      if (grouping.getStreamingFacets( facet -> streamingInfo.streamingFacets.add(facet) )) {
+        groupingCollectors.add(grouping.getReductionManager());
+      }
+    });
+    
+    // Create an streaming collection manager to manage the collection of all ungrouped expressions and
+    // grouped expressions that are calculated over streaming facets.
+    streamingInfo.streamingCollectionManager = ungroupedReductionManager.merge(groupingCollectors);
+    return streamingInfo;
+  }
+  
+  /**
+   * Class to encapsulate all necessary data for collecting {@link StreamingFacet}s.
+   */
+  public static class StreamingInfo {
+    Collection<StreamingFacet> streamingFacets = new ArrayList<>();
+    /**
+     * Manages the collection of all expressions needed for streaming facets
+     */
+    ReductionCollectionManager streamingCollectionManager;
+  }
+
+  /**
+   * Create the {@link FacetValueQueryExecuter}s for all {@link AbstractSolrQueryFacet}s contained in the request.
+   * 
+   * @param filter representing the overall search query
+   * @param queryRequest of the overall search query
+   * @return an {@link Iterable} of executers
+   */
+  public Iterable<FacetValueQueryExecuter> getFacetExecuters(Filter filter, SolrQueryRequest queryRequest) {
+    ArrayList<FacetValueQueryExecuter> facetExecutors = new ArrayList<>();
+    groupingManagers.values().forEach( grouping -> {
+      grouping.getFacetExecuters(filter, queryRequest, executor -> facetExecutors.add(executor));
+    });
+    return facetExecutors;
+  }
+  
+  /**
+   * Create the response for a request given in the old olap-style format.
+   * The old response returned overall expressions within groupings.
+   * 
+   * @return a {@link NamedList} representation of the response
+   */
+  public NamedList<Object> createOldResponse() {
+    NamedList<Object> analyticsResponse = new NamedList<>();
+    Map<String,Object> ungroupedResults = getUngroupedResults();
+    groupingManagers.forEach( (name, groupingManager) -> {
+      analyticsResponse.add(name, groupingManager.createOldResponse(ungroupedResults));
+    });
+    
+    return analyticsResponse;
+  }
+  
+  /**
+   * Create the response for a request.
+   * 
+   * <p>
+   * NOTE: Analytics requests specified in the old olap-style format
+   * have their responses generated by {@link #createOldResponse()}.
+   * 
+   * @return a {@link Map} representation of the response
+   */
+  public Map<String,Object> createResponse() {
+    Map<String,Object> analyticsResponse = new HashMap<>();
+    if (ungroupedExpressions.size() > 0) {
+      addUngroupedResults(analyticsResponse);
+    }
+
+    Map<String,Object> groupingsResponse = new HashMap<>();
+    groupingManagers.forEach( (name, groupingManager) -> {
+      groupingsResponse.put(name, groupingManager.createResponse());
+    });
+    
+    if (groupingsResponse.size() > 0) {
+      analyticsResponse.put(AnalyticsResponseHeadings.GROUPINGS, groupingsResponse);
+    }
+    return analyticsResponse;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestParser.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestParser.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestParser.java
new file mode 100644
index 0000000..bfa62e2
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestParser.java
@@ -0,0 +1,549 @@
+/*
+ * 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.solr.analytics;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+
+import org.apache.solr.analytics.facet.PivotFacet;
+import org.apache.solr.analytics.facet.PivotNode;
+import org.apache.solr.analytics.facet.QueryFacet;
+import org.apache.solr.analytics.facet.RangeFacet;
+import org.apache.solr.analytics.facet.ValueFacet;
+import org.apache.solr.analytics.facet.SortableFacet.FacetSortSpecification;
+import org.apache.solr.analytics.facet.compare.DelegatingComparator;
+import org.apache.solr.analytics.facet.compare.FacetValueComparator;
+import org.apache.solr.analytics.facet.compare.FacetResultsComparator;
+import org.apache.solr.analytics.value.AnalyticsValue;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.ComparableValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.common.params.FacetParams.FacetRangeInclude;
+import org.apache.solr.common.params.FacetParams.FacetRangeOther;
+import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.schema.SchemaField;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import com.fasterxml.jackson.annotation.JsonSubTypes;
+import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.JsonTypeName;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * Class to manage the parsing of new-style analytics requests.
+ */
+public class AnalyticsRequestParser {
+  
+  private static ObjectMapper mapper = new ObjectMapper();
+  
+  public static void init() {
+    mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
+    mapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, true);
+  }
+  
+  public static final String analyticsParamName = "analytics";
+
+  private static Predicate<String> request         = acceptNames("request", "req");
+  private static Predicate<String> functions       = acceptNames("functions", "funcs", "f");
+  private static Predicate<String> expressions     = acceptNames("expressions", "exprs", "e");
+
+  private static Predicate<String> grouping        = acceptNames("grouping", "group", "g");
+  
+  private static Predicate<String> valueFacet      = acceptNames("valuefacet", "vf");
+  
+  private static Predicate<String> pivotFacet      = acceptNames("pivotfacet", "pf");
+  private static Predicate<String> pivot           = acceptNames("pivot", "p");
+  
+  private static Predicate<String> sort            = acceptNames("sort", "s");
+  private static Predicate<String> sortExpression  = acceptNames("expression", "expr");
+  private static Predicate<String> sortFacetValue  = acceptNames("facetvalue", "fv");
+  private static Predicate<String> sortDirection   = acceptNames("direction", "dir");
+  private static Predicate<String> sortAscending   = acceptNames("ascending", "asc", "a");
+  private static Predicate<String> sortDescending  = acceptNames("descending", "desc", "d");
+  private static Predicate<String> sortLimit       = acceptNames("limit", "l");
+  private static Predicate<String> sortOffset      = acceptNames("offset", "o");
+  
+  private static Predicate<String> rangeFacet      = acceptNames("rangefacet", "rf");
+  private static Predicate<String> rangeGaps       = acceptNames("gaps", "g");
+  private static Predicate<String> rangeHardEnd    = acceptNames("hardend", "he");
+  private static Predicate<String> rangeInclude    = acceptNames("include", "i");
+  private static Predicate<String> rangeOthers     = acceptNames("others", "o");
+  
+  private static Predicate<String> queryFacet      = acceptNames("queryfacet", "qf");
+  private static Predicate<String> query           = acceptNames("query", "q");
+  
+  private static Predicate<String> acceptNames(String... names) {
+    return Pattern.compile("^(?:" + Arrays.stream(names).reduce((a,b) -> a + "|" + b).orElse("") + ")$", Pattern.CASE_INSENSITIVE).asPredicate();
+  }
+  
+  // Defaults
+  public static final String DEFAULT_SORT_DIRECTION = "ascending";
+  public static final int DEFAULT_OFFSET = 0;
+  public static final int DEFAULT_LIMIT = -1;
+  public static final boolean DEFAULT_HARDEND = false;
+  
+  @JsonInclude(Include.NON_EMPTY)
+  public static class AnalyticsRequest {
+    public Map<String, String> functions;
+    public Map<String, String> expressions;
+    
+    public Map<String, AnalyticsGroupingRequest> groupings;
+  }
+  
+  public static class AnalyticsGroupingRequest {
+    public Map<String, String> expressions;
+    
+    public Map<String, AnalyticsFacetRequest> facets;
+  }
+  
+  @JsonTypeInfo(
+      use = JsonTypeInfo.Id.NAME,
+      include = JsonTypeInfo.As.PROPERTY,
+      property = "type"
+  )
+  @JsonSubTypes({
+    @Type(value = AnalyticsValueFacetRequest.class, name = "value"),
+    @Type(value = AnalyticsPivotFacetRequest.class, name = "pivot"),
+    @Type(value = AnalyticsRangeFacetRequest.class, name = "range"),
+    @Type(value = AnalyticsQueryFacetRequest.class, name = "query") }
+  )
+  @JsonInclude(Include.NON_EMPTY)
+  public static interface AnalyticsFacetRequest { }
+  
+  @JsonTypeName("value")
+  public static class AnalyticsValueFacetRequest implements AnalyticsFacetRequest {
+    public String expression;
+    public AnalyticsSortRequest sort;
+  }
+
+  @JsonTypeName("pivot")
+  public static class AnalyticsPivotFacetRequest implements AnalyticsFacetRequest {
+    public List<AnalyticsPivotRequest> pivots;
+  }
+  
+  public static class AnalyticsPivotRequest {
+    public String name;
+    public String expression;
+    public AnalyticsSortRequest sort;
+  }
+
+  @JsonInclude(Include.NON_EMPTY)
+  public static class AnalyticsSortRequest {
+    public List<AnalyticsSortCriteriaRequest> criteria;
+    public int limit = DEFAULT_LIMIT;
+    public int offset = DEFAULT_OFFSET;
+  }
+
+  @JsonTypeInfo(
+      use = JsonTypeInfo.Id.NAME,
+      include = JsonTypeInfo.As.PROPERTY,
+      property = "type"
+  )
+  @JsonSubTypes({
+    @Type(value = AnalyticsExpressionSortRequest.class, name = "expression"),
+    @Type(value = AnalyticsFacetValueSortRequest.class, name = "facetvalue") }
+  )
+  @JsonInclude(Include.NON_EMPTY)
+  public static abstract class AnalyticsSortCriteriaRequest {
+    public String direction;
+  }
+
+  @JsonTypeName("expression")
+  public static class AnalyticsExpressionSortRequest extends AnalyticsSortCriteriaRequest {
+    public String expression;
+  }
+
+  @JsonTypeName("facetvalue")
+  public static class AnalyticsFacetValueSortRequest extends AnalyticsSortCriteriaRequest { }
+
+  @JsonTypeName("range")
+  public static class AnalyticsRangeFacetRequest implements AnalyticsFacetRequest {
+    public String field;
+    public String start;
+    public String end;
+    public List<String> gaps;
+    public boolean hardend = DEFAULT_HARDEND;
+    public List<String> include;
+    public List<String> others;
+  }
+
+  @JsonTypeName("query")
+  public static class AnalyticsQueryFacetRequest implements AnalyticsFacetRequest {
+    public Map<String, String> queries;
+  }
+  
+  /* ***************
+   * Request & Groupings 
+   * ***************/
+  
+  public static AnalyticsRequestManager parse(AnalyticsRequest request, ExpressionFactory expressionFactory, boolean isDistribRequest) throws SolrException {
+    AnalyticsRequestManager manager = constructRequest(request, expressionFactory, isDistribRequest);
+    if (isDistribRequest) {
+      try {
+        manager.analyticsRequest = mapper.writeValueAsString(request);
+      } catch (JsonProcessingException e) {
+        throw new RuntimeException(e);
+      }
+    }
+    return manager;
+  }
+  
+  public static AnalyticsRequestManager parse(String rawRequest, ExpressionFactory expressionFactory, boolean isDistribRequest) throws SolrException {
+    JsonParser parser;
+    try {
+      parser = new JsonFactory().createParser(rawRequest)
+          .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true)
+          .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
+    } catch (IOException e1) {
+      throw new RuntimeException(e1);
+    }
+    AnalyticsRequest request;
+    try {
+      request = mapper.readValue(parser, AnalyticsRequest.class);
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+
+    AnalyticsRequestManager manager = constructRequest(request, expressionFactory, isDistribRequest);
+    if (isDistribRequest) {
+      manager.analyticsRequest = rawRequest;
+    }
+    return manager;
+  }
+  
+  private static AnalyticsRequestManager constructRequest(AnalyticsRequest request, ExpressionFactory expressionFactory, boolean isDistribRequest) throws SolrException {
+    expressionFactory.startRequest();
+    
+    // Functions
+    if (request.functions != null) {
+      request.functions.forEach( (funcSig, retSig) -> expressionFactory.addUserDefinedVariableFunction(funcSig, retSig));
+    }
+    
+    // Expressions
+    Map<String,AnalyticsExpression> topLevelExpressions;
+    if (request.expressions != null) {
+      topLevelExpressions = constructExpressions(request.expressions, expressionFactory);
+    } else {
+      topLevelExpressions = new HashMap<>();
+    }
+    AnalyticsRequestManager manager = new AnalyticsRequestManager(expressionFactory.createReductionManager(isDistribRequest), topLevelExpressions.values());
+    
+    // Groupings
+    if (request.groupings != null) {
+      request.groupings.forEach( (name, grouping) -> {
+        manager.addGrouping(constructGrouping(name, grouping, expressionFactory, isDistribRequest));
+      });
+    }
+    return manager;
+  }
+  
+  private static AnalyticsGroupingManager constructGrouping(String name, AnalyticsGroupingRequest grouping, ExpressionFactory expressionFactory, boolean isDistribRequest) throws SolrException {
+    expressionFactory.startGrouping();
+    
+    // Expressions
+    if (grouping.expressions == null || grouping.expressions.size() == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"Groupings must contain at least one expression, '" + name + "' has none.");
+    }
+    
+    Map<String,AnalyticsExpression> expressions = constructExpressions(grouping.expressions, expressionFactory);
+    AnalyticsGroupingManager manager = new AnalyticsGroupingManager(name, 
+                                                                    expressionFactory.createGroupingReductionManager(isDistribRequest), 
+                                                                    expressions.values());
+    
+    if (grouping.facets == null) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"Groupings must contain at least one facet, '" + name + "' has none.");
+    }
+    // Parse the facets
+    grouping.facets.forEach( (facetName, facet) -> {
+      if (facet instanceof AnalyticsValueFacetRequest) {
+        manager.addFacet(constructValueFacet(facetName, (AnalyticsValueFacetRequest) facet, expressionFactory, expressions));
+      } else if (facet instanceof AnalyticsPivotFacetRequest) {
+        manager.addFacet(constructPivotFacet(facetName, (AnalyticsPivotFacetRequest) facet, expressionFactory, expressions));
+      } else if (facet instanceof AnalyticsRangeFacetRequest) {
+        manager.addFacet(constructRangeFacet(facetName, (AnalyticsRangeFacetRequest) facet, expressionFactory.getSchema()));
+      } else if (facet instanceof AnalyticsQueryFacetRequest) {
+        manager.addFacet(constructQueryFacet(facetName, (AnalyticsQueryFacetRequest) facet));
+      } else {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The facet type, '" + facet.getClass().toString() + "' in "
+            + "grouping '" + name + "' is not a valid type of facet"); 
+      }
+    });
+    
+    return manager;
+  }
+  
+  /* ***************
+   * Expression & Functions 
+   * ***************/
+  
+  private static Map<String, AnalyticsExpression> constructExpressions(Map<String, String> rawExpressions, ExpressionFactory expressionFactory) throws SolrException {
+    Map<String, AnalyticsExpression> expressions = new HashMap<>();
+    rawExpressions.forEach( (name, expression) -> {
+      AnalyticsValueStream exprVal = expressionFactory.createExpression(expression);
+      if (exprVal instanceof AnalyticsValue) {
+        if (exprVal.getExpressionType().isReduced()) {
+          expressions.put(name, (new AnalyticsExpression(name, (AnalyticsValue)exprVal)));
+        } else {
+          throw new SolrException(ErrorCode.BAD_REQUEST,"Top-level expressions must be reduced, the '" + name + "' expression is not.");
+        }
+      } else {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"Top-level expressions must be single-valued, the '" + name + "' expression is not.");
+      }
+    });
+    return expressions;
+  }
+  
+  /* ***************
+   * FACETS 
+   * ***************/
+  
+  /*
+   * Value Facets
+   */
+  
+  private static ValueFacet constructValueFacet(String name, AnalyticsValueFacetRequest facetRequest, ExpressionFactory expressionFactory, Map<String, AnalyticsExpression> expressions) throws SolrException {
+    if (facetRequest.expression == null) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "Value Facets must contain a mapping expression to facet over, '" + name + "' has none.");
+    }
+    
+    // The second parameter must be a mapping expression
+    AnalyticsValueStream expr = expressionFactory.createExpression(facetRequest.expression);
+    if (!expr.getExpressionType().isUnreduced()) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "Value Facet expressions must be mapping expressions, "
+          + "the following expression in value facet '" + name + "' contains a reduction: " + facetRequest.expression);
+    }
+    if (!(expr instanceof StringValueStream)) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "Value Facet expressions must be castable to string expressions, "
+          + "the following expression in value facet '" + name + "' is not: " + facetRequest.expression);
+    }
+    
+    ValueFacet facet = new ValueFacet(name, (StringValueStream)expr);
+    
+    // Check if the value facet is sorted
+    if (facetRequest.sort != null) {
+      facet.setSort(constructSort(facetRequest.sort, expressions));
+    }
+    return facet;
+  }
+
+  /*
+   * Pivot Facets
+   */
+  
+  private static PivotFacet constructPivotFacet(String name, AnalyticsPivotFacetRequest facetRequest, ExpressionFactory expressionFactory, Map<String, AnalyticsExpression> expressions) throws SolrException {
+    PivotNode<?> topPivot = null;
+    
+    // Pivots
+    if (facetRequest.pivots == null || facetRequest.pivots.size() == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "Pivot Facets must contain at least one pivot to facet over, '" + name + "' has none.");
+    }
+    
+    ListIterator<AnalyticsPivotRequest> iter = facetRequest.pivots.listIterator(facetRequest.pivots.size());
+    while (iter.hasPrevious()) {
+      topPivot = constructPivot(iter.previous(), topPivot, expressionFactory, expressions);
+    }
+    
+    return new PivotFacet(name, topPivot);
+  }
+  
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  private static PivotNode<?> constructPivot(AnalyticsPivotRequest pivotRequest,
+                                      PivotNode<?> childPivot,
+                                      ExpressionFactory expressionFactory,
+                                      Map<String, AnalyticsExpression> expressions) throws SolrException {
+    if (pivotRequest.name == null || pivotRequest.name.length() == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "Pivots must have a name.");
+    }
+    if (pivotRequest.expression == null) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "Pivots must have an expression to facet over, '" + pivotRequest.name + "' does not.");
+    }
+    
+    // The second parameter must be a mapping expression
+    AnalyticsValueStream expr = expressionFactory.createExpression(pivotRequest.expression);
+    if (!expr.getExpressionType().isUnreduced()) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "Pivot expressions must be mapping expressions, "
+          + "the following expression in pivot '" + pivotRequest.name + "' contains a reduction: " + pivotRequest.expression);
+    }
+    if (!(expr instanceof StringValueStream)) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "Pivot expressions must be castable to string expressions, "
+          + "the following expression in pivot '" + pivotRequest.name + "' is not: '" + pivotRequest.expression);
+    }
+    
+    PivotNode<?> pivot;
+    if (childPivot == null) {
+      pivot = new PivotNode.PivotLeaf(pivotRequest.name, (StringValueStream)expr);
+    } else {
+      pivot = new PivotNode.PivotBranch(pivotRequest.name, (StringValueStream)expr, childPivot);
+    }
+    
+    // Check if the pivot is sorted
+    if (pivotRequest.sort != null) {
+      pivot.setSort(constructSort(pivotRequest.sort, expressions));
+    }
+    return pivot;
+  }
+  
+  /*
+   * Range Facets
+   */
+
+  private static RangeFacet constructRangeFacet(String name, AnalyticsRangeFacetRequest facetRequest, IndexSchema schema) throws SolrException {
+    if (facetRequest.field == null || facetRequest.field.length() == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "Range Facets must specify a field to facet over, '" +name + "' does not.");
+    }
+    SchemaField field = schema.getFieldOrNull(facetRequest.field);
+    if (field == null) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "Range Facets must have a valid field as the second parameter. The '" + name + "' facet "
+          + "tries to facet over the non-existent field: " + facetRequest.field);
+    }
+
+    if (facetRequest.start == null || facetRequest.start.length() == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "Range Facets must specify a start value, '" +name + "' does not.");
+    }
+    if (facetRequest.end == null || facetRequest.end.length() == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "Range Facets must specify a end value, '" +name + "' does not.");
+    }
+    if (facetRequest.gaps == null || facetRequest.gaps.size() == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "Range Facets must specify a gap or list of gaps to determine facet buckets, '" +name + "' does not.");
+    }
+    RangeFacet facet = new RangeFacet(name, field, facetRequest.start, facetRequest.end, facetRequest.gaps);
+
+    facet.setHardEnd(facetRequest.hardend);
+
+    if (facetRequest.include != null && facetRequest.include.size() > 0) {
+      facet.setInclude(constructInclude(facetRequest.include));
+    }
+    if (facetRequest.others != null && facetRequest.others.size() > 0) {
+      facet.setOthers(constructOthers(facetRequest.others, name));
+    }
+    return facet;
+  }
+
+  private static EnumSet<FacetRangeInclude> constructInclude(List<String> includes) throws SolrException {
+    return FacetRangeInclude.parseParam(includes.toArray(new String[includes.size()]));
+  }
+
+  private static EnumSet<FacetRangeOther> constructOthers(List<String> othersRequest, String facetName) throws SolrException {
+    EnumSet<FacetRangeOther> others = EnumSet.noneOf(FacetRangeOther.class);
+    for (String rawOther : othersRequest) {
+      if (!others.add(FacetRangeOther.get(rawOther))) {
+        throw new SolrException(ErrorCode.BAD_REQUEST, "Duplicate include value '" + rawOther + "' found in range facet '" + facetName + "'");
+      }
+    }
+    if (others.contains(FacetRangeOther.NONE)) {
+      if (others.size() > 1) {
+        throw new SolrException(ErrorCode.BAD_REQUEST, "Include value 'NONE' is used with other includes in a range facet '" + facetName + "'. "
+            + "If 'NONE' is used, it must be the only include.");
+      }
+      return EnumSet.noneOf(FacetRangeOther.class);
+    }
+    if (others.contains(FacetRangeOther.ALL)) {
+      if (others.size() > 1) {
+        throw new SolrException(ErrorCode.BAD_REQUEST, "Include value 'ALL' is used with other includes in a range facet '" + facetName + "'. "
+            + "If 'ALL' is used, it must be the only include.");
+      }
+      return EnumSet.of(FacetRangeOther.BEFORE, FacetRangeOther.BETWEEN, FacetRangeOther.AFTER);
+    }
+    return others;
+  }
+
+  /*
+   * Query Facets
+   */
+  
+  private static QueryFacet constructQueryFacet(String name, AnalyticsQueryFacetRequest facetRequest) throws SolrException {
+    if (facetRequest.queries == null || facetRequest.queries.size() == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "Query Facets must be contain at least 1 query to facet over, '" + name + "' does not.");
+    }
+    
+    // The first param must be the facet name
+    return new QueryFacet(name, facetRequest.queries);
+  }
+  
+  /*
+   * Facet Sorting
+   */
+  
+  private static FacetSortSpecification constructSort(AnalyticsSortRequest sortRequest, Map<String, AnalyticsExpression> expressions) throws SolrException {
+    if (sortRequest.criteria == null || sortRequest.criteria.size() == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "Sorts must be given at least 1 criteria.");
+    }
+
+    return new FacetSortSpecification(constructSortCriteria(sortRequest.criteria, expressions), sortRequest.limit, sortRequest.offset);
+  }
+
+  private static FacetResultsComparator constructSortCriteria(List<AnalyticsSortCriteriaRequest> criteria, Map<String, AnalyticsExpression> expressions) {
+    ArrayList<FacetResultsComparator> comparators = new ArrayList<>();
+    for (AnalyticsSortCriteriaRequest criterion : criteria) {
+      FacetResultsComparator comparator;
+      if (criterion instanceof AnalyticsExpressionSortRequest) {
+        comparator = constructExpressionSortCriteria((AnalyticsExpressionSortRequest) criterion, expressions);
+      } else if (criterion instanceof AnalyticsFacetValueSortRequest) {
+        comparator = constructFacetValueSortCriteria((AnalyticsFacetValueSortRequest) criterion);
+      } else {
+        // Shouldn't happen
+        throw new SolrException(ErrorCode.BAD_REQUEST,"Sort Criteria must either be expressions or facetValues, '" + criterion.getClass().getName() + "' given.");
+      }
+      if (criterion.direction != null && criterion.direction.length() > 0) {
+        if (sortAscending.test(criterion.direction)) {
+          comparator.setDirection(true);
+        } else if (sortDescending.test(criterion.direction)) {
+          comparator.setDirection(false);
+        } else {
+          throw new SolrException(ErrorCode.BAD_REQUEST,"Sort direction '" + criterion.direction + " is not a recognized direction.");
+        }
+      }
+      comparators.add(comparator);
+    }
+    return DelegatingComparator.joinComparators(comparators);
+  }
+  
+  private static FacetResultsComparator constructExpressionSortCriteria(AnalyticsExpressionSortRequest criterion, Map<String, AnalyticsExpression> expressions) {
+    if (criterion.expression == null || criterion.expression.length() == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"Expression Sorts must contain an expression parameter, none given.");
+    }
+
+    AnalyticsExpression expression = expressions.get(criterion.expression);
+    if (expression == null) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"Sort Expression not defined within the grouping: " + criterion.expression);
+    }
+    if (!(expression.getExpression() instanceof ComparableValue)) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"Expression Sorts must be comparable, the following is not: " + criterion.expression);
+    }
+    return ((ComparableValue)expression.getExpression()).getObjectComparator(expression.getName());
+  }
+  
+  private static FacetResultsComparator constructFacetValueSortCriteria(AnalyticsFacetValueSortRequest criterion) {
+    return new FacetValueComparator();
+  }
+}