You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by dp...@apache.org on 2017/08/26 02:10:44 UTC

[6/6] lucene-solr:master: SOLR-11283: Refactors all Stream Evaluators in solrj.io.eval to simplify them

SOLR-11283: Refactors all Stream Evaluators in solrj.io.eval to simplify them


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

Branch: refs/heads/master
Commit: 4b6f2e5ca781102ae4270b98245f8d658fd0ed3a
Parents: 287ab9d
Author: Dennis Gove <dp...@gmail.com>
Authored: Tue Aug 22 07:30:35 2017 -0400
Committer: Dennis Gove <dp...@gmail.com>
Committed: Fri Aug 25 22:07:37 2017 -0400

----------------------------------------------------------------------
 solr/CHANGES.txt                                |   5 +
 .../apache/solr/handler/AnalyzeEvaluator.java   |   5 +-
 .../org/apache/solr/handler/StreamHandler.java  | 139 +++++++++-
 .../org/apache/solr/handler/sql/SolrTable.java  |  12 +-
 .../solrj/io/eval/AbsoluteValueEvaluator.java   |  32 +--
 .../client/solrj/io/eval/AddAllEvaluator.java   |  69 -----
 .../solr/client/solrj/io/eval/AddEvaluator.java |  48 ++--
 .../solr/client/solrj/io/eval/AndEvaluator.java |  47 +---
 .../client/solrj/io/eval/AnovaEvaluator.java    |  69 ++---
 .../client/solrj/io/eval/AppendEvaluator.java   |  53 ++++
 .../solrj/io/eval/ArcCosineEvaluator.java       |  33 +--
 .../client/solrj/io/eval/ArcSineEvaluator.java  |  33 +--
 .../solrj/io/eval/ArcTangentEvaluator.java      |  33 +--
 .../client/solrj/io/eval/ArrayEvaluator.java    |  85 ++----
 .../solr/client/solrj/io/eval/AscEvaluator.java |  75 +++---
 .../client/solrj/io/eval/BooleanEvaluator.java  |  83 ------
 .../client/solrj/io/eval/CeilingEvaluator.java  |  36 ++-
 .../client/solrj/io/eval/CoalesceEvaluator.java |  21 +-
 .../client/solrj/io/eval/ColumnEvaluator.java   |  83 +++---
 .../client/solrj/io/eval/ComplexEvaluator.java  | 126 ---------
 .../solrj/io/eval/ConditionalEvaluator.java     |  56 ----
 .../solrj/io/eval/ConversionEvaluator.java      | 144 +++++-----
 .../solrj/io/eval/ConvolutionEvaluator.java     |  77 ++----
 .../client/solrj/io/eval/CopyOfEvaluator.java   |  85 +++---
 .../solrj/io/eval/CopyOfRangeEvaluator.java     | 104 ++++----
 .../solrj/io/eval/CorrelationEvaluator.java     |  70 ++---
 .../client/solrj/io/eval/CosineEvaluator.java   |  33 +--
 .../solrj/io/eval/CovarianceEvaluator.java      |  70 ++---
 .../solrj/io/eval/CubedRootEvaluator.java       |  33 +--
 .../io/eval/CumulativeProbabilityEvaluator.java |  61 ++---
 .../client/solrj/io/eval/DescribeEvaluator.java |  63 ++---
 .../client/solrj/io/eval/DistanceEvaluator.java |  82 ------
 .../client/solrj/io/eval/DivideEvaluator.java   |  44 ++--
 .../io/eval/EmpiricalDistributionEvaluator.java |  56 ++--
 .../client/solrj/io/eval/EqualToEvaluator.java  |  73 +++++
 .../client/solrj/io/eval/EqualsEvaluator.java   | 109 --------
 .../io/eval/EuclideanDistanceEvaluator.java     |  56 ++++
 .../solrj/io/eval/EvaluatorException.java       |   4 +-
 .../solrj/io/eval/ExclusiveOrEvaluator.java     |  42 +--
 .../client/solrj/io/eval/FieldEvaluator.java    | 100 -------
 .../solrj/io/eval/FieldValueEvaluator.java      | 100 +++++++
 .../solrj/io/eval/FindDelayEvaluator.java       |  98 +++----
 .../client/solrj/io/eval/FloorEvaluator.java    |  36 ++-
 .../io/eval/GreaterThanEqualToEvaluator.java    |  45 +---
 .../solrj/io/eval/GreaterThanEvaluator.java     |  45 +---
 .../solrj/io/eval/HistogramEvaluator.java       | 108 ++++----
 .../io/eval/HyperbolicCosineEvaluator.java      |  33 +--
 .../solrj/io/eval/HyperbolicSineEvaluator.java  |  33 +--
 .../io/eval/HyperbolicTangentEvaluator.java     |  33 +--
 .../solrj/io/eval/IfThenElseEvaluator.java      |  31 +--
 .../io/eval/KolmogorovSmirnovEvaluator.java     |  93 +++----
 .../client/solrj/io/eval/LengthEvaluator.java   |  56 ++--
 .../solrj/io/eval/LessThanEqualToEvaluator.java |  45 +---
 .../client/solrj/io/eval/LessThanEvaluator.java |  45 +---
 .../client/solrj/io/eval/ManyValueWorker.java   |  25 ++
 .../client/solrj/io/eval/ModuloEvaluator.java   |  42 +--
 .../solrj/io/eval/MovingAverageEvaluator.java   |  82 +++---
 .../client/solrj/io/eval/MultiplyEvaluator.java |  49 ++--
 .../solrj/io/eval/NaturalLogEvaluator.java      |  35 ++-
 .../io/eval/NormalDistributionEvaluator.java    |  45 +---
 .../solrj/io/eval/NormalizeEvaluator.java       |  65 ++---
 .../solr/client/solrj/io/eval/NotEvaluator.java |  45 ++--
 .../client/solrj/io/eval/NumberEvaluator.java   |  89 -------
 .../client/solrj/io/eval/OneValueWorker.java    |  34 +++
 .../solr/client/solrj/io/eval/OrEvaluator.java  |  47 +---
 .../solrj/io/eval/PercentileEvaluator.java      |  69 ++---
 .../client/solrj/io/eval/PowerEvaluator.java    |  29 +-
 .../client/solrj/io/eval/PredictEvaluator.java  |  79 ++----
 .../client/solrj/io/eval/RankEvaluator.java     |  66 ++---
 .../client/solrj/io/eval/RawValueEvaluator.java |   2 +-
 .../io/eval/RecursiveBooleanEvaluator.java      | 112 ++++++++
 .../solrj/io/eval/RecursiveEvaluator.java       | 240 +++++++++++++++++
 .../io/eval/RecursiveNumericEvaluator.java      |  75 ++++++
 .../io/eval/RecursiveNumericListEvaluator.java  |  67 +++++
 .../solrj/io/eval/RecursiveObjectEvaluator.java |  35 +++
 .../io/eval/RecursiveTemporalEvaluator.java     | 114 ++++++++
 .../solrj/io/eval/RegressionEvaluator.java      |  87 +++---
 .../solrj/io/eval/ResidualsEvaluator.java       |  99 +++----
 .../client/solrj/io/eval/ReverseEvaluator.java  |  63 ++---
 .../client/solrj/io/eval/RoundEvaluator.java    |  33 +--
 .../client/solrj/io/eval/SampleEvaluator.java   |  59 ++---
 .../client/solrj/io/eval/ScaleEvaluator.java    |  79 ++----
 .../client/solrj/io/eval/SequenceEvaluator.java |  67 ++---
 .../client/solrj/io/eval/SimpleEvaluator.java   |  36 ---
 .../client/solrj/io/eval/SineEvaluator.java     |  33 +--
 .../client/solrj/io/eval/SourceEvaluator.java   |  36 +++
 .../solrj/io/eval/SquareRootEvaluator.java      |  33 +--
 .../client/solrj/io/eval/StreamEvaluator.java   |   2 +-
 .../solrj/io/eval/StreamEvaluatorException.java |  32 +++
 .../client/solrj/io/eval/SubtractEvaluator.java |  65 ++++-
 .../client/solrj/io/eval/TangentEvaluator.java  |  33 +--
 .../client/solrj/io/eval/TemporalEvaluator.java | 136 ----------
 .../solrj/io/eval/TemporalEvaluatorDay.java     |  16 +-
 .../io/eval/TemporalEvaluatorDayOfQuarter.java  |  18 +-
 .../io/eval/TemporalEvaluatorDayOfYear.java     |  18 +-
 .../solrj/io/eval/TemporalEvaluatorEpoch.java   |  22 +-
 .../solrj/io/eval/TemporalEvaluatorHour.java    |  18 +-
 .../solrj/io/eval/TemporalEvaluatorMinute.java  |  18 +-
 .../solrj/io/eval/TemporalEvaluatorMonth.java   |  18 +-
 .../solrj/io/eval/TemporalEvaluatorQuarter.java |  18 +-
 .../solrj/io/eval/TemporalEvaluatorSecond.java  |  18 +-
 .../solrj/io/eval/TemporalEvaluatorWeek.java    |  18 +-
 .../solrj/io/eval/TemporalEvaluatorYear.java    |  18 +-
 .../client/solrj/io/eval/TwoValueWorker.java    |  34 +++
 .../io/eval/UniformDistributionEvaluator.java   |  45 +---
 .../client/solrj/io/eval/UuidEvaluator.java     |  16 +-
 .../solr/client/solrj/io/eval/ValueWorker.java  |  25 ++
 .../solrj/io/stream/CartesianProductStream.java |   4 +-
 .../client/solrj/io/stream/HavingStream.java    |  20 +-
 .../solrj/io/stream/expr/StreamFactory.java     |   8 +-
 .../solrj/io/stream/StreamExpressionTest.java   |  43 +--
 .../solrj/io/stream/eval/AddEvaluatorTest.java  |  20 +-
 .../io/stream/eval/AppendEvaluatorTest.java     |  62 +++++
 .../solrj/io/stream/eval/AscEvaluatorTest.java  | 107 ++++++++
 .../io/stream/eval/CoalesceEvaluatorTest.java   |   4 +-
 .../io/stream/eval/CompoundEvaluatorTest.java   |  85 ------
 .../stream/eval/ConversionEvaluatorsTest.java   |   2 +-
 .../stream/eval/CorrelationEvaluatorTest.java   |  55 ++++
 .../CumulativeProbabilityEvaluatorTest.java     |  56 ++++
 .../EmpiricalDistributionEvaluatorTest.java     |  57 ++++
 .../io/stream/eval/EqualToEvaluatorTest.java    | 263 +++++++++++++++++++
 .../io/stream/eval/EqualsEvaluatorTest.java     | 263 -------------------
 .../io/stream/eval/FieldEvaluatorTest.java      | 114 --------
 .../io/stream/eval/FieldValueEvaluatorTest.java | 114 ++++++++
 .../eval/HyperbolicTangentEvaluatorTest.java    |   4 +-
 .../io/stream/eval/MultiplyEvaluatorTest.java   |  18 +-
 .../eval/NormalDistributionEvaluatorTest.java   |  54 ++++
 .../io/stream/eval/NormalizeEvaluatorTest.java  |  74 ++++++
 .../io/stream/eval/RecursiveEvaluatorTest.java  |  85 ++++++
 .../io/stream/eval/RegressionEvaluatorTest.java |  63 +++++
 .../io/stream/eval/ReverseEvaluatorTest.java    |  57 ++++
 .../io/stream/eval/TemporalEvaluatorsTest.java  |  48 ++--
 .../eval/UniformDistributionEvaluatorTest.java  |  55 ++++
 133 files changed, 3919 insertions(+), 3725 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 609356e..fda6a80 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -59,6 +59,11 @@ Upgrade Notes
 
 * SOLR-11254: the abstract DocTransformer class now has an abstract score-less transform method variant.
 
+* SOLR-11283: all Stream Evaluators in solrj.io.eval have been refactored to have a simplier and more
+  robust structure. This simplifies and condenses the code required to implement a new Evaluator and 
+  makes it much easier for evaluators to handle differing data types (primitives, objects, arrays,
+  lists, and so forth). (Dennis Gove)
+
 New Features
 ----------------------
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/core/src/java/org/apache/solr/handler/AnalyzeEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/AnalyzeEvaluator.java b/solr/core/src/java/org/apache/solr/handler/AnalyzeEvaluator.java
index d5bc70d..4112e04 100644
--- a/solr/core/src/java/org/apache/solr/handler/AnalyzeEvaluator.java
+++ b/solr/core/src/java/org/apache/solr/handler/AnalyzeEvaluator.java
@@ -23,6 +23,7 @@ import java.util.List;
 
 import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
 import org.apache.solr.client.solrj.io.Tuple;
+import org.apache.solr.client.solrj.io.eval.SourceEvaluator;
 import org.apache.solr.client.solrj.io.stream.StreamContext;
 import org.apache.solr.client.solrj.io.stream.expr.Explanation;
 import org.apache.solr.client.solrj.io.stream.expr.Explanation.ExpressionType;
@@ -30,13 +31,11 @@ import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpressionParameter;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpressionValue;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
-import org.apache.solr.client.solrj.io.eval.*;
-
 import org.apache.solr.common.SolrException;
 import org.apache.lucene.analysis.*;
 import org.apache.solr.core.SolrCore;
 
-public class AnalyzeEvaluator extends SimpleEvaluator {
+public class AnalyzeEvaluator extends SourceEvaluator {
   private static final long serialVersionUID = 1L;
 
   private String fieldName;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/core/src/java/org/apache/solr/handler/StreamHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/StreamHandler.java b/solr/core/src/java/org/apache/solr/handler/StreamHandler.java
index 399c92b..8b6a6c0 100644
--- a/solr/core/src/java/org/apache/solr/handler/StreamHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/StreamHandler.java
@@ -33,14 +33,140 @@ import org.apache.solr.client.solrj.io.ModelCache;
 import org.apache.solr.client.solrj.io.SolrClientCache;
 import org.apache.solr.client.solrj.io.Tuple;
 import org.apache.solr.client.solrj.io.comp.StreamComparator;
-import org.apache.solr.client.solrj.io.eval.*;
+import org.apache.solr.client.solrj.io.eval.AbsoluteValueEvaluator;
+import org.apache.solr.client.solrj.io.eval.AddEvaluator;
+import org.apache.solr.client.solrj.io.eval.AndEvaluator;
+import org.apache.solr.client.solrj.io.eval.AnovaEvaluator;
+import org.apache.solr.client.solrj.io.eval.AppendEvaluator;
+import org.apache.solr.client.solrj.io.eval.ArcCosineEvaluator;
+import org.apache.solr.client.solrj.io.eval.ArcSineEvaluator;
+import org.apache.solr.client.solrj.io.eval.ArcTangentEvaluator;
+import org.apache.solr.client.solrj.io.eval.ArrayEvaluator;
+import org.apache.solr.client.solrj.io.eval.AscEvaluator;
+import org.apache.solr.client.solrj.io.eval.CeilingEvaluator;
+import org.apache.solr.client.solrj.io.eval.CoalesceEvaluator;
+import org.apache.solr.client.solrj.io.eval.ColumnEvaluator;
+import org.apache.solr.client.solrj.io.eval.ConversionEvaluator;
+import org.apache.solr.client.solrj.io.eval.ConvolutionEvaluator;
+import org.apache.solr.client.solrj.io.eval.CopyOfEvaluator;
+import org.apache.solr.client.solrj.io.eval.CopyOfRangeEvaluator;
+import org.apache.solr.client.solrj.io.eval.CorrelationEvaluator;
+import org.apache.solr.client.solrj.io.eval.CosineEvaluator;
+import org.apache.solr.client.solrj.io.eval.CovarianceEvaluator;
+import org.apache.solr.client.solrj.io.eval.CubedRootEvaluator;
+import org.apache.solr.client.solrj.io.eval.CumulativeProbabilityEvaluator;
+import org.apache.solr.client.solrj.io.eval.DescribeEvaluator;
+import org.apache.solr.client.solrj.io.eval.DivideEvaluator;
+import org.apache.solr.client.solrj.io.eval.EmpiricalDistributionEvaluator;
+import org.apache.solr.client.solrj.io.eval.EqualToEvaluator;
+import org.apache.solr.client.solrj.io.eval.EuclideanDistanceEvaluator;
+import org.apache.solr.client.solrj.io.eval.ExclusiveOrEvaluator;
+import org.apache.solr.client.solrj.io.eval.FindDelayEvaluator;
+import org.apache.solr.client.solrj.io.eval.FloorEvaluator;
+import org.apache.solr.client.solrj.io.eval.GreaterThanEqualToEvaluator;
+import org.apache.solr.client.solrj.io.eval.GreaterThanEvaluator;
+import org.apache.solr.client.solrj.io.eval.HistogramEvaluator;
+import org.apache.solr.client.solrj.io.eval.HyperbolicCosineEvaluator;
+import org.apache.solr.client.solrj.io.eval.HyperbolicSineEvaluator;
+import org.apache.solr.client.solrj.io.eval.HyperbolicTangentEvaluator;
+import org.apache.solr.client.solrj.io.eval.IfThenElseEvaluator;
+import org.apache.solr.client.solrj.io.eval.KolmogorovSmirnovEvaluator;
+import org.apache.solr.client.solrj.io.eval.LengthEvaluator;
+import org.apache.solr.client.solrj.io.eval.LessThanEqualToEvaluator;
+import org.apache.solr.client.solrj.io.eval.LessThanEvaluator;
+import org.apache.solr.client.solrj.io.eval.ModuloEvaluator;
+import org.apache.solr.client.solrj.io.eval.MovingAverageEvaluator;
+import org.apache.solr.client.solrj.io.eval.MultiplyEvaluator;
+import org.apache.solr.client.solrj.io.eval.NaturalLogEvaluator;
+import org.apache.solr.client.solrj.io.eval.NormalDistributionEvaluator;
+import org.apache.solr.client.solrj.io.eval.NormalizeEvaluator;
+import org.apache.solr.client.solrj.io.eval.NotEvaluator;
+import org.apache.solr.client.solrj.io.eval.OrEvaluator;
+import org.apache.solr.client.solrj.io.eval.PercentileEvaluator;
+import org.apache.solr.client.solrj.io.eval.PowerEvaluator;
+import org.apache.solr.client.solrj.io.eval.PredictEvaluator;
+import org.apache.solr.client.solrj.io.eval.RankEvaluator;
+import org.apache.solr.client.solrj.io.eval.RawValueEvaluator;
+import org.apache.solr.client.solrj.io.eval.RegressionEvaluator;
+import org.apache.solr.client.solrj.io.eval.ResidualsEvaluator;
+import org.apache.solr.client.solrj.io.eval.ReverseEvaluator;
+import org.apache.solr.client.solrj.io.eval.RoundEvaluator;
+import org.apache.solr.client.solrj.io.eval.SampleEvaluator;
+import org.apache.solr.client.solrj.io.eval.ScaleEvaluator;
+import org.apache.solr.client.solrj.io.eval.SequenceEvaluator;
+import org.apache.solr.client.solrj.io.eval.SineEvaluator;
+import org.apache.solr.client.solrj.io.eval.SquareRootEvaluator;
+import org.apache.solr.client.solrj.io.eval.SubtractEvaluator;
+import org.apache.solr.client.solrj.io.eval.TangentEvaluator;
+import org.apache.solr.client.solrj.io.eval.TemporalEvaluatorDay;
+import org.apache.solr.client.solrj.io.eval.TemporalEvaluatorDayOfQuarter;
+import org.apache.solr.client.solrj.io.eval.TemporalEvaluatorDayOfYear;
+import org.apache.solr.client.solrj.io.eval.TemporalEvaluatorEpoch;
+import org.apache.solr.client.solrj.io.eval.TemporalEvaluatorHour;
+import org.apache.solr.client.solrj.io.eval.TemporalEvaluatorMinute;
+import org.apache.solr.client.solrj.io.eval.TemporalEvaluatorMonth;
+import org.apache.solr.client.solrj.io.eval.TemporalEvaluatorQuarter;
+import org.apache.solr.client.solrj.io.eval.TemporalEvaluatorSecond;
+import org.apache.solr.client.solrj.io.eval.TemporalEvaluatorWeek;
+import org.apache.solr.client.solrj.io.eval.TemporalEvaluatorYear;
+import org.apache.solr.client.solrj.io.eval.UniformDistributionEvaluator;
+import org.apache.solr.client.solrj.io.eval.UuidEvaluator;
 import org.apache.solr.client.solrj.io.graph.GatherNodesStream;
 import org.apache.solr.client.solrj.io.graph.ShortestPathStream;
 import org.apache.solr.client.solrj.io.ops.ConcatOperation;
 import org.apache.solr.client.solrj.io.ops.DistinctOperation;
 import org.apache.solr.client.solrj.io.ops.GroupOperation;
 import org.apache.solr.client.solrj.io.ops.ReplaceOperation;
-import org.apache.solr.client.solrj.io.stream.*;
+import org.apache.solr.client.solrj.io.stream.CalculatorStream;
+import org.apache.solr.client.solrj.io.stream.CartesianProductStream;
+import org.apache.solr.client.solrj.io.stream.CellStream;
+import org.apache.solr.client.solrj.io.stream.CloudSolrStream;
+import org.apache.solr.client.solrj.io.stream.CommitStream;
+import org.apache.solr.client.solrj.io.stream.ComplementStream;
+import org.apache.solr.client.solrj.io.stream.DaemonStream;
+import org.apache.solr.client.solrj.io.stream.EchoStream;
+import org.apache.solr.client.solrj.io.stream.EvalStream;
+import org.apache.solr.client.solrj.io.stream.ExceptionStream;
+import org.apache.solr.client.solrj.io.stream.ExecutorStream;
+import org.apache.solr.client.solrj.io.stream.FacetStream;
+import org.apache.solr.client.solrj.io.stream.FeaturesSelectionStream;
+import org.apache.solr.client.solrj.io.stream.FetchStream;
+import org.apache.solr.client.solrj.io.stream.GetStream;
+import org.apache.solr.client.solrj.io.stream.HashJoinStream;
+import org.apache.solr.client.solrj.io.stream.HavingStream;
+import org.apache.solr.client.solrj.io.stream.InnerJoinStream;
+import org.apache.solr.client.solrj.io.stream.IntersectStream;
+import org.apache.solr.client.solrj.io.stream.JDBCStream;
+import org.apache.solr.client.solrj.io.stream.KnnStream;
+import org.apache.solr.client.solrj.io.stream.LeftOuterJoinStream;
+import org.apache.solr.client.solrj.io.stream.LetStream;
+import org.apache.solr.client.solrj.io.stream.ListStream;
+import org.apache.solr.client.solrj.io.stream.MergeStream;
+import org.apache.solr.client.solrj.io.stream.ModelStream;
+import org.apache.solr.client.solrj.io.stream.NullStream;
+import org.apache.solr.client.solrj.io.stream.OuterHashJoinStream;
+import org.apache.solr.client.solrj.io.stream.ParallelStream;
+import org.apache.solr.client.solrj.io.stream.PlotStream;
+import org.apache.solr.client.solrj.io.stream.PriorityStream;
+import org.apache.solr.client.solrj.io.stream.RandomStream;
+import org.apache.solr.client.solrj.io.stream.RankStream;
+import org.apache.solr.client.solrj.io.stream.ReducerStream;
+import org.apache.solr.client.solrj.io.stream.RollupStream;
+import org.apache.solr.client.solrj.io.stream.ScoreNodesStream;
+import org.apache.solr.client.solrj.io.stream.SelectStream;
+import org.apache.solr.client.solrj.io.stream.ShuffleStream;
+import org.apache.solr.client.solrj.io.stream.SignificantTermsStream;
+import org.apache.solr.client.solrj.io.stream.SortStream;
+import org.apache.solr.client.solrj.io.stream.SqlStream;
+import org.apache.solr.client.solrj.io.stream.StatsStream;
+import org.apache.solr.client.solrj.io.stream.StreamContext;
+import org.apache.solr.client.solrj.io.stream.TextLogitStream;
+import org.apache.solr.client.solrj.io.stream.TimeSeriesStream;
+import org.apache.solr.client.solrj.io.stream.TopicStream;
+import org.apache.solr.client.solrj.io.stream.TupStream;
+import org.apache.solr.client.solrj.io.stream.TupleStream;
+import org.apache.solr.client.solrj.io.stream.UniqueStream;
+import org.apache.solr.client.solrj.io.stream.UpdateStream;
 import org.apache.solr.client.solrj.io.stream.expr.Explanation;
 import org.apache.solr.client.solrj.io.stream.expr.Explanation.ExpressionType;
 import org.apache.solr.client.solrj.io.stream.expr.Expressible;
@@ -197,11 +323,11 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
         .withFunctionName("array", ArrayEvaluator.class)
         .withFunctionName("col", ColumnEvaluator.class)
         .withFunctionName("conv", ConvolutionEvaluator.class)
-        .withFunctionName("copyOf", CopyOfEvaluator.class)
         .withFunctionName("copyOfRange", CopyOfRangeEvaluator.class)
+        .withFunctionName("copyOf", CopyOfEvaluator.class)
         .withFunctionName("cov", CovarianceEvaluator.class)
         .withFunctionName("describe", DescribeEvaluator.class)
-        .withFunctionName("distance", DistanceEvaluator.class)
+        .withFunctionName("distance", EuclideanDistanceEvaluator.class)
         .withFunctionName("empiricalDistribution", EmpiricalDistributionEvaluator.class)
         .withFunctionName("finddelay", FindDelayEvaluator.class)
         .withFunctionName("hist", HistogramEvaluator.class)
@@ -215,7 +341,8 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
         .withFunctionName("rev", ReverseEvaluator.class)
         .withFunctionName("scale", ScaleEvaluator.class)
         .withFunctionName("sequence", SequenceEvaluator.class)
-        .withFunctionName("addAll", AddAllEvaluator.class)
+        .withFunctionName("addAll", AppendEvaluator.class)
+        .withFunctionName("append", AppendEvaluator.class)
         .withFunctionName("residuals", ResidualsEvaluator.class)
         .withFunctionName("plot", PlotStream.class)
         .withFunctionName("normalDistribution", NormalDistributionEvaluator.class)
@@ -229,7 +356,7 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
         // Boolean Stream Evaluators
         .withFunctionName("and", AndEvaluator.class)
         .withFunctionName("eor", ExclusiveOrEvaluator.class)
-        .withFunctionName("eq", EqualsEvaluator.class)
+        .withFunctionName("eq", EqualToEvaluator.class)
         .withFunctionName("gt", GreaterThanEvaluator.class)
         .withFunctionName("gteq", GreaterThanEqualToEvaluator.class)
         .withFunctionName("lt", LessThanEvaluator.class)

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/core/src/java/org/apache/solr/handler/sql/SolrTable.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/handler/sql/SolrTable.java b/solr/core/src/java/org/apache/solr/handler/sql/SolrTable.java
index f863680..c0b35cf 100644
--- a/solr/core/src/java/org/apache/solr/handler/sql/SolrTable.java
+++ b/solr/core/src/java/org/apache/solr/handler/sql/SolrTable.java
@@ -36,8 +36,8 @@ import org.apache.solr.client.solrj.io.eq.FieldEqualitor;
 import org.apache.solr.client.solrj.io.eq.MultipleFieldEqualitor;
 import org.apache.solr.client.solrj.io.eq.StreamEqualitor;
 import org.apache.solr.client.solrj.io.eval.AndEvaluator;
-import org.apache.solr.client.solrj.io.eval.BooleanEvaluator;
-import org.apache.solr.client.solrj.io.eval.EqualsEvaluator;
+import org.apache.solr.client.solrj.io.eval.RecursiveBooleanEvaluator;
+import org.apache.solr.client.solrj.io.eval.EqualToEvaluator;
 import org.apache.solr.client.solrj.io.eval.GreaterThanEqualToEvaluator;
 import org.apache.solr.client.solrj.io.eval.GreaterThanEvaluator;
 import org.apache.solr.client.solrj.io.eval.LessThanEqualToEvaluator;
@@ -497,7 +497,7 @@ class SolrTable extends AbstractQueryableTable implements TranslatableTable {
         .withFunctionName("and", AndEvaluator.class)
         .withFunctionName("or", OrEvaluator.class)
         .withFunctionName("not", NotEvaluator.class)
-        .withFunctionName("eq", EqualsEvaluator.class)
+        .withFunctionName("eq", EqualToEvaluator.class)
         .withFunctionName("gt", GreaterThanEvaluator.class)
         .withFunctionName("lt", LessThanEvaluator.class)
         .withFunctionName("val", RawValueEvaluator.class)
@@ -506,7 +506,7 @@ class SolrTable extends AbstractQueryableTable implements TranslatableTable {
         .withFunctionName("gteq", GreaterThanEqualToEvaluator.class);
 
     if(havingPredicate != null) {
-      BooleanEvaluator booleanOperation = (BooleanEvaluator)factory.constructEvaluator(StreamExpressionParser.parse(havingPredicate));
+      RecursiveBooleanEvaluator booleanOperation = (RecursiveBooleanEvaluator)factory.constructEvaluator(StreamExpressionParser.parse(havingPredicate));
       tupleStream = new HavingStream(tupleStream, booleanOperation);
     }
 
@@ -633,7 +633,7 @@ class SolrTable extends AbstractQueryableTable implements TranslatableTable {
         .withFunctionName("and", AndEvaluator.class)
         .withFunctionName("or", OrEvaluator.class)
         .withFunctionName("not", NotEvaluator.class)
-        .withFunctionName("eq", EqualsEvaluator.class)
+        .withFunctionName("eq", EqualToEvaluator.class)
         .withFunctionName("val", RawValueEvaluator.class)
         .withFunctionName("gt", GreaterThanEvaluator.class)
         .withFunctionName("lt", LessThanEvaluator.class)
@@ -641,7 +641,7 @@ class SolrTable extends AbstractQueryableTable implements TranslatableTable {
         .withFunctionName("gteq", GreaterThanEqualToEvaluator.class);
 
     if(havingPredicate != null) {
-      BooleanEvaluator booleanOperation = (BooleanEvaluator)factory.constructEvaluator(StreamExpressionParser.parse(havingPredicate));
+      RecursiveBooleanEvaluator booleanOperation = (RecursiveBooleanEvaluator)factory.constructEvaluator(StreamExpressionParser.parse(havingPredicate));
       tupleStream = new HavingStream(tupleStream, booleanOperation);
     }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AbsoluteValueEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AbsoluteValueEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AbsoluteValueEvaluator.java
index 7ad4a16..b5cc1c7 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AbsoluteValueEvaluator.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AbsoluteValueEvaluator.java
@@ -21,37 +21,31 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.Locale;
 
-import org.apache.solr.client.solrj.io.Tuple;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
 
-public class AbsoluteValueEvaluator extends NumberEvaluator {
+public class AbsoluteValueEvaluator extends RecursiveNumericEvaluator implements OneValueWorker {
   protected static final long serialVersionUID = 1L;
   
   public AbsoluteValueEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
     super(expression, factory);
     
-    if(1 != subEvaluators.size()){
-      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting one value but found %d",expression,subEvaluators.size()));
+    if(1 != containedEvaluators.size()){
+      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting exactly 1 value but found %d",expression,containedEvaluators.size()));
     }
   }
 
   @Override
-  public Number evaluate(Tuple tuple) throws IOException {
-    
-    List<BigDecimal> results = evaluateAll(tuple);
-    
-    // we're still doing these checks because if we ever add an array-flatten evaluator, 
-    // one found in the constructor could become != 1
-    if(1 != results.size()){
-      throw new IOException(String.format(Locale.ROOT,"%s(...) only works with a 1 value but %d were provided", constructingFactory.getFunctionName(getClass()), results.size()));
-    }
-    
-    if(null == results.get(0)){
+  public Object doWork(Object value){
+    if(null == value){
       return null;
     }
-    
-    return normalizeType(results.get(0).abs());
-  }  
-
+    else if(value instanceof List){
+      return ((List<?>)value).stream().map(innerValue -> doWork(innerValue));
+    }
+    else{
+      // we know it's a BigDecimal
+      return Math.abs(((BigDecimal)value).doubleValue());
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AddAllEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AddAllEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AddAllEvaluator.java
deleted file mode 100644
index 4370e1b..0000000
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AddAllEvaluator.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.client.solrj.io.eval;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.solr.client.solrj.io.Tuple;
-import org.apache.solr.client.solrj.io.stream.expr.Explanation;
-import org.apache.solr.client.solrj.io.stream.expr.Explanation.ExpressionType;
-import org.apache.solr.client.solrj.io.stream.expr.Expressible;
-import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
-import org.apache.solr.client.solrj.io.stream.expr.StreamExpressionParameter;
-import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
-
-public class AddAllEvaluator extends ComplexEvaluator implements Expressible {
-
-  private static final long serialVersionUID = 1;
-
-  public AddAllEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
-    super(expression, factory);
-
-    if(subEvaluators.size() == 0) {
-      throw new IOException("addAll evaluator expects atleast one array parameter");
-    }
-  }
-
-  public List<Number> evaluate(Tuple tuple) throws IOException {
-
-    List<Number> all = new ArrayList();
-    for(StreamEvaluator subEvaluator : subEvaluators) {
-      List<Number> numbers = (List<Number>)subEvaluator.evaluate(tuple);
-      all.addAll(numbers);
-    }
-
-    return all;
-  }
-
-  @Override
-  public StreamExpressionParameter toExpression(StreamFactory factory) throws IOException {
-    StreamExpression expression = new StreamExpression(factory.getFunctionName(getClass()));
-    return expression;
-  }
-
-  @Override
-  public Explanation toExplanation(StreamFactory factory) throws IOException {
-    return new Explanation(nodeId.toString())
-        .withExpressionType(ExpressionType.EVALUATOR)
-        .withFunctionName(factory.getFunctionName(getClass()))
-        .withImplementingClass(getClass().getName())
-        .withExpression(toExpression(factory).toString());
-  }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AddEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AddEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AddEvaluator.java
index fee7d8c..dbcd6c8 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AddEvaluator.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AddEvaluator.java
@@ -18,41 +18,59 @@ package org.apache.solr.client.solrj.io.eval;
 
 import java.io.IOException;
 import java.math.BigDecimal;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 
-import org.apache.solr.client.solrj.io.Tuple;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
 
-public class AddEvaluator extends NumberEvaluator {
+public class AddEvaluator extends RecursiveNumericEvaluator implements ManyValueWorker {
   protected static final long serialVersionUID = 1L;
   
   public AddEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
     super(expression, factory);
     
-    if(subEvaluators.size() < 1){
-      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting at least 1 value but found %d",expression,subEvaluators.size()));
+    if(containedEvaluators.size() < 1){
+      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting at least one value but found %d",expression,containedEvaluators.size()));
     }
   }
 
   @Override
-  public Number evaluate(Tuple tuple) throws IOException {
-    
-    List<BigDecimal> results = evaluateAll(tuple);
+  public Object doWork(Object... values) throws IOException {
+    if(Arrays.stream(values).anyMatch(item -> null == item)){
+      return null;
+    }
     
-    if(results.stream().anyMatch(item -> null == item)){
+    if(0 == values.length){
       return null;
     }
     
-    BigDecimal result = null;
-    if(results.size() > 0){
-      result = results.get(0);
-      for(int idx = 1; idx < results.size(); ++idx){
-        result = result.add(results.get(idx));
-      }
+    BigDecimal result = BigDecimal.ZERO;
+    for(Object value : values){
+      result = add(result, value);
     }
     
-    return normalizeType(result);
+    return result;
   }
+  
+  private BigDecimal add(BigDecimal left, Object right) throws IOException{
+    if(null == left || null == right){
+      return null;
+    }
+    else if(right instanceof BigDecimal){
+      return left.add((BigDecimal)right);
+    }
+    else if(right instanceof Number){
+      return add(left, new BigDecimal(right.toString()));
+    }
+    else if(right instanceof List){
+      return add(left, doWork(((List<?>)right).toArray()));
+    }
+    else{
+      throw new StreamEvaluatorException("Numeric value expected but found type %s for value %s", right.getClass().getName(), right.toString());
+    }
+
+  }
+  
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AndEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AndEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AndEvaluator.java
index a4d968b..cab876e 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AndEvaluator.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AndEvaluator.java
@@ -17,59 +17,23 @@
 package org.apache.solr.client.solrj.io.eval;
 
 import java.io.IOException;
-import java.util.List;
 import java.util.Locale;
-import java.util.stream.Collectors;
 
-import org.apache.solr.client.solrj.io.Tuple;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
 
-public class AndEvaluator extends BooleanEvaluator {
+public class AndEvaluator extends RecursiveBooleanEvaluator implements ManyValueWorker {
   protected static final long serialVersionUID = 1L;
   
   public AndEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
     super(expression, factory);
     
-    if(subEvaluators.size() < 2){
-      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting at least two values but found %d",expression,subEvaluators.size()));
+    if(containedEvaluators.size() < 2){
+      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting at least two values but found %d",expression,containedEvaluators.size()));
     }
   }
-
-  @Override
-  public Boolean evaluate(Tuple tuple) throws IOException {
-    
-    List<Object> results = evaluateAll(tuple);
-    
-    if(results.size() < 2){
-      String message = null;
-      if(1 == results.size()){
-        message = String.format(Locale.ROOT,"%s(...) only works with at least 2 values but 1 was provided", constructingFactory.getFunctionName(getClass())); 
-      }
-      else{
-        message = String.format(Locale.ROOT,"%s(...) only works with at least 2 values but 0 were provided", constructingFactory.getFunctionName(getClass()));
-      }
-      throw new IOException(message);
-    }
-    
-    Checker checker = constructChecker(results.get(0));
-    if(results.stream().anyMatch(result -> null == result && !checker.isNullAllowed())){
-      throw new IOException(String.format(Locale.ROOT,"Unable to check %s(...) because a null value was found", constructingFactory.getFunctionName(getClass())));
-    }
-    if(results.stream().anyMatch(result -> !checker.isCorrectType(result))){
-      throw new IOException(String.format(Locale.ROOT,"Unable to check %s(...) of differing types [%s]", constructingFactory.getFunctionName(getClass()), results.stream().map(item -> item.getClass().getSimpleName()).collect(Collectors.joining(","))));
-    }
-
-    for(int idx = 1; idx < results.size(); ++idx){
-      if(!checker.test(results.get(0), results.get(idx))){
-        return false;
-      }
-    }
-    
-    return true;
-  }
   
-  private Checker constructChecker(Object fromValue) throws IOException{
+  protected Checker constructChecker(Object fromValue) throws IOException{
     if(null == fromValue){
       throw new IOException(String.format(Locale.ROOT,"Unable to check %s(...) because a null value was found", constructingFactory.getFunctionName(getClass())));
     }
@@ -77,11 +41,12 @@ public class AndEvaluator extends BooleanEvaluator {
       return new BooleanChecker(){
         @Override
         public boolean test(Object left, Object right) {
-          return (boolean)left && (boolean)right;
+          return (Boolean)left && (Boolean)right;
         }
       };
     }
     
     throw new IOException(String.format(Locale.ROOT,"Unable to check %s(...) for values of type '%s'", constructingFactory.getFunctionName(getClass()), fromValue.getClass().getSimpleName()));
   }
+
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AnovaEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AnovaEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AnovaEvaluator.java
index ca962f1..c1d6d93 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AnovaEvaluator.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AnovaEvaluator.java
@@ -17,66 +17,49 @@
 package org.apache.solr.client.solrj.io.eval;
 
 import java.io.IOException;
-import java.util.ArrayList;
+import java.math.BigDecimal;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
-import java.util.HashMap;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 import org.apache.commons.math3.stat.inference.OneWayAnova;
 import org.apache.solr.client.solrj.io.Tuple;
-import org.apache.solr.client.solrj.io.stream.expr.Explanation;
-import org.apache.solr.client.solrj.io.stream.expr.Explanation.ExpressionType;
-import org.apache.solr.client.solrj.io.stream.expr.Expressible;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
-import org.apache.solr.client.solrj.io.stream.expr.StreamExpressionParameter;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
 
-public class AnovaEvaluator extends ComplexEvaluator implements Expressible {
-
-  private static final long serialVersionUID = 1;
-
-  public AnovaEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
+public class AnovaEvaluator extends RecursiveNumericListEvaluator implements ManyValueWorker {
+  protected static final long serialVersionUID = 1L;
+  
+  public AnovaEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
     super(expression, factory);
     
-    if(subEvaluators.size() < 2){
-      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting at least two values but found %d",expression,subEvaluators.size()));
+    if(containedEvaluators.size() < 1){
+      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting at least one value but found %d",expression,containedEvaluators.size()));
     }
   }
 
-  public Tuple evaluate(Tuple tuple) throws IOException {
-
-    List<double[]> list = new ArrayList();
-    for(StreamEvaluator subEvaluator : subEvaluators) {
-      List<Number> nums = (List<Number>)subEvaluator.evaluate(tuple);
-      double[] darray = new double[nums.size()];
-      for(int i=0; i< nums.size(); i++) {
-        darray[i]=nums.get(i).doubleValue();
-      }
-      list.add(darray);
-    }
-
+  @Override
+  public Object doWork(Object... values) throws IOException {
+    
+    // at this point we know every incoming value is an array of BigDecimals
+    
+    List<double[]> anovaInput = Arrays.stream(values)
+        // for each List, convert to double[]
+        .map(value -> ((List<BigDecimal>)value).stream().mapToDouble(BigDecimal::doubleValue).toArray())
+        // turn into List<double[]>
+        .collect(Collectors.toList());
+    
     OneWayAnova anova = new OneWayAnova();
-    double p = anova.anovaPValue(list);
-    double f = anova.anovaFValue(list);
-    Map m = new HashMap();
+    double p = anova.anovaPValue(anovaInput);
+    double f = anova.anovaFValue(anovaInput);
+    Map<String,Number> m = new HashMap<>();
     m.put("p-value", p);
     m.put("f-ratio", f);
     return new Tuple(m);
   }
+  
 
-  @Override
-  public StreamExpressionParameter toExpression(StreamFactory factory) throws IOException {
-    StreamExpression expression = new StreamExpression(factory.getFunctionName(getClass()));
-    return expression;
-  }
-
-  @Override
-  public Explanation toExplanation(StreamFactory factory) throws IOException {
-    return new Explanation(nodeId.toString())
-        .withExpressionType(ExpressionType.EVALUATOR)
-        .withFunctionName(factory.getFunctionName(getClass()))
-        .withImplementingClass(getClass().getName())
-        .withExpression(toExpression(factory).toString());
-  }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AppendEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AppendEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AppendEvaluator.java
new file mode 100644
index 0000000..58a17a8
--- /dev/null
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AppendEvaluator.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.client.solrj.io.eval;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
+import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
+
+public class AppendEvaluator extends RecursiveObjectEvaluator implements ManyValueWorker {
+  protected static final long serialVersionUID = 1L;
+  
+  public AppendEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
+    super(expression, factory);
+  }
+
+  @Override
+  public Object doWork(Object ... values){
+    List<Object> newList = new ArrayList<>();
+    
+    for(Object value : values){
+      if(null == value){
+        // skip nulls
+        continue;
+      }
+      else if(value instanceof Collection<?>){
+        newList.addAll((Collection<?>)value);
+      }
+      else{
+        newList.add(value);
+      }
+    }
+    
+    return newList;
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArcCosineEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArcCosineEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArcCosineEvaluator.java
index ee76542..7dfe992 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArcCosineEvaluator.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArcCosineEvaluator.java
@@ -20,38 +20,33 @@ import java.io.IOException;
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.Locale;
+import java.util.stream.Collectors;
 
-import org.apache.solr.client.solrj.io.Tuple;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
 
-public class ArcCosineEvaluator extends NumberEvaluator {
+public class ArcCosineEvaluator extends RecursiveNumericEvaluator implements OneValueWorker {
   protected static final long serialVersionUID = 1L;
   
   public ArcCosineEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
     super(expression, factory);
     
-    if(1 != subEvaluators.size()){
-      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting one value but found %d",expression,subEvaluators.size()));
+    if(1 != containedEvaluators.size()){
+      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting exactly 1 value but found %d",expression,containedEvaluators.size()));
     }
   }
 
   @Override
-  public Number evaluate(Tuple tuple) throws IOException {
-    
-    List<BigDecimal> results = evaluateAll(tuple);
-    
-    // we're still doing these checks because if we ever add an array-flatten evaluator, 
-    // one found in the constructor could become != 1
-    if(1 != results.size()){
-      throw new IOException(String.format(Locale.ROOT,"%s(...) only works with a 1 value but %d were provided", constructingFactory.getFunctionName(getClass()), results.size()));
-    }
-    
-    if(null == results.get(0)){
+  public Object doWork(Object value){
+    if(null == value){
       return null;
     }
-    
-    return Math.acos(results.get(0).doubleValue());
-  }  
-
+    else if(value instanceof List){
+      return ((List<?>)value).stream().map(innerValue -> doWork(innerValue)).collect(Collectors.toList());
+    }
+    else{
+      // we know it's a BigDecimal
+      return Math.acos(((BigDecimal)value).doubleValue());
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArcSineEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArcSineEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArcSineEvaluator.java
index 23d860c..0750621 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArcSineEvaluator.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArcSineEvaluator.java
@@ -20,38 +20,33 @@ import java.io.IOException;
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.Locale;
+import java.util.stream.Collectors;
 
-import org.apache.solr.client.solrj.io.Tuple;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
 
-public class ArcSineEvaluator extends NumberEvaluator {
+public class ArcSineEvaluator extends RecursiveNumericEvaluator implements OneValueWorker {
   protected static final long serialVersionUID = 1L;
   
   public ArcSineEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
     super(expression, factory);
     
-    if(1 != subEvaluators.size()){
-      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting one value but found %d",expression,subEvaluators.size()));
+    if(1 != containedEvaluators.size()){
+      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting exactly 1 value but found %d",expression,containedEvaluators.size()));
     }
   }
 
   @Override
-  public Number evaluate(Tuple tuple) throws IOException {
-    
-    List<BigDecimal> results = evaluateAll(tuple);
-    
-    // we're still doing these checks because if we ever add an array-flatten evaluator, 
-    // one found in the constructor could become != 1
-    if(1 != results.size()){
-      throw new IOException(String.format(Locale.ROOT,"%s(...) only works with a 1 value but %d were provided", constructingFactory.getFunctionName(getClass()), results.size()));
-    }
-    
-    if(null == results.get(0)){
+  public Object doWork(Object value){
+    if(null == value){
       return null;
     }
-    
-    return Math.asin(results.get(0).doubleValue());
-  }  
-
+    else if(value instanceof List){
+      return ((List<?>)value).stream().map(innerValue -> doWork(innerValue)).collect(Collectors.toList());
+    }
+    else{
+      // we know it's a BigDecimal
+      return Math.asin(((BigDecimal)value).doubleValue());
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArcTangentEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArcTangentEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArcTangentEvaluator.java
index 65e0174..95901ee 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArcTangentEvaluator.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArcTangentEvaluator.java
@@ -20,38 +20,33 @@ import java.io.IOException;
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.Locale;
+import java.util.stream.Collectors;
 
-import org.apache.solr.client.solrj.io.Tuple;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
 
-public class ArcTangentEvaluator extends NumberEvaluator {
+public class ArcTangentEvaluator extends RecursiveNumericEvaluator implements OneValueWorker {
   protected static final long serialVersionUID = 1L;
   
   public ArcTangentEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
     super(expression, factory);
     
-    if(1 != subEvaluators.size()){
-      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting one value but found %d",expression,subEvaluators.size()));
+    if(1 != containedEvaluators.size()){
+      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting exactly 1 value but found %d",expression,containedEvaluators.size()));
     }
   }
 
   @Override
-  public Number evaluate(Tuple tuple) throws IOException {
-    
-    List<BigDecimal> results = evaluateAll(tuple);
-    
-    // we're still doing these checks because if we ever add an array-flatten evaluator, 
-    // one found in the constructor could become != 1
-    if(1 != results.size()){
-      throw new IOException(String.format(Locale.ROOT,"%s(...) only works with a 1 value but %d were provided", constructingFactory.getFunctionName(getClass()), results.size()));
-    }
-    
-    if(null == results.get(0)){
+  public Object doWork(Object value){
+    if(null == value){
       return null;
     }
-    
-    return Math.atan(results.get(0).doubleValue());
-  }  
-
+    else if(value instanceof List){
+      return ((List<?>)value).stream().map(innerValue -> doWork(innerValue)).collect(Collectors.toList());
+    }
+    else{
+      // we know it's a BigDecimal
+      return Math.atan(((BigDecimal)value).doubleValue());
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArrayEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArrayEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArrayEvaluator.java
index 127f587..67dbf1a 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArrayEvaluator.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArrayEvaluator.java
@@ -17,88 +17,53 @@
 package org.apache.solr.client.solrj.io.eval;
 
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
 import java.util.List;
 import java.util.Locale;
 import java.util.stream.Collectors;
 
-import org.apache.solr.client.solrj.io.Tuple;
-import org.apache.solr.client.solrj.io.stream.expr.Explanation;
-import org.apache.solr.client.solrj.io.stream.expr.Explanation.ExpressionType;
-import org.apache.solr.client.solrj.io.stream.expr.Expressible;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpressionNamedParameter;
-import org.apache.solr.client.solrj.io.stream.expr.StreamExpressionParameter;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpressionValue;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
 
-public class ArrayEvaluator extends ComplexEvaluator implements Expressible {
-
-  private static final long serialVersionUID = 1;
-  private String sortOrder;
+public class ArrayEvaluator extends RecursiveObjectEvaluator implements ManyValueWorker {
+  protected static final long serialVersionUID = 1L;
+  
+  private Comparator<Comparable> sortComparator;
   
-  public ArrayEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
+  public ArrayEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
     super(expression, factory, Arrays.asList("sort"));
     
-    sortOrder = extractSortOrder(expression, factory);
-  }
-  
-  private String extractSortOrder(StreamExpression expression, StreamFactory factory) throws IOException{
     StreamExpressionNamedParameter sortParam = factory.getNamedOperand(expression, "sort");
-    if(null == sortParam){
-      return null; // this is ok
-    }
-    
-    if(sortParam.getParameter() instanceof StreamExpressionValue){
+    if(null != sortParam && sortParam.getParameter() instanceof StreamExpressionValue){
       String sortOrder = ((StreamExpressionValue)sortParam.getParameter()).getValue().trim().toLowerCase(Locale.ROOT);
       if("asc".equals(sortOrder) || "desc".equals(sortOrder)){
-        return sortOrder;
+        sortComparator = "asc".equals(sortOrder) ? (left,right) -> left.compareTo(right) : (left,right) -> right.compareTo(left);
       }
-    }
-    
-    throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - invalid 'sort' parameter - expecting either 'asc' or 'desc'", expression));
+      else{
+        throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - invalid 'sort' parameter - expecting either 'asc' or 'desc'", expression));
+      }
+    }    
   }
 
-  public List<Object> evaluate(Tuple tuple) throws IOException {
-    List<Object> list = new ArrayList<>();
-    for(StreamEvaluator subEvaluator : subEvaluators) {
-      Object value = subEvaluator.evaluate(tuple);
-      
-      // if we want sorting but the evaluated value is not comparable then we have an error
-      if(null != sortOrder && !(value instanceof Comparable<?>)){
-        String message = String.format(Locale.ROOT,"Failed to evaluate to a comparable object - evaluator '%s' resulted in type '%s' and value '%s'",
-            subEvaluator.toExpression(constructingFactory),
-            value.getClass().getName(),
-            value.toString());
-        throw new IOException(message);
+  @Override
+  public Object doWork(Object ... values) throws IOException{
+  
+    List<Object> newList = Arrays.stream(values).collect(Collectors.toList());
+    
+    if(null != sortComparator){
+      // validate everything is comparable
+      for(Object value : newList){
+        if(!(value instanceof Comparable<?>)){
+          throw new IOException(String.format(Locale.ROOT, "Unable to evaluate because a non-Comparable value ('%s') was found and sorting was requested", value.toString()));
+        }
       }
       
-      list.add(value);
+      newList = newList.stream().map(value -> (Comparable)value).sorted(sortComparator).collect(Collectors.toList());
     }
     
-    if(null != sortOrder){
-      // Because of the type checking above we know that the value is at least Comparable
-      Comparator<Comparable> comparator = "asc".equals(sortOrder) ? (left,right) -> left.compareTo(right) : (left,right) -> right.compareTo(left);
-      list = list.stream().map(value -> (Comparable<Object>)value).sorted(comparator).collect(Collectors.toList());
-    }
-
-    return list;
-  }
-
-  @Override
-  public StreamExpressionParameter toExpression(StreamFactory factory) throws IOException {
-    StreamExpression expression = new StreamExpression(factory.getFunctionName(getClass()));
-    return expression;
-  }
-
-  @Override
-  public Explanation toExplanation(StreamFactory factory) throws IOException {
-    return new Explanation(nodeId.toString())
-        .withExpressionType(ExpressionType.EVALUATOR)
-        .withFunctionName(factory.getFunctionName(getClass()))
-        .withImplementingClass(getClass().getName())
-        .withExpression(toExpression(factory).toString());
+    return newList;
   }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AscEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AscEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AscEvaluator.java
index 8e5e4a1..0d7321c 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AscEvaluator.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/AscEvaluator.java
@@ -17,64 +17,51 @@
 package org.apache.solr.client.solrj.io.eval;
 
 import java.io.IOException;
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
 import java.util.Locale;
+import java.util.stream.Collectors;
 
-import org.apache.solr.client.solrj.io.Tuple;
-import org.apache.solr.client.solrj.io.stream.expr.Explanation;
-import org.apache.solr.client.solrj.io.stream.expr.Explanation.ExpressionType;
-import org.apache.solr.client.solrj.io.stream.expr.Expressible;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
-import org.apache.solr.client.solrj.io.stream.expr.StreamExpressionParameter;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
 
-public class AscEvaluator extends ComplexEvaluator implements Expressible {
-
+public class AscEvaluator extends RecursiveObjectEvaluator implements OneValueWorker {
   private static final long serialVersionUID = 1;
 
   public AscEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
     super(expression, factory);
-
-    if(1 != subEvaluators.size()){
-      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting one value but found %d",expression,subEvaluators.size()));
-    }
-
   }
 
-  public List<Number> evaluate(Tuple tuple) throws IOException {
-
-    StreamEvaluator colEval1 = subEvaluators.get(0);
-
-    List<Number> numbers1 = (List<Number>)colEval1.evaluate(tuple);
-    List<Number> asc = new ArrayList();
-    asc.addAll(numbers1);
-    Collections.sort(asc, new Comparator<Number>() {
-      @Override
-      public int compare(Number a, Number b) {
-        return new BigDecimal(a.toString()).compareTo(new BigDecimal(b.toString()));
+  @Override
+  public Object doWork(Object value) throws IOException {
+    if(null == value){
+      return value;
+    }
+    else if(!(value instanceof List<?>)){
+      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - found type %s for value, expecting a List",toExpression(constructingFactory), value.getClass().getSimpleName()));
+    }
+    
+    List<?> list = (List<?>)value;
+    
+    if(0 == list.size()){
+      return list;
+    }
 
+    // Validate all of same type and are comparable
+    Object checkingObject = list.get(0);
+    for(int idx = 0; idx < list.size(); ++idx){
+      Object item = list.get(0);
+      
+      if(null == item){
+        throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - found null value",toExpression(constructingFactory)));
       }
-    });
-
-    return asc;
-  }
-
-  @Override
-  public StreamExpressionParameter toExpression(StreamFactory factory) throws IOException {
-    StreamExpression expression = new StreamExpression(factory.getFunctionName(getClass()));
-    return expression;
-  }
+      else if(!(item instanceof Comparable<?>)){
+        throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - found non-comparable value %s of type %s",toExpression(constructingFactory), item.toString(), item.getClass().getSimpleName()));
+      }
+      else if(!item.getClass().getCanonicalName().equals(checkingObject.getClass().getCanonicalName())){
+        throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - value %s is of type %s but we are expeting type %s",toExpression(constructingFactory), item.toString(), item.getClass().getSimpleName(), checkingObject.getClass().getCanonicalName()));
+      }
+    }
 
-  @Override
-  public Explanation toExplanation(StreamFactory factory) throws IOException {
-    return new Explanation(nodeId.toString())
-        .withExpressionType(ExpressionType.EVALUATOR)
-        .withFunctionName(factory.getFunctionName(getClass()))
-        .withImplementingClass(getClass().getName())
-        .withExpression(toExpression(factory).toString());
+    return list.stream().sorted((left,right) -> ((Comparable)left).compareTo((Comparable)right)).collect(Collectors.toList());
   }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/BooleanEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/BooleanEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/BooleanEvaluator.java
deleted file mode 100644
index 447ab86..0000000
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/BooleanEvaluator.java
+++ /dev/null
@@ -1,83 +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.client.solrj.io.eval;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.solr.client.solrj.io.Tuple;
-import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
-import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
-
-public abstract class BooleanEvaluator extends ComplexEvaluator {
-  protected static final long serialVersionUID = 1L;
-  
-  public BooleanEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
-    super(expression, factory);
-  }
-  
-  // restrict result to a Boolean
-  public abstract Boolean evaluate(Tuple tuple) throws IOException;
-  
-  public List<Object> evaluateAll(final Tuple tuple) throws IOException {
-    List<Object> results = new ArrayList<Object>();
-    for(StreamEvaluator subEvaluator : subEvaluators){
-      results.add(subEvaluator.evaluate(tuple));
-    }
-    
-    return results;
-  }
-
-  public interface Checker {
-    default boolean isNullAllowed(){
-      return false;
-    }
-    boolean isCorrectType(Object value);
-    boolean test(Object left, Object right);
-  }
-  
-  public interface NullChecker extends Checker {
-    default boolean isNullAllowed(){
-      return true;
-    }
-    default boolean isCorrectType(Object value){
-      return true;
-    }
-    default boolean test(Object left, Object right){
-      return null == left && null == right;
-    }
-  }
-  
-  public interface BooleanChecker extends Checker {
-    default boolean isCorrectType(Object value){
-      return value instanceof Boolean;
-    }
-  }
-  
-  public interface NumberChecker extends Checker {
-    default boolean isCorrectType(Object value){
-      return value instanceof Number;
-    }
-  }
-  
-  public interface StringChecker extends Checker {
-    default boolean isCorrectType(Object value){
-      return value instanceof String;
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/CeilingEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/CeilingEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/CeilingEvaluator.java
index 368385e..7f11900 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/CeilingEvaluator.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/CeilingEvaluator.java
@@ -18,41 +18,35 @@ package org.apache.solr.client.solrj.io.eval;
 
 import java.io.IOException;
 import java.math.BigDecimal;
-import java.math.RoundingMode;
 import java.util.List;
 import java.util.Locale;
+import java.util.stream.Collectors;
 
-import org.apache.solr.client.solrj.io.Tuple;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
 
-public class CeilingEvaluator extends NumberEvaluator {
+public class CeilingEvaluator extends RecursiveNumericEvaluator implements OneValueWorker {
   protected static final long serialVersionUID = 1L;
-
+  
   public CeilingEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
     super(expression, factory);
-
-    if(1 != subEvaluators.size()){
-      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting one value but found %d",expression,subEvaluators.size()));
+    
+    if(1 != containedEvaluators.size()){
+      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting exactly 1 value but found %d",expression,containedEvaluators.size()));
     }
   }
 
   @Override
-  public Number evaluate(Tuple tuple) throws IOException {
-
-    List<BigDecimal> results = evaluateAll(tuple);
-
-    // we're still doing these checks because if we ever add an array-flatten evaluator,
-    // one found in the constructor could become != 1
-    if(1 != results.size()){
-      throw new IOException(String.format(Locale.ROOT,"%s(...) only works with a 1 value but %d were provided", constructingFactory.getFunctionName(getClass()), results.size()));
-    }
-
-    if(null == results.get(0)){
+  public Object doWork(Object value){
+    if(null == value){
       return null;
     }
-
-    return normalizeType(results.get(0).setScale(0, RoundingMode.CEILING));
+    else if(value instanceof List){
+      return ((List<?>)value).stream().map(innerValue -> doWork(innerValue)).collect(Collectors.toList());
+    }
+    else{
+      // we know it's a BigDecimal
+      return Math.ceil(((BigDecimal)value).doubleValue());
+    }
   }
-
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/CoalesceEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/CoalesceEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/CoalesceEvaluator.java
index a9f08b0..6c25110 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/CoalesceEvaluator.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/CoalesceEvaluator.java
@@ -19,31 +19,28 @@ package org.apache.solr.client.solrj.io.eval;
 import java.io.IOException;
 import java.util.Locale;
 
-import org.apache.solr.client.solrj.io.Tuple;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
 
-public class CoalesceEvaluator extends ComplexEvaluator {
+public class CoalesceEvaluator extends RecursiveObjectEvaluator implements ManyValueWorker {
   protected static final long serialVersionUID = 1L;
   
   public CoalesceEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
     super(expression, factory);
     
-    if(subEvaluators.size() < 1){
-      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting at least one value but found %d",expression,subEvaluators.size()));
+    if(containedEvaluators.size() < 1){
+      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting at least one value but found %d",expression,containedEvaluators.size()));
     }
   }
 
   @Override
-  public Object evaluate(Tuple tuple) throws IOException {
-    
-    for(StreamEvaluator evaluator : subEvaluators){
-      Object result = evaluator.evaluate(tuple);
-      if(null != result){
-        return result;
+  public Object doWork(Object... values) throws IOException {
+    for(Object value : values){
+      if(null != value){
+        return value;
       }
     }
-        
-    return null;    
+    
+    return null;
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ColumnEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ColumnEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ColumnEvaluator.java
index dbb17b0..360cc38 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ColumnEvaluator.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ColumnEvaluator.java
@@ -17,60 +17,61 @@
 package org.apache.solr.client.solrj.io.eval;
 
 import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Locale;
 
 import org.apache.solr.client.solrj.io.Tuple;
-import org.apache.solr.client.solrj.io.stream.expr.Explanation;
-import org.apache.solr.client.solrj.io.stream.expr.Explanation.ExpressionType;
-import org.apache.solr.client.solrj.io.stream.expr.Expressible;
 import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
-import org.apache.solr.client.solrj.io.stream.expr.StreamExpressionParameter;
 import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
 
-public class ColumnEvaluator extends SimpleEvaluator implements Expressible {
-
-  private static final long serialVersionUID = 1;
-  private String name;
-  private String fieldName;
- ;
-  public ColumnEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
-    String name = factory.getValueOperand(expression, 0);
-    String fieldName = factory.getValueOperand(expression, 1);
-    init(name, fieldName);
+public class ColumnEvaluator extends RecursiveEvaluator {
+  protected static final long serialVersionUID = 1L;
+  
+  public ColumnEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
+    super(expression, factory);
+    
+    init();
   }
-
-  private void init(String name, String fieldName) {
-    this.name = name;
-    this.fieldName = fieldName;
+  
+  public ColumnEvaluator(StreamExpression expression, StreamFactory factory, List<String> ignoredNamedParameters) throws IOException{
+    super(expression, factory, ignoredNamedParameters);
+    
+    init();
   }
-
-  public List<Number> evaluate(Tuple tuple) throws IOException {
-    List<Tuple> tuples = (List<Tuple>)tuple.get(name);
-    List<Number> column = new ArrayList(tuples.size());
-    for(Tuple t : tuples) {
-      Object o = t.get(fieldName);
-      if(o instanceof Number) {
-        column.add((Number)o);
-      } else {
-        throw new IOException("Found non-numeric in column:"+o.toString());
-      }
+  
+  private void init() throws IOException{
+    if(2 != containedEvaluators.size()){
+      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting exactly 2 parameters but found %d", toExpression(constructingFactory), containedEvaluators.size()));
     }
-    return column;
   }
-
+      
   @Override
-  public StreamExpressionParameter toExpression(StreamFactory factory) throws IOException {
-    StreamExpression expression = new StreamExpression(factory.getFunctionName(getClass()));
-    return expression;
+  public Object evaluate(Tuple tuple) throws IOException {    
+    try{
+      
+      Object firstLevel = containedEvaluators.get(0).evaluate(tuple);
+      
+      if(!(firstLevel instanceof List<?>) || 0 != ((List<?>)firstLevel).stream().filter(value -> !(value instanceof Tuple)).count()){
+        throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - expecting a list of tuples but found %s", toExpression(constructingFactory), firstLevel.getClass().getSimpleName()));
+      }
+
+      List<Object> column = new ArrayList<>();
+      for(Object innerTuple : (List<?>)firstLevel){
+        column.add(containedEvaluators.get(1).evaluate((Tuple)innerTuple));
+      }
+
+      return normalizeOutputType(column);
+    }
+    catch(UncheckedIOException e){
+      throw e.getCause();
+    }
   }
 
   @Override
-  public Explanation toExplanation(StreamFactory factory) throws IOException {
-    return new Explanation(nodeId.toString())
-        .withExpressionType(ExpressionType.EVALUATOR)
-        .withFunctionName(factory.getFunctionName(getClass()))
-        .withImplementingClass(getClass().getName())
-        .withExpression(toExpression(factory).toString());
+  public Object doWork(Object... values) throws IOException {
+    // Nothing to do here
+    throw new IOException("This call should never occur");
   }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ComplexEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ComplexEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ComplexEvaluator.java
deleted file mode 100644
index ca1f0de..0000000
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ComplexEvaluator.java
+++ /dev/null
@@ -1,126 +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.client.solrj.io.eval;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import java.util.UUID;
-import java.util.stream.Collectors;
-
-import org.apache.solr.client.solrj.io.stream.StreamContext;
-import org.apache.solr.client.solrj.io.stream.expr.Explanation;
-import org.apache.solr.client.solrj.io.stream.expr.Explanation.ExpressionType;
-import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
-import org.apache.solr.client.solrj.io.stream.expr.StreamExpressionParameter;
-import org.apache.solr.client.solrj.io.stream.expr.StreamExpressionValue;
-import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
-
-public abstract class ComplexEvaluator implements StreamEvaluator {
-  protected static final long serialVersionUID = 1L;
-  protected StreamContext streamContext;
-  
-  protected UUID nodeId = UUID.randomUUID();
-  
-  protected StreamFactory constructingFactory;
-  protected List<StreamEvaluator> subEvaluators = new ArrayList<StreamEvaluator>();
-  
-  public ComplexEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
-    this(expression, factory, new ArrayList<>());
-  }
-  
-  public ComplexEvaluator(StreamExpression expression, StreamFactory factory, List<String> ignoredNamedParameters) throws IOException{
-    constructingFactory = factory;
-    
-    // We have to do this because order of the parameters matter
-    List<StreamExpressionParameter> parameters = factory.getOperandsOfType(expression, StreamExpressionParameter.class);
-    
-    for(StreamExpressionParameter parameter : parameters){
-      if(parameter instanceof StreamExpression){
-        // possible evaluator
-        StreamExpression streamExpression = (StreamExpression)parameter;
-        if(factory.doesRepresentTypes(streamExpression, ComplexEvaluator.class)){
-          subEvaluators.add(factory.constructEvaluator(streamExpression));
-        }
-        else if(factory.doesRepresentTypes(streamExpression, SimpleEvaluator.class)){
-          subEvaluators.add(factory.constructEvaluator(streamExpression));
-        }
-        else{
-          // Will be treated as a field name
-          subEvaluators.add(new FieldEvaluator(streamExpression.toString()));
-        }
-      }
-      else if(parameter instanceof StreamExpressionValue){
-        if(0 != ((StreamExpressionValue)parameter).getValue().length()){
-          // special case - if evaluates to a number, boolean, or null then we'll treat it 
-          // as a RawValueEvaluator
-          Object value = factory.constructPrimitiveObject(((StreamExpressionValue)parameter).getValue());
-          if(null == value || value instanceof Boolean || value instanceof Number){
-            subEvaluators.add(new RawValueEvaluator(value));
-          }
-          else if(value instanceof String){
-            subEvaluators.add(new FieldEvaluator((String)value));
-          }
-        }
-      }
-    }
-    
-    Set<String> namedParameters = factory.getNamedOperands(expression).stream().map(param -> param.getName()).collect(Collectors.toSet());
-    long ignorableCount = ignoredNamedParameters.stream().filter(name -> namedParameters.contains(name)).count();
-    
-    if(0 != expression.getParameters().size() - subEvaluators.size() - ignorableCount){
-      if(namedParameters.isEmpty()){
-        throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - unknown operands found - expecting only StreamEvaluators or field names", expression));
-      }
-      else{
-        throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - unknown operands found - expecting only StreamEvaluators, field names, or named parameters [%s]", expression, namedParameters.stream().collect(Collectors.joining(","))));
-      }
-    }
-  }
-
-  @Override
-  public StreamExpressionParameter toExpression(StreamFactory factory) throws IOException {
-    StreamExpression expression = new StreamExpression(factory.getFunctionName(getClass()));
-    
-    for(StreamEvaluator evaluator : subEvaluators){
-      expression.addParameter(evaluator.toExpression(factory));
-    }
-    return expression;
-  }
-
-  @Override
-  public Explanation toExplanation(StreamFactory factory) throws IOException {
-    return new Explanation(nodeId.toString())
-      .withExpressionType(ExpressionType.EVALUATOR)
-      .withFunctionName(factory.getFunctionName(getClass()))
-      .withImplementingClass(getClass().getName())
-      .withExpression(toExpression(factory).toString());
-  }
-  
-  public void setStreamContext(StreamContext context) {
-    this.streamContext = context;
-    
-    for(StreamEvaluator subEvaluator : subEvaluators){
-      subEvaluator.setStreamContext(context);
-    }
-  }
-  public StreamContext getStreamContext(){
-    return streamContext;
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/4b6f2e5c/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ConditionalEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ConditionalEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ConditionalEvaluator.java
deleted file mode 100644
index 9c534ed..0000000
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ConditionalEvaluator.java
+++ /dev/null
@@ -1,56 +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.client.solrj.io.eval;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.solr.client.solrj.io.Tuple;
-import org.apache.solr.client.solrj.io.stream.expr.StreamExpression;
-import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
-
-public abstract class ConditionalEvaluator extends ComplexEvaluator {
-  protected static final long serialVersionUID = 1L;
-  
-  public ConditionalEvaluator(StreamExpression expression, StreamFactory factory) throws IOException{
-    super(expression, factory);
-  }
-  
-  public List<Object> evaluateAll(final Tuple tuple) throws IOException {
-    List<Object> results = new ArrayList<Object>();
-    for(StreamEvaluator subEvaluator : subEvaluators){
-      results.add(subEvaluator.evaluate(tuple));
-    }
-    
-    return results;
-  }
-
-  public interface Checker {
-    default boolean isNullAllowed(){
-      return false;
-    }
-    boolean isCorrectType(Object value);
-    boolean test(Object left, Object right);
-  }
-    
-  public interface BooleanChecker extends Checker {
-    default boolean isCorrectType(Object value){
-      return value instanceof Boolean;
-    }
-  }  
-}