You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ab...@apache.org on 2017/07/03 09:36:52 UTC

[12/59] [abbrv] lucene-solr:jira/solr-10878: SOLR-10123: Upgraded the Analytics Component to version 2.0

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