lucene-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From a.@apache.org
Subject [22/50] [abbrv] lucene-solr:jira/solr-11320: SOLR-11145, SOLR-11146: Added comprehensive unit tests for Analytics Component 2.0 as well as analytics bug fixes.
Date Thu, 19 Oct 2017 10:36:29 GMT
SOLR-11145, SOLR-11146: Added comprehensive unit tests for Analytics Component 2.0 as well as analytics bug fixes.


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

Branch: refs/heads/jira/solr-11320
Commit: 86d84bff82ff8c7a1f6cadfe307685a4dce2f8d9
Parents: 5446979
Author: Houston Putman <hputman1@bloomberg.net>
Authored: Tue Aug 1 15:01:17 2017 +0000
Committer: Dennis Gove <dpgove@gmail.com>
Committed: Tue Oct 17 13:52:02 2017 -0400

----------------------------------------------------------------------
 .../solr/analytics/AnalyticsRequestParser.java  |   26 -
 .../solr/analytics/ExpressionFactory.java       |  136 +-
 .../function/field/AnalyticsField.java          |    5 +
 .../function/mapping/BottomFunction.java        |    4 +-
 .../function/mapping/CompareFunction.java       |  614 ---------
 .../function/mapping/ComparisonFunction.java    |  318 +++++
 .../function/mapping/ConcatFunction.java        |   95 +-
 .../function/mapping/DateMathFunction.java      |    8 +-
 .../function/mapping/DateParseFunction.java     |   24 +-
 .../DecimalNumericConversionFunction.java       |  271 ++++
 .../function/mapping/EqualFunction.java         |  238 ++++
 .../function/mapping/ExistsFunction.java        |  123 ++
 .../function/mapping/FillMissingFunction.java   |    5 +-
 .../function/mapping/FilterFunction.java        |    3 +-
 .../analytics/function/mapping/IfFunction.java  |    3 +-
 .../function/mapping/JoinFunction.java          |   57 -
 .../function/mapping/LogicFunction.java         |    2 +-
 .../mapping/NumericConvertFunction.java         |  256 ----
 .../function/mapping/RemoveFunction.java        |   39 +-
 .../function/mapping/ReplaceFunction.java       |   65 +-
 .../analytics/function/mapping/TopFunction.java |    4 +-
 .../function/reduction/DocCountFunction.java    |    2 +-
 .../function/reduction/MeanFunction.java        |   97 ++
 .../function/reduction/OrdinalFunction.java     |   44 +-
 .../function/reduction/PercentileFunction.java  |   16 +-
 .../function/reduction/SumFunction.java         |    2 +-
 .../reduction/data/SortedListCollector.java     |   25 +-
 .../plugin/AnalyticsStatisticsCollector.java    |  104 --
 .../solr/analytics/util/OldAnalyticsParams.java |    3 +-
 .../solr/analytics/util/OrdinalCalculator.java  |    5 +-
 .../solr/analytics/value/AnalyticsValue.java    |    6 +-
 .../analytics/value/AnalyticsValueStream.java   |   17 +
 .../solr/analytics/value/BooleanValue.java      |    8 +
 .../analytics/value/BooleanValueStream.java     |    4 +
 .../apache/solr/analytics/value/DateValue.java  |   11 +-
 .../solr/analytics/value/DateValueStream.java   |    6 +-
 .../solr/analytics/value/DoubleValue.java       |    8 +
 .../solr/analytics/value/DoubleValueStream.java |    4 +
 .../apache/solr/analytics/value/FloatValue.java |    8 +
 .../solr/analytics/value/FloatValueStream.java  |    4 +
 .../apache/solr/analytics/value/IntValue.java   |    8 +
 .../solr/analytics/value/IntValueStream.java    |    4 +
 .../apache/solr/analytics/value/LongValue.java  |    8 +
 .../solr/analytics/value/LongValueStream.java   |    4 +
 .../solr/analytics/value/StringValue.java       |    8 +
 .../solr/analytics/value/StringValueStream.java |    4 +
 .../analytics/value/constant/ConstantValue.java |   73 +-
 .../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 -
 .../solr/analytics/legacy/expressions.txt       |   65 +
 .../solr/analytics/legacy/facetSorting.txt      |    4 +
 .../solr/analytics/legacy/fieldFacetExtras.txt  |   66 +
 .../solr/analytics/legacy/fieldFacets.txt       |  132 ++
 .../solr/analytics/legacy/functions.txt         |   57 +
 .../solr/analytics/legacy/noFacets.txt          |   74 ++
 .../solr/analytics/legacy/queryFacets.txt       |   27 +
 .../solr/analytics/legacy/rangeFacets.txt       |  161 +++
 .../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  |  123 +-
 .../configsets/cloud-analytics/conf/schema.xml  |   66 +-
 .../AbstractAnalyticsStatsCloudTest.java        |  187 ---
 .../analytics/AbstractAnalyticsStatsTest.java   |  235 ----
 .../solr/analytics/ExpressionFactoryTest.java   |  249 ++++
 .../apache/solr/analytics/NoFacetCloudTest.java |  557 --------
 .../org/apache/solr/analytics/NoFacetTest.java  |  536 +++-----
 .../solr/analytics/OverallAnalyticsTest.java    |  320 +++++
 .../solr/analytics/SolrAnalyticsTestCase.java   |  349 +++++
 .../analytics/expression/ExpressionTest.java    |  201 ---
 .../solr/analytics/expression/FunctionTest.java |  221 ----
 .../facet/AbstractAnalyticsFacetCloudTest.java  |  284 ----
 .../facet/AbstractAnalyticsFacetTest.java       |  343 -----
 .../solr/analytics/facet/FacetSortingTest.java  |   53 -
 .../analytics/facet/FieldFacetCloudTest.java    | 1212 ------------------
 .../facet/FieldFacetExtrasCloudTest.java        |  253 ----
 .../analytics/facet/FieldFacetExtrasTest.java   |  170 ---
 .../solr/analytics/facet/FieldFacetTest.java    | 1065 ---------------
 .../solr/analytics/facet/PivotFacetTest.java    |  230 ++++
 .../analytics/facet/QueryFacetCloudTest.java    |  159 ---
 .../solr/analytics/facet/QueryFacetTest.java    |  150 +--
 .../analytics/facet/RangeFacetCloudTest.java    |  588 ---------
 .../solr/analytics/facet/RangeFacetTest.java    |  620 ++++-----
 .../facet/SolrAnalyticsFacetTestCase.java       |   80 ++
 .../solr/analytics/facet/ValueFacetTest.java    |  433 +++++++
 .../field/AbstractAnalyticsFieldTest.java       |  299 +++++
 .../function/field/BooleanFieldsTest.java       |   71 +
 .../function/field/DateFieldsTest.java          |  108 ++
 .../function/field/DoubleFieldsTest.java        |  108 ++
 .../function/field/FloatFieldsTest.java         |  108 ++
 .../analytics/function/field/IntFieldsTest.java |  108 ++
 .../function/field/LongFieldsTest.java          |  108 ++
 .../function/field/StringFieldsTest.java        |   71 +
 .../mapping/AbsoluteValueFunctionTest.java      |  267 ++++
 .../function/mapping/AddFunctionTest.java       |  156 +++
 .../function/mapping/AndFunctionTest.java       |  192 +++
 .../function/mapping/BottomFunctionTest.java    |  406 ++++++
 .../function/mapping/CeilingFunctionTest.java   |  180 +++
 .../function/mapping/ConcatFunctionTest.java    |  317 +++++
 .../function/mapping/DateMathFunctionTest.java  |  127 ++
 .../function/mapping/DateParseFunctionTest.java |  245 ++++
 .../function/mapping/DivideFunctionTest.java    |  128 ++
 .../function/mapping/EqualFunctionTest.java     |  530 ++++++++
 .../function/mapping/ExistsFunctionTest.java    |   74 ++
 .../mapping/FillMissingFunctionTest.java        |  717 +++++++++++
 .../function/mapping/FilterFunctionTest.java    | 1035 +++++++++++++++
 .../function/mapping/FloorFunctionTest.java     |  180 +++
 .../function/mapping/GTEFunctionTest.java       |  290 +++++
 .../function/mapping/GTFunctionTest.java        |  290 +++++
 .../function/mapping/IfFunctionTest.java        |  994 ++++++++++++++
 .../function/mapping/LTEFunctionTest.java       |  290 +++++
 .../function/mapping/LTFunctionTest.java        |  290 +++++
 .../function/mapping/LogFunctionTest.java       |  186 +++
 .../function/mapping/MultFunctionTest.java      |  155 +++
 .../function/mapping/NegateFunctionTest.java    |  327 +++++
 .../function/mapping/OrFunctionTest.java        |  192 +++
 .../function/mapping/PowerFunctionTest.java     |  128 ++
 .../function/mapping/RemoveFunctionTest.java    |  849 ++++++++++++
 .../function/mapping/ReplaceFunctionTest.java   |  990 ++++++++++++++
 .../function/mapping/RoundFunctionTest.java     |  180 +++
 .../mapping/StringCastFunctionTest.java         |   87 ++
 .../function/mapping/SubtractFunctionTest.java  |  128 ++
 .../function/mapping/TopFunctionTest.java       |  406 ++++++
 .../LegacyAbstractAnalyticsCloudTest.java       |  167 +++
 .../legacy/LegacyAbstractAnalyticsTest.java     |  234 ++++
 .../legacy/LegacyNoFacetCloudTest.java          |  551 ++++++++
 .../analytics/legacy/LegacyNoFacetTest.java     |  450 +++++++
 .../legacy/expression/LegacyExpressionTest.java |  201 +++
 .../legacy/expression/LegacyFunctionTest.java   |  221 ++++
 .../LegacyAbstractAnalyticsFacetCloudTest.java  |  194 +++
 .../facet/LegacyAbstractAnalyticsFacetTest.java |  342 +++++
 .../legacy/facet/LegacyFacetSortingTest.java    |   53 +
 .../legacy/facet/LegacyFieldFacetCloudTest.java | 1212 ++++++++++++++++++
 .../facet/LegacyFieldFacetExtrasCloudTest.java  |  253 ++++
 .../facet/LegacyFieldFacetExtrasTest.java       |  170 +++
 .../legacy/facet/LegacyFieldFacetTest.java      | 1065 +++++++++++++++
 .../legacy/facet/LegacyQueryFacetCloudTest.java |  159 +++
 .../legacy/facet/LegacyQueryFacetTest.java      |  124 ++
 .../legacy/facet/LegacyRangeFacetCloudTest.java |  588 +++++++++
 .../legacy/facet/LegacyRangeFacetTest.java      |  417 ++++++
 .../value/CastingAnalyticsValueTest.java        |   56 +
 .../value/CastingBooleanValueStreamTest.java    |   79 ++
 .../value/CastingBooleanValueTest.java          |  159 +++
 .../value/CastingDateValueStreamTest.java       |  111 ++
 .../analytics/value/CastingDateValueTest.java   |  172 +++
 .../value/CastingDoubleValueStreamTest.java     |   79 ++
 .../analytics/value/CastingDoubleValueTest.java |  159 +++
 .../value/CastingFloatValueStreamTest.java      |  102 ++
 .../analytics/value/CastingFloatValueTest.java  |  198 +++
 .../value/CastingIntValueStreamTest.java        |  148 +++
 .../analytics/value/CastingIntValueTest.java    |  276 ++++
 .../value/CastingLongValueStreamTest.java       |  102 ++
 .../analytics/value/CastingLongValueTest.java   |  198 +++
 .../value/CastingStringValueStreamTest.java     |   56 +
 .../analytics/value/CastingStringValueTest.java |  120 ++
 .../solr/analytics/value/ConstantValueTest.java |  368 ++++++
 .../solr/analytics/value/FillableTestValue.java |  641 +++++++++
 160 files changed, 25063 insertions(+), 8314 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/86d84bff/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
index bfa62e2..bcb7747 100644
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestParser.java
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/AnalyticsRequestParser.java
@@ -73,34 +73,8 @@ public class AnalyticsRequestParser {
   
   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();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/86d84bff/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
index 1d7a793..18020b7 100644
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/ExpressionFactory.java
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/ExpressionFactory.java
@@ -17,6 +17,7 @@
 package org.apache.solr.analytics;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
@@ -28,15 +29,14 @@ 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.ComparisonFunction.GTEFunction;
+import org.apache.solr.analytics.function.mapping.ComparisonFunction.GTFunction;
+import org.apache.solr.analytics.function.mapping.ComparisonFunction.LTEFunction;
+import org.apache.solr.analytics.function.mapping.ComparisonFunction.LTFunction;
+import org.apache.solr.analytics.function.mapping.ConcatFunction.SeparatedConcatFunction;
+import org.apache.solr.analytics.function.mapping.DecimalNumericConversionFunction.CeilingFunction;
+import org.apache.solr.analytics.function.mapping.DecimalNumericConversionFunction.FloorFunction;
+import org.apache.solr.analytics.function.mapping.DecimalNumericConversionFunction.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.*;
@@ -177,13 +177,17 @@ public class ExpressionFactory {
   /**
    * 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 functionName the function's name
+   * @param functionParams the comma separated and ordered parameters of the function (e.g. {@code "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);
+  public ExpressionFactory addSystemVariableFunction(final String functionName, final String functionParams, final String returnSignature) throws SolrException {
+    return addVariableFunction(functionName, 
+                               Arrays.stream(functionParams.split(",")).map(param -> param.trim()).toArray(size -> new String[size]),
+                               returnSignature,
+                               systemVariableFunctions);
   }
 
   /**
@@ -210,12 +214,29 @@ public class ExpressionFactory {
   private ExpressionFactory addVariableFunction(final String functionSignature,
                                                 final String returnSignature,
                                                 Map<String,VariableFunctionInfo> variableFunctions) throws SolrException {
-    String functionName = getFunctionName(functionSignature);
+    addVariableFunction(getFunctionName(functionSignature), getParams(functionSignature, null, null), returnSignature, variableFunctions);
+    return this;
+  }
+  
+  /**
+   * Add a variable function to the given map of variable functions. 
+   * 
+   * @param functionName the function's name
+   * @param functionParams the parameters of the function (this is ordered)
+   * @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 functionName,
+                                                final String[] functionParams,
+                                                final String returnSignature,
+                                                Map<String,VariableFunctionInfo> variableFunctions) throws SolrException {
     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.params = functionParams;
     varFuncInfo.returnSignature = returnSignature;
     variableFunctions.put(functionName, varFuncInfo);
     return this;
@@ -292,13 +313,9 @@ public class ExpressionFactory {
       // 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); 
-        }  
+        // Try to make a field out of it
+        expression = createField(schema.getField(expressionStr));
+        isField = true;
       } else {
         // Must be a function
         expression = createFunction(expressionStr, varFuncParams, varFuncVarParamName, varFuncVarParamValues);
@@ -367,6 +384,9 @@ public class ExpressionFactory {
 
     final String[] params = getParams(expressionStr, varFuncVarParamName, varFuncVarParamValues);
     AnalyticsValueStream[] paramStreams = new AnalyticsValueStream[params.length];
+    
+    boolean allParamsConstant = true;
+    
     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])) {
@@ -374,7 +394,11 @@ public class ExpressionFactory {
       } else {
         paramStreams[i] = createExpression(params[i], varFuncParams, varFuncVarParamName, varFuncVarParamValues);
       }
+      
+      // Then update whether all of the params are constant
+      allParamsConstant &= paramStreams[i].getExpressionType().equals(ExpressionType.CONST);
     }
+    
     // 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)) {
@@ -386,6 +410,10 @@ public class ExpressionFactory {
       
       boolean varLenEnd = false;
       
+      if (paramStreams.length < newVarFunc.params.length) {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The variable function '" + name + "' requires at least " + newVarFunc.params.length + " parameters."
+            + " Only " + paramStreams.length + " arguments given in the following invocation : " + expressionStr);
+      }
       for (int i = 0; i < newVarFunc.params.length; ++i) {
         String variable = newVarFunc.params[i];
         if (variable.endsWith(variableLengthParamSuffix)) {
@@ -408,16 +436,23 @@ public class ExpressionFactory {
         }
       }
       if (!varLenEnd) {
+        if (paramStreams.length > newVarFunc.params.length) {
+          throw new SolrException(ErrorCode.BAD_REQUEST,"The variable function '" + name + "' requires " + newVarFunc.params.length + " parameters."
+              + " " + paramStreams.length + " arguments given in the following invocation : " + expressionStr);
+        }
         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);
     }
+    
+    // If the all params are constant, then try to convert the expression to a constant value.
+    expression = expression.convertToConstant();
+    
     return expression;
   }
 
@@ -751,19 +786,18 @@ public class ExpressionFactory {
     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(SeparatedConcatFunction.name, SeparatedConcatFunction.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(ExistsFunction.name,ExistsFunction.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);
@@ -780,7 +814,9 @@ public class ExpressionFactory {
     
     // Reduction Functions
     expressionCreators.put(CountFunction.name, CountFunction.creatorFunction);
+    expressionCreators.put(DocCountFunction.name, DocCountFunction.creatorFunction);
     expressionCreators.put(MaxFunction.name, MaxFunction.creatorFunction);
+    expressionCreators.put(MeanFunction.name, MeanFunction.creatorFunction);
     expressionCreators.put(MedianFunction.name, MedianFunction.creatorFunction);
     expressionCreators.put(MinFunction.name, MinFunction.creatorFunction);
     expressionCreators.put(MissingFunction.name, MissingFunction.creatorFunction);
@@ -790,14 +826,13 @@ public class ExpressionFactory {
     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(_,';'),''))");
+    addSystemVariableFunction(WeightedMeanVariableFunction.name, WeightedMeanVariableFunction.params, WeightedMeanVariableFunction.function);
+    addSystemVariableFunction(SumOfSquaresVariableFunction.name, SumOfSquaresVariableFunction.params, SumOfSquaresVariableFunction.function);
+    addSystemVariableFunction(SquareRootVariableFunction.name, SquareRootVariableFunction.params, SquareRootVariableFunction.function);
+    addSystemVariableFunction(VarianceVariableFunction.name, VarianceVariableFunction.params, VarianceVariableFunction.function);
+    addSystemVariableFunction(SandardDeviationVariableFunction.name, SandardDeviationVariableFunction.params, SandardDeviationVariableFunction.function);
+    addSystemVariableFunction(CSVVariableFunction.name, CSVVariableFunction.params, CSVVariableFunction.function);
+    addSystemVariableFunction(CSVOutputVariableFunction.name, CSVOutputVariableFunction.params, CSVOutputVariableFunction.function);
   }
 
   /**
@@ -818,4 +853,39 @@ public class ExpressionFactory {
 class VariableFunctionInfo {
   public String[] params;
   public String returnSignature;
+}
+class WeightedMeanVariableFunction {
+  public static final String name = "wmean";
+  public static final String params = "a,b";
+  public static final String function = DivideFunction.name+"("+SumFunction.name+"("+MultFunction.name+"(a,b)),"+SumFunction.name+"("+FilterFunction.name+"(b,"+ExistsFunction.name+"(a))))";
+}
+class SumOfSquaresVariableFunction {
+  public static final String name = "sumofsquares";
+  public static final String params = "a";
+  public static final String function = SumFunction.name+"("+PowerFunction.name+"(a,2))";
+}
+class SquareRootVariableFunction {
+  public static final String name = "sqrt";
+  public static final String params = "a";
+  public static final String function = PowerFunction.name+"(a,0.5)";
+}
+class VarianceVariableFunction {
+  public static final String name = "variance";
+  public static final String params = "a";
+  public static final String function = SubtractFunction.name+"("+MeanFunction.name+"("+PowerFunction.name+"(a,2)),"+PowerFunction.name+"("+MeanFunction.name+"(a),2))";
+}
+class SandardDeviationVariableFunction {
+  public static final String name = "stddev";
+  public static final String params = "a";
+  public static final String function = SquareRootVariableFunction.name+"("+VarianceVariableFunction.name+"(a))";
+}
+class CSVVariableFunction {
+  public static final String name = "csv";
+  public static final String params = "a"+ExpressionFactory.variableLengthParamSuffix;
+  public static final String function = SeparatedConcatFunction.name+"(',',a)";
+}
+class CSVOutputVariableFunction {
+  public static final String name = "csv_output";
+  public static final String params = "a"+ExpressionFactory.variableLengthParamSuffix;
+  public static final String function = "concat_sep(',',a"+ExpressionFactory.variableForEachSep+FillMissingFunction.name+"("+SeparatedConcatFunction.name+"(';',"+ExpressionFactory.variableForEachParam+"),''))";
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/86d84bff/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
index dab0358..e0bbb4b 100644
--- 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
@@ -50,6 +50,11 @@ public abstract class AnalyticsField implements AnalyticsValueStream {
     return ExpressionType.FIELD;
   }
   
+  @Override
+  public AnalyticsValueStream convertToConstant() {
+    return this;
+  }
+  
   /**
    * Set the segment reader context
    * 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/86d84bff/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
index f896da4..10674fd 100644
--- 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
@@ -141,7 +141,7 @@ public class BottomFunction {
     }
     if (param instanceof StringValueStream) {
       if (params.length == 1) {
-        return LambdaFunction.createStringLambdaFunction(name, (a,b) -> (a.compareTo(b)<=0)? a:b, (StringValueStream)param);
+        return LambdaFunction.createStringLambdaFunction(name, (a,b) -> (a.compareTo(b)<0)? a:b, (StringValueStream)param);
       }
       StringValue[] castedParams = new StringValue[params.length];
       boolean tryNextType = false;
@@ -154,7 +154,7 @@ public class BottomFunction {
         }
       }
       if (!tryNextType) {
-        return LambdaFunction.createStringLambdaFunction(name, (a,b) -> (a.compareTo(b)<=0)? a:b, castedParams, false);
+        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. " + 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/86d84bff/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
deleted file mode 100644
index a06f7b8..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/CompareFunction.java
+++ /dev/null
@@ -1,614 +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.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/86d84bff/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ComparisonFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ComparisonFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ComparisonFunction.java
new file mode 100644
index 0000000..1bbf9ae
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ComparisonFunction.java
@@ -0,0 +1,318 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.ComparisonFunction.CompResultFunction;
+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.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 comparable (numeric and date) 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 ComparisonFunction {
+  
+  /**
+   * Create a comparison mapping function, comparing two analytics value (streams) of the same type.
+   * 
+   * @param name name of the function
+   * @param comp function to find the result of a comparison
+   * @param params the parameters to compare
+   * @return an instance of the requested comparison function using the given parameters
+   */
+  public static BooleanValueStream createComparisonFunction(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 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 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 {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires comparable (numeric or date) parameters.");
+    }
+    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 ComparisonFunction.createComparisonFunction(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 ComparisonFunction.createComparisonFunction(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 ComparisonFunction.createComparisonFunction(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 ComparisonFunction.createComparisonFunction(name, val -> {
+        return val <= 0;
+      }, params);
+    });
+  }
+
+  @FunctionalInterface
+  public static interface CompResultFunction {
+    public boolean apply(int compResult);
+  }
+  
+  private static CompResultFunction reverse(CompResultFunction original) {
+    return val -> original.apply(val*-1);
+  }
+}
+/**
+ * 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;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/86d84bff/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
index e54e8c5..9b0809e 100644
--- 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
@@ -18,61 +18,90 @@ package org.apache.solr.analytics.function.mapping;
 
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrException.ErrorCode;
+
+import java.util.Arrays;
+
 import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.function.mapping.LambdaFunction.TwoStringInStringOutLambda;
+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;
+import org.apache.solr.analytics.value.constant.ConstantValue;
 
 /**
  * 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. 
+ * Uses:
+ * <ul>
+ * <li>If a single {@link StringValueStream} is passed in, a {@link StringValue} representing the concatenation of the values for each document is returned.
+ * No ordering is guaranteed while concatenating.
+ * <li>If a {@link StringValue} and a {@link StringValueStream} are passed in, a {@link StringValueStream} representing the concatenation 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 any number (more than 0) of {@link StringValue}s are passed in, a {@link StringValue} representing the concatenation of all values is returned.
+ * If any values don't exist, the overall concatenation value will still exist with an empty string used for any missing values. If none of the parameter
+ * values exist, then the overall concatenation value will not exist. 
+ * </ul>
  */
 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);
+    return createConcatFunction(name, name, (a,b) -> a + b, params);
   });
   
   /**
    * 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)
+   * The usage is exactly the same as the {@link ConcatFunction}, however a {@link ConstantStringValue} separator is added as the first argument
+   * of every usage. So the acceptable inputs are as follows:
+   * <ul>
+   * <li>{@value #name} ( {@link ConstantStringValue} , {@link StringValueStream} )
+   * <li>{@value #name} ( {@link ConstantStringValue} , {@link StringValueStream} , {@link StringValue} )
+   * <li>{@value #name} ( {@link ConstantStringValue} , {@link StringValue} , {@link StringValueStream} )
+   * <li>{@value #name} ( {@link ConstantStringValue} , {@link StringValue} ... )
+   * </ul>
+   * The {@link ConstantStringValue} separator is used to separate every two (or more) string during concatenation. If only one string value exists,
+   * then the separator will not be used.
    */
-  public static class ConcatSeparatedFunction {
-    public static final String name = "concatsep";
+  public static class SeparatedConcatFunction {
+    public static final String name = "concat_sep";
     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)) {
+      } else if (!(params[0] instanceof StringValue && params[0] instanceof ConstantValue)) {
         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);
+      final String sep = ((StringValue)params[0]).getString();
+      String uniqueName = name + "(" + sep + ")";
+      return createConcatFunction(name, uniqueName, (a,b) -> a + sep + b, Arrays.copyOfRange(params, 1, params.length));
     });
   }
+  
+  private static StringValueStream createConcatFunction(String functionName, String uniqueName, TwoStringInStringOutLambda lambda, AnalyticsValueStream[] params) {
+    if (params.length == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST, "The "+functionName+" function requires parameters.");
+    } else if (params.length == 1) {
+      if (params[0] instanceof StringValueStream) {
+        return LambdaFunction.createStringLambdaFunction(uniqueName, lambda, (StringValueStream)params[0]);
+      }
+      throw new SolrException(ErrorCode.BAD_REQUEST, "The "+functionName+" function requires that all parameters be string-castable.");
+    } else if (params.length == 2) {
+      // If it is not a pair of a single valued and multi valued string, then it will be taken care of below
+      if (params[0] instanceof StringValueStream && params[1] instanceof StringValueStream
+          && !(params[0] instanceof StringValue && params[1] instanceof StringValue)) {
+        return LambdaFunction.createStringLambdaFunction(uniqueName, lambda, (StringValueStream)params[0], (StringValueStream)params[1]);
+      }
+    }
+    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 "+functionName+" function requires that all parameters be string-castable, and if more than 2 parameters"
+            + " are provided then all must be single-valued.");
+      }
+    }
+    return LambdaFunction.createStringLambdaFunction(uniqueName, lambda, castedParams, false);
+  }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/86d84bff/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
index 9901625..c9dab98 100644
--- 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
@@ -24,9 +24,11 @@ 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.StringValue;
 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.analytics.value.constant.ConstantValue;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrException.ErrorCode;
 import org.apache.solr.util.DateMathParser;
@@ -46,8 +48,8 @@ public class DateMathFunction {
     }
     StringBuilder mathParam = new StringBuilder();
     for (int i = 1; i < params.length; ++i) {
-      if (params[i] instanceof ConstantStringValue) {
-        mathParam.append(((ConstantStringValue) params[i]).getString());
+      if (params[i] instanceof StringValue && params[i] instanceof ConstantValue) {
+        mathParam.append(((StringValue) params[i]).getString());
       } else {
         throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires all math parameters to be a constant strings.");
       }
@@ -84,7 +86,7 @@ class DateMathValueFunction extends AbstractDateValue {
   @Override
   public long getLong() {
     Date date = getDate();
-    return (date == null) ? 0 : date.getTime();
+    return (exists) ? date.getTime() : 0;
   }
   @Override
   public Date getDate() {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/86d84bff/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
index 5929b88..226acac 100644
--- 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
@@ -16,10 +16,8 @@
  */
 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.time.Instant;
+import java.time.format.DateTimeParseException;
 import java.util.function.LongConsumer;
 
 import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
@@ -103,7 +101,6 @@ class LongStreamToDateParseFunction extends AbstractDateValueStream {
   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;
@@ -134,7 +131,6 @@ class StringToDateParseFunction extends AbstractDateValue {
   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;
@@ -147,9 +143,12 @@ class StringToDateParseFunction extends AbstractDateValue {
   public long getLong() {
     long value = 0;
     try {
-      value = formatter.parse(param.toString()).getTime();
+      String paramStr = param.getString();
       exists = param.exists();
-    } catch (ParseException e) {
+      if (exists) {
+        value = Instant.parse(paramStr).toEpochMilli();
+      }
+    } catch (DateTimeParseException e) {
       exists = false;
     }
     return value;
@@ -177,7 +176,6 @@ class StringStreamToDateParseFunction extends AbstractDateValueStream {
   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;
@@ -189,13 +187,11 @@ class StringStreamToDateParseFunction extends AbstractDateValueStream {
   public void streamLongs(LongConsumer cons) {
     param.streamStrings(value -> {
       try {
-        cons.accept(formatter.parse(value).getTime());
-      } catch (ParseException e) {
-      }
+        cons.accept(Instant.parse(value).toEpochMilli());
+      } catch (DateTimeParseException e) {}
     });
   }
 
-  @Override
   public String getName() {
     return name;
   }
@@ -207,4 +203,4 @@ class StringStreamToDateParseFunction extends AbstractDateValueStream {
   public ExpressionType getExpressionType() {
     return funcType;
   }
-}
\ No newline at end of file
+}


Mime
View raw message