You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by sh...@apache.org on 2017/06/21 07:10:41 UTC

[15/50] [abbrv] lucene-solr:feature/autoscaling: SOLR-10882: ArrayEvaluator now works with all types and allows sorts (deleted ArraySortEvaluator)

SOLR-10882: ArrayEvaluator now works with all types and allows sorts (deleted ArraySortEvaluator)


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

Branch: refs/heads/feature/autoscaling
Commit: 113459a840e8ca3482ebd36a76dda551fac885ec
Parents: 5fca6a4
Author: Dennis Gove <dp...@gmail.com>
Authored: Thu Jun 15 22:10:37 2017 -0400
Committer: Dennis Gove <dp...@gmail.com>
Committed: Sun Jun 18 11:50:58 2017 -0400

----------------------------------------------------------------------
 .../org/apache/solr/handler/StreamHandler.java  | 479 +++++++++----------
 .../client/solrj/io/eval/ArrayEvaluator.java    |  48 +-
 .../solrj/io/eval/ArraySortEvaluator.java       |  77 ---
 .../client/solrj/io/eval/ComplexEvaluator.java  |  18 +-
 .../io/stream/eval/ArrayEvaluatorTest.java      | 155 ++++++
 5 files changed, 452 insertions(+), 325 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/113459a8/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 7889bf7..4616204 100644
--- a/solr/core/src/java/org/apache/solr/handler/StreamHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/StreamHandler.java
@@ -77,7 +77,7 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
   private StreamFactory streamFactory = new StreamFactory();
   private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
   private String coreName;
-  private Map<String, DaemonStream> daemons = Collections.synchronizedMap(new HashMap());
+  private Map<String,DaemonStream> daemons = Collections.synchronizedMap(new HashMap());
 
   @Override
   public PermissionNameProvider.Name getPermissionName(AuthorizationContext request) {
@@ -89,202 +89,202 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
   }
 
   public void inform(SolrCore core) {
-    
-    /* The stream factory will always contain the zkUrl for the given collection
-     * Adds default streams with their corresponding function names. These 
-     * defaults can be overridden or added to in the solrConfig in the stream 
-     * RequestHandler def. Example config override
-     *  <lst name="streamFunctions">
-     *    <str name="group">org.apache.solr.client.solrj.io.stream.ReducerStream</str>
-     *    <str name="count">org.apache.solr.client.solrj.io.stream.RecordCountStream</str>
-     *  </lst>
-     * */
+
+    /*
+     * The stream factory will always contain the zkUrl for the given collection Adds default streams with their
+     * corresponding function names. These defaults can be overridden or added to in the solrConfig in the stream
+     * RequestHandler def. Example config override 
+     * <lst name="streamFunctions"> 
+     *  <str name="group">org.apache.solr.client.solrj.io.stream.ReducerStream</str> 
+     *  <str name="count">org.apache.solr.client.solrj.io.stream.RecordCountStream</str> 
+     * </lst>
+     */
 
     String defaultCollection;
     String defaultZkhost;
     CoreContainer coreContainer = core.getCoreContainer();
     this.coreName = core.getName();
 
-    if(coreContainer.isZooKeeperAware()) {
+    if (coreContainer.isZooKeeperAware()) {
       defaultCollection = core.getCoreDescriptor().getCollectionName();
       defaultZkhost = core.getCoreContainer().getZkController().getZkServerAddress();
       streamFactory.withCollectionZkHost(defaultCollection, defaultZkhost);
       streamFactory.withDefaultZkHost(defaultZkhost);
       modelCache = new ModelCache(250,
-                                  defaultZkhost,
-                                  clientCache);
-    }
-
-     streamFactory
-       // source streams
-      .withFunctionName("search", CloudSolrStream.class)
-      .withFunctionName("facet", FacetStream.class)
-      .withFunctionName("update", UpdateStream.class)
-      .withFunctionName("jdbc", JDBCStream.class)
-      .withFunctionName("topic", TopicStream.class)
-      .withFunctionName("commit", CommitStream.class)
-      .withFunctionName("random", RandomStream.class)
-      .withFunctionName("knn", KnnStream.class)
-      
-      // decorator streams
-      .withFunctionName("merge", MergeStream.class)
-      .withFunctionName("unique", UniqueStream.class)
-      .withFunctionName("top", RankStream.class)
-      .withFunctionName("group", GroupOperation.class)
-      .withFunctionName("reduce", ReducerStream.class)
-      .withFunctionName("parallel", ParallelStream.class)
-      .withFunctionName("rollup", RollupStream.class)
-      .withFunctionName("stats", StatsStream.class)
-      .withFunctionName("innerJoin", InnerJoinStream.class)
-      .withFunctionName("leftOuterJoin", LeftOuterJoinStream.class) 
-      .withFunctionName("hashJoin", HashJoinStream.class)
-      .withFunctionName("outerHashJoin", OuterHashJoinStream.class)
-      .withFunctionName("intersect", IntersectStream.class)
-      .withFunctionName("complement", ComplementStream.class)
-      .withFunctionName(SORT, SortStream.class)
-      .withFunctionName("train", TextLogitStream.class)
-      .withFunctionName("features", FeaturesSelectionStream.class)
-      .withFunctionName("daemon", DaemonStream.class)
-      .withFunctionName("shortestPath", ShortestPathStream.class)
-      .withFunctionName("gatherNodes", GatherNodesStream.class)
-      .withFunctionName("nodes", GatherNodesStream.class)
-      .withFunctionName("select", SelectStream.class)
-      .withFunctionName("shortestPath", ShortestPathStream.class)
-      .withFunctionName("gatherNodes", GatherNodesStream.class)
-      .withFunctionName("nodes", GatherNodesStream.class)
-      .withFunctionName("scoreNodes", ScoreNodesStream.class)
-      .withFunctionName("model", ModelStream.class)
-      .withFunctionName("classify", ClassifyStream.class)
-      .withFunctionName("fetch", FetchStream.class)
-      .withFunctionName("executor", ExecutorStream.class)
-      .withFunctionName("null", NullStream.class)
-      .withFunctionName("priority", PriorityStream.class)
-         .withFunctionName("significantTerms", SignificantTermsStream.class)
-      .withFunctionName("cartesianProduct", CartesianProductStream.class)
-         .withFunctionName("shuffle", ShuffleStream.class)
-         .withFunctionName("calc", CalculatorStream.class)
-      .withFunctionName("eval",EvalStream.class)
-      .withFunctionName("echo", EchoStream.class)
-      .withFunctionName("cell", CellStream.class)
-      .withFunctionName("list", ListStream.class)
-      .withFunctionName("let", LetStream.class)
-      .withFunctionName("get", GetStream.class)
-      .withFunctionName("timeseries", TimeSeriesStream.class)
-      .withFunctionName("tuple", TupStream.class)
-      .withFunctionName("sql", SqlStream.class)
-      .withFunctionName("col", ColumnEvaluator.class)
-      .withFunctionName("predict", PredictEvaluator.class)
-      .withFunctionName("regress", RegressionEvaluator.class)
-      .withFunctionName("cov", CovarianceEvaluator.class)
-      .withFunctionName("conv", ConvolutionEvaluator.class)
-      .withFunctionName("normalize", NormalizeEvaluator.class)
-      .withFunctionName("rev", ReverseEvaluator.class)
-      .withFunctionName("length", LengthEvaluator.class)
-      .withFunctionName("rank", RankEvaluator.class)
-      .withFunctionName("scale", ScaleEvaluator.class)
-      .withFunctionName("distance", DistanceEvaluator.class)
-      .withFunctionName("copyOf", CopyOfEvaluator.class)
-      .withFunctionName("copyOfRange", CopyOfRangeEvaluator.class)
-      .withFunctionName("percentile", PercentileEvaluator.class)
-      .withFunctionName("empiricalDistribution", EmpiricalDistributionEvaluator.class)
-      .withFunctionName("cumulativeProbability", CumulativeProbabilityEvaluator.class)
-      .withFunctionName("describe", DescribeEvaluator.class)
-      .withFunctionName("finddelay", FindDelayEvaluator.class)
-      .withFunctionName("sequence", SequenceEvaluator.class)
-      .withFunctionName("array", ArrayEvaluator.class)
-      .withFunctionName("hist", HistogramEvaluator.class)
-      .withFunctionName("anova", AnovaEvaluator.class)
-      .withFunctionName("movingAvg", MovingAverageEvaluator.class)
-      .withFunctionName("arraySort", ArraySortEvaluator.class)
-
-      // metrics
-         .withFunctionName("min", MinMetric.class)
-      .withFunctionName("max", MaxMetric.class)
-      .withFunctionName("avg", MeanMetric.class)
-      .withFunctionName("sum", SumMetric.class)
-      .withFunctionName("count", CountMetric.class)
-      
-      // tuple manipulation operations
-         .withFunctionName("replace", ReplaceOperation.class)
-      .withFunctionName("concat", ConcatOperation.class)
-      
-      // stream reduction operations
-         .withFunctionName("group", GroupOperation.class)
-      .withFunctionName("distinct", DistinctOperation.class)
-      .withFunctionName("having", HavingStream.class)
-      
-      // Stream Evaluators
-         .withFunctionName("val", RawValueEvaluator.class)
-      
-      // Boolean Stream Evaluators
-         .withFunctionName("and", AndEvaluator.class)
-      .withFunctionName("eor", ExclusiveOrEvaluator.class)
-      .withFunctionName("eq", EqualsEvaluator.class)
-      .withFunctionName("gt", GreaterThanEvaluator.class)
-      .withFunctionName("gteq", GreaterThanEqualToEvaluator.class)
-      .withFunctionName("lt", LessThanEvaluator.class)
-      .withFunctionName("lteq", LessThanEqualToEvaluator.class)
-      .withFunctionName("not", NotEvaluator.class)
-         .withFunctionName("or", OrEvaluator.class)
-
-      // Date Time Evaluators
-         .withFunctionName(TemporalEvaluatorYear.FUNCTION_NAME, TemporalEvaluatorYear.class)
-      .withFunctionName(TemporalEvaluatorMonth.FUNCTION_NAME, TemporalEvaluatorMonth.class)
-      .withFunctionName(TemporalEvaluatorDay.FUNCTION_NAME, TemporalEvaluatorDay.class)
-      .withFunctionName(TemporalEvaluatorDayOfYear.FUNCTION_NAME, TemporalEvaluatorDayOfYear.class)
-         .withFunctionName(TemporalEvaluatorHour.FUNCTION_NAME, TemporalEvaluatorHour.class)
-      .withFunctionName(TemporalEvaluatorMinute.FUNCTION_NAME, TemporalEvaluatorMinute.class)
-         .withFunctionName(TemporalEvaluatorSecond.FUNCTION_NAME, TemporalEvaluatorSecond.class)
-      .withFunctionName(TemporalEvaluatorEpoch.FUNCTION_NAME, TemporalEvaluatorEpoch.class)
-      .withFunctionName(TemporalEvaluatorWeek.FUNCTION_NAME, TemporalEvaluatorWeek.class)
-         .withFunctionName(TemporalEvaluatorQuarter.FUNCTION_NAME, TemporalEvaluatorQuarter.class)
-         .withFunctionName(TemporalEvaluatorDayOfQuarter.FUNCTION_NAME, TemporalEvaluatorDayOfQuarter.class)
-
-      // Number Stream Evaluators
-         .withFunctionName("abs", AbsoluteValueEvaluator.class)
-      .withFunctionName("add", AddEvaluator.class)
-      .withFunctionName("div", DivideEvaluator.class)
-      .withFunctionName("mult", MultiplyEvaluator.class)
-      .withFunctionName("sub", SubtractEvaluator.class)
-      .withFunctionName("log", NaturalLogEvaluator.class)
-      .withFunctionName("pow", PowerEvaluator.class)
-      .withFunctionName("mod", ModuloEvaluator.class)
-         .withFunctionName("ceil", CeilingEvaluator.class)
-      .withFunctionName("floor", FloorEvaluator.class)
-      .withFunctionName("sin", SineEvaluator.class)
-      .withFunctionName("asin", ArcSineEvaluator.class)
-      .withFunctionName("sinh", HyperbolicSineEvaluator.class)
-      .withFunctionName("cos", CosineEvaluator.class)
-      .withFunctionName("acos", ArcCosineEvaluator.class)
-      .withFunctionName("cosh", HyperbolicCosineEvaluator.class)
-      .withFunctionName("tan", TangentEvaluator.class)
-      .withFunctionName("atan", ArcTangentEvaluator.class)
-      .withFunctionName("tanh", HyperbolicTangentEvaluator.class)
-         .withFunctionName("round", RoundEvaluator.class)
-      .withFunctionName("sqrt", SquareRootEvaluator.class)
-      .withFunctionName("cbrt", CubedRootEvaluator.class)
-      .withFunctionName("coalesce", CoalesceEvaluator.class)
-      .withFunctionName("uuid", UuidEvaluator.class)
-      .withFunctionName("corr", CorrelationEvaluator.class)
-
-
-      // Conditional Stream Evaluators
-         .withFunctionName("if", IfThenElseEvaluator.class)
-         .withFunctionName("analyze", AnalyzeEvaluator.class)
-         .withFunctionName("convert", ConversionEvaluator.class)
-      ;
-
-     // This pulls all the overrides and additions from the config
-     List<PluginInfo> pluginInfos = core.getSolrConfig().getPluginInfos(Expressible.class.getName());
-     for (PluginInfo pluginInfo : pluginInfos) {
-       Class<? extends Expressible> clazz = core.getMemClassLoader().findClass(pluginInfo.className, Expressible.class);
-       streamFactory.withFunctionName(pluginInfo.name, clazz);
-     }
+          defaultZkhost,
+          clientCache);
+    }
+
+    streamFactory
+        // source streams
+        .withFunctionName("search", CloudSolrStream.class)
+        .withFunctionName("facet", FacetStream.class)
+        .withFunctionName("update", UpdateStream.class)
+        .withFunctionName("jdbc", JDBCStream.class)
+        .withFunctionName("topic", TopicStream.class)
+        .withFunctionName("commit", CommitStream.class)
+        .withFunctionName("random", RandomStream.class)
+        .withFunctionName("knn", KnnStream.class)
+
+        // decorator streams
+        .withFunctionName("merge", MergeStream.class)
+        .withFunctionName("unique", UniqueStream.class)
+        .withFunctionName("top", RankStream.class)
+        .withFunctionName("group", GroupOperation.class)
+        .withFunctionName("reduce", ReducerStream.class)
+        .withFunctionName("parallel", ParallelStream.class)
+        .withFunctionName("rollup", RollupStream.class)
+        .withFunctionName("stats", StatsStream.class)
+        .withFunctionName("innerJoin", InnerJoinStream.class)
+        .withFunctionName("leftOuterJoin", LeftOuterJoinStream.class)
+        .withFunctionName("hashJoin", HashJoinStream.class)
+        .withFunctionName("outerHashJoin", OuterHashJoinStream.class)
+        .withFunctionName("intersect", IntersectStream.class)
+        .withFunctionName("complement", ComplementStream.class)
+        .withFunctionName(SORT, SortStream.class)
+        .withFunctionName("train", TextLogitStream.class)
+        .withFunctionName("features", FeaturesSelectionStream.class)
+        .withFunctionName("daemon", DaemonStream.class)
+        .withFunctionName("shortestPath", ShortestPathStream.class)
+        .withFunctionName("gatherNodes", GatherNodesStream.class)
+        .withFunctionName("nodes", GatherNodesStream.class)
+        .withFunctionName("select", SelectStream.class)
+        .withFunctionName("shortestPath", ShortestPathStream.class)
+        .withFunctionName("gatherNodes", GatherNodesStream.class)
+        .withFunctionName("nodes", GatherNodesStream.class)
+        .withFunctionName("scoreNodes", ScoreNodesStream.class)
+        .withFunctionName("model", ModelStream.class)
+        .withFunctionName("classify", ClassifyStream.class)
+        .withFunctionName("fetch", FetchStream.class)
+        .withFunctionName("executor", ExecutorStream.class)
+        .withFunctionName("null", NullStream.class)
+        .withFunctionName("priority", PriorityStream.class)
+        .withFunctionName("significantTerms", SignificantTermsStream.class)
+        .withFunctionName("cartesianProduct", CartesianProductStream.class)
+        .withFunctionName("shuffle", ShuffleStream.class)
+        .withFunctionName("calc", CalculatorStream.class)
+        .withFunctionName("eval", EvalStream.class)
+        .withFunctionName("echo", EchoStream.class)
+        .withFunctionName("cell", CellStream.class)
+        .withFunctionName("list", ListStream.class)
+        .withFunctionName("let", LetStream.class)
+        .withFunctionName("get", GetStream.class)
+        .withFunctionName("timeseries", TimeSeriesStream.class)
+        .withFunctionName("tuple", TupStream.class)
+        .withFunctionName("sql", SqlStream.class)
+
+        // metrics
+        .withFunctionName("min", MinMetric.class)
+        .withFunctionName("max", MaxMetric.class)
+        .withFunctionName("avg", MeanMetric.class)
+        .withFunctionName("sum", SumMetric.class)
+        .withFunctionName("count", CountMetric.class)
+
+        // tuple manipulation operations
+        .withFunctionName("replace", ReplaceOperation.class)
+        .withFunctionName("concat", ConcatOperation.class)
+
+        // stream reduction operations
+        .withFunctionName("group", GroupOperation.class)
+        .withFunctionName("distinct", DistinctOperation.class)
+        .withFunctionName("having", HavingStream.class)
+
+        // Stream Evaluators
+        .withFunctionName("val", RawValueEvaluator.class)
+
+        // New Evaluators
+        .withFunctionName("anova", AnovaEvaluator.class)
+        .withFunctionName("array", ArrayEvaluator.class)
+        .withFunctionName("col", ColumnEvaluator.class)
+        .withFunctionName("conv", ConvolutionEvaluator.class)
+        .withFunctionName("copyOf", CopyOfEvaluator.class)
+        .withFunctionName("copyOfRange", CopyOfRangeEvaluator.class)
+        .withFunctionName("cov", CovarianceEvaluator.class)
+        .withFunctionName("cumulativeProbability", CumulativeProbabilityEvaluator.class)
+        .withFunctionName("describe", DescribeEvaluator.class)
+        .withFunctionName("distance", DistanceEvaluator.class)
+        .withFunctionName("empiricalDistribution", EmpiricalDistributionEvaluator.class)
+        .withFunctionName("finddelay", FindDelayEvaluator.class)
+        .withFunctionName("hist", HistogramEvaluator.class)
+        .withFunctionName("length", LengthEvaluator.class)
+        .withFunctionName("movingAvg", MovingAverageEvaluator.class)
+        .withFunctionName("normalize", NormalizeEvaluator.class)
+        .withFunctionName("percentile", PercentileEvaluator.class)
+        .withFunctionName("predict", PredictEvaluator.class)
+        .withFunctionName("rank", RankEvaluator.class)
+        .withFunctionName("regress", RegressionEvaluator.class)
+        .withFunctionName("rev", ReverseEvaluator.class)
+        .withFunctionName("scale", ScaleEvaluator.class)
+        .withFunctionName("sequence", SequenceEvaluator.class)
+
         
+        // Boolean Stream Evaluators
+        .withFunctionName("and", AndEvaluator.class)
+        .withFunctionName("eor", ExclusiveOrEvaluator.class)
+        .withFunctionName("eq", EqualsEvaluator.class)
+        .withFunctionName("gt", GreaterThanEvaluator.class)
+        .withFunctionName("gteq", GreaterThanEqualToEvaluator.class)
+        .withFunctionName("lt", LessThanEvaluator.class)
+        .withFunctionName("lteq", LessThanEqualToEvaluator.class)
+        .withFunctionName("not", NotEvaluator.class)
+        .withFunctionName("or", OrEvaluator.class)
+
+        // Date Time Evaluators
+        .withFunctionName(TemporalEvaluatorYear.FUNCTION_NAME, TemporalEvaluatorYear.class)
+        .withFunctionName(TemporalEvaluatorMonth.FUNCTION_NAME, TemporalEvaluatorMonth.class)
+        .withFunctionName(TemporalEvaluatorDay.FUNCTION_NAME, TemporalEvaluatorDay.class)
+        .withFunctionName(TemporalEvaluatorDayOfYear.FUNCTION_NAME, TemporalEvaluatorDayOfYear.class)
+        .withFunctionName(TemporalEvaluatorHour.FUNCTION_NAME, TemporalEvaluatorHour.class)
+        .withFunctionName(TemporalEvaluatorMinute.FUNCTION_NAME, TemporalEvaluatorMinute.class)
+        .withFunctionName(TemporalEvaluatorSecond.FUNCTION_NAME, TemporalEvaluatorSecond.class)
+        .withFunctionName(TemporalEvaluatorEpoch.FUNCTION_NAME, TemporalEvaluatorEpoch.class)
+        .withFunctionName(TemporalEvaluatorWeek.FUNCTION_NAME, TemporalEvaluatorWeek.class)
+        .withFunctionName(TemporalEvaluatorQuarter.FUNCTION_NAME, TemporalEvaluatorQuarter.class)
+        .withFunctionName(TemporalEvaluatorDayOfQuarter.FUNCTION_NAME, TemporalEvaluatorDayOfQuarter.class)
+
+        // Number Stream Evaluators
+        .withFunctionName("abs", AbsoluteValueEvaluator.class)
+        .withFunctionName("add", AddEvaluator.class)
+        .withFunctionName("div", DivideEvaluator.class)
+        .withFunctionName("mult", MultiplyEvaluator.class)
+        .withFunctionName("sub", SubtractEvaluator.class)
+        .withFunctionName("log", NaturalLogEvaluator.class)
+        .withFunctionName("pow", PowerEvaluator.class)
+        .withFunctionName("mod", ModuloEvaluator.class)
+        .withFunctionName("ceil", CeilingEvaluator.class)
+        .withFunctionName("floor", FloorEvaluator.class)
+        .withFunctionName("sin", SineEvaluator.class)
+        .withFunctionName("asin", ArcSineEvaluator.class)
+        .withFunctionName("sinh", HyperbolicSineEvaluator.class)
+        .withFunctionName("cos", CosineEvaluator.class)
+        .withFunctionName("acos", ArcCosineEvaluator.class)
+        .withFunctionName("cosh", HyperbolicCosineEvaluator.class)
+        .withFunctionName("tan", TangentEvaluator.class)
+        .withFunctionName("atan", ArcTangentEvaluator.class)
+        .withFunctionName("tanh", HyperbolicTangentEvaluator.class)
+        .withFunctionName("round", RoundEvaluator.class)
+        .withFunctionName("sqrt", SquareRootEvaluator.class)
+        .withFunctionName("cbrt", CubedRootEvaluator.class)
+        .withFunctionName("coalesce", CoalesceEvaluator.class)
+        .withFunctionName("uuid", UuidEvaluator.class)
+        .withFunctionName("corr", CorrelationEvaluator.class)
+
+        // Conditional Stream Evaluators
+        .withFunctionName("if", IfThenElseEvaluator.class)
+        .withFunctionName("analyze", AnalyzeEvaluator.class)
+        .withFunctionName("convert", ConversionEvaluator.class);
+
+    // This pulls all the overrides and additions from the config
+    List<PluginInfo> pluginInfos = core.getSolrConfig().getPluginInfos(Expressible.class.getName());
+    for (PluginInfo pluginInfo : pluginInfos) {
+      Class<? extends Expressible> clazz = core.getMemClassLoader().findClass(pluginInfo.className, Expressible.class);
+      streamFactory.withFunctionName(pluginInfo.name, clazz);
+    }
+
     core.addCloseHook(new CloseHook() {
       @Override
       public void preClose(SolrCore core) {
-        //To change body of implemented methods use File | Settings | File Templates.
+        // To change body of implemented methods use File | Settings | File Templates.
       }
 
       @Override
@@ -299,7 +299,7 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
     params = adjustParams(params);
     req.setParams(params);
 
-    if(params.get("action") != null) {
+    if (params.get("action") != null) {
       handleAdmin(req, rsp, params);
       return;
     }
@@ -308,7 +308,7 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
 
     try {
       StreamExpression streamExpression = StreamExpressionParser.parse(params.get("expr"));
-      if(this.streamFactory.isEvaluator(streamExpression)) {
+      if (this.streamFactory.isEvaluator(streamExpression)) {
         StreamExpression tupleExpression = new StreamExpression("tuple");
         tupleExpression.addParameter(new StreamExpressionNamedParameter("return-value", streamExpression));
         tupleStream = this.streamFactory.constructStream(tupleExpression);
@@ -316,7 +316,8 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
         tupleStream = this.streamFactory.constructStream(streamExpression);
       }
     } catch (Exception e) {
-      //Catch exceptions that occur while the stream is being created. This will include streaming expression parse rules.
+      // Catch exceptions that occur while the stream is being created. This will include streaming expression parse
+      // rules.
       SolrException.log(logger, e);
       rsp.add("result-set", new DummyErrorStream(e));
 
@@ -334,21 +335,21 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
     context.put("core", this.coreName);
     context.put("solr-core", req.getCore());
     tupleStream.setStreamContext(context);
-    
+
     // if asking for explanation then go get it
-    if(params.getBool("explain", false)){
+    if (params.getBool("explain", false)) {
       rsp.add("explanation", tupleStream.toExplanation(this.streamFactory));
     }
-    
-    if(tupleStream instanceof DaemonStream) {
-      DaemonStream daemonStream = (DaemonStream)tupleStream;
-      if(daemons.containsKey(daemonStream.getId())) {
+
+    if (tupleStream instanceof DaemonStream) {
+      DaemonStream daemonStream = (DaemonStream) tupleStream;
+      if (daemons.containsKey(daemonStream.getId())) {
         daemons.remove(daemonStream.getId()).close();
       }
       daemonStream.setDaemons(daemons);
-      daemonStream.open();  //This will start the deamonStream
+      daemonStream.open(); // This will start the deamonStream
       daemons.put(daemonStream.getId(), daemonStream);
-      rsp.add("result-set", new DaemonResponseStream("Deamon:"+daemonStream.getId()+" started on "+coreName));
+      rsp.add("result-set", new DaemonResponseStream("Deamon:" + daemonStream.getId() + " started on " + coreName));
     } else {
       rsp.add("result-set", new TimerStream(new ExceptionStream(tupleStream)));
     }
@@ -356,10 +357,10 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
 
   private void handleAdmin(SolrQueryRequest req, SolrQueryResponse rsp, SolrParams params) {
     String action = params.get("action");
-    if("stop".equalsIgnoreCase(action)) {
+    if ("stop".equalsIgnoreCase(action)) {
       String id = params.get(ID);
       DaemonStream d = daemons.get(id);
-      if(d != null) {
+      if (d != null) {
         d.close();
         rsp.add("result-set", new DaemonResponseStream("Deamon:" + id + " stopped on " + coreName));
       } else {
@@ -400,50 +401,46 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
     return null;
   }
 
-
   public static class DummyErrorStream extends TupleStream {
     private Exception e;
 
     public DummyErrorStream(Exception e) {
       this.e = e;
     }
+
     public StreamComparator getStreamSort() {
       return null;
     }
 
-    public void close() {
-    }
+    public void close() {}
 
-    public void open() {
-    }
+    public void open() {}
 
-    public void setStreamContext(StreamContext context) {
-    }
+    public void setStreamContext(StreamContext context) {}
 
     public List<TupleStream> children() {
       return null;
     }
-    
+
     @Override
     public Explanation toExplanation(StreamFactory factory) throws IOException {
 
       return new StreamExplanation(getStreamNodeId().toString())
-        .withFunctionName("error")
-        .withImplementingClass(this.getClass().getName())
-        .withExpressionType(ExpressionType.STREAM_DECORATOR)
-        .withExpression("--non-expressible--");
+          .withFunctionName("error")
+          .withImplementingClass(this.getClass().getName())
+          .withExpressionType(ExpressionType.STREAM_DECORATOR)
+          .withExpression("--non-expressible--");
     }
 
     public Tuple read() {
       String msg = e.getMessage();
 
       Throwable t = e.getCause();
-      while(t != null) {
+      while (t != null) {
         msg = t.getMessage();
         t = t.getCause();
       }
 
-
       Map m = new HashMap();
       m.put("EOF", true);
       m.put("EXCEPTION", msg);
@@ -457,18 +454,16 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
     public DaemonCollectionStream(Collection<DaemonStream> col) {
       this.it = col.iterator();
     }
+
     public StreamComparator getStreamSort() {
       return null;
     }
 
-    public void close() {
-    }
+    public void close() {}
 
-    public void open() {
-    }
+    public void open() {}
 
-    public void setStreamContext(StreamContext context) {
-    }
+    public void setStreamContext(StreamContext context) {}
 
     public List<TupleStream> children() {
       return null;
@@ -478,14 +473,14 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
     public Explanation toExplanation(StreamFactory factory) throws IOException {
 
       return new StreamExplanation(getStreamNodeId().toString())
-        .withFunctionName("daemon-collection")
-        .withImplementingClass(this.getClass().getName())
-        .withExpressionType(ExpressionType.STREAM_DECORATOR)
-        .withExpression("--non-expressible--");
+          .withFunctionName("daemon-collection")
+          .withImplementingClass(this.getClass().getName())
+          .withExpressionType(ExpressionType.STREAM_DECORATOR)
+          .withExpression("--non-expressible--");
     }
-    
+
     public Tuple read() {
-      if(it.hasNext()) {
+      if (it.hasNext()) {
         return it.next().getInfo();
       } else {
         Map m = new HashMap();
@@ -502,18 +497,16 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
     public DaemonResponseStream(String message) {
       this.message = message;
     }
+
     public StreamComparator getStreamSort() {
       return null;
     }
 
-    public void close() {
-    }
+    public void close() {}
 
-    public void open() {
-    }
+    public void open() {}
 
-    public void setStreamContext(StreamContext context) {
-    }
+    public void setStreamContext(StreamContext context) {}
 
     public List<TupleStream> children() {
       return null;
@@ -523,10 +516,10 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
     public Explanation toExplanation(StreamFactory factory) throws IOException {
 
       return new StreamExplanation(getStreamNodeId().toString())
-        .withFunctionName("daemon-response")
-        .withImplementingClass(this.getClass().getName())
-        .withExpressionType(ExpressionType.STREAM_DECORATOR)
-        .withExpression("--non-expressible--");
+          .withFunctionName("daemon-response")
+          .withImplementingClass(this.getClass().getName())
+          .withExpressionType(ExpressionType.STREAM_DECORATOR)
+          .withExpression("--non-expressible--");
     }
 
     public Tuple read() {
@@ -537,7 +530,7 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
       } else {
         sendEOF = true;
         Map m = new HashMap();
-        m.put("DaemonOp",message);
+        m.put("DaemonOp", message);
         return new Tuple(m);
       }
     }
@@ -577,15 +570,15 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
     public Explanation toExplanation(StreamFactory factory) throws IOException {
 
       return new StreamExplanation(getStreamNodeId().toString())
-        .withFunctionName("timer")
-        .withImplementingClass(this.getClass().getName())
-        .withExpressionType(ExpressionType.STREAM_DECORATOR)
-        .withExpression("--non-expressible--");
+          .withFunctionName("timer")
+          .withImplementingClass(this.getClass().getName())
+          .withExpressionType(ExpressionType.STREAM_DECORATOR)
+          .withExpression("--non-expressible--");
     }
 
     public Tuple read() throws IOException {
       Tuple tuple = this.tupleStream.read();
-      if(tuple.EOF) {
+      if (tuple.EOF) {
         long totalTime = (System.nanoTime() - begin) / 1000000;
         tuple.fields.put("RESPONSE_TIME", totalTime);
       }
@@ -593,25 +586,25 @@ public class StreamHandler extends RequestHandlerBase implements SolrCoreAware,
     }
   }
 
-  private Map<String, List<String>> getCollectionShards(SolrParams params) {
+  private Map<String,List<String>> getCollectionShards(SolrParams params) {
 
-    Map<String, List<String>> collectionShards = new HashMap();
+    Map<String,List<String>> collectionShards = new HashMap();
     Iterator<String> paramsIt = params.getParameterNamesIterator();
-    while(paramsIt.hasNext()) {
+    while (paramsIt.hasNext()) {
       String param = paramsIt.next();
-      if(param.indexOf(".shards") > -1) {
+      if (param.indexOf(".shards") > -1) {
         String collection = param.split("\\.")[0];
         String shardString = params.get(param);
         String[] shards = shardString.split(",");
         List<String> shardList = new ArrayList();
-        for(String shard : shards) {
+        for (String shard : shards) {
           shardList.add(shard);
         }
         collectionShards.put(collection, shardList);
       }
     }
 
-    if(collectionShards.size() > 0) {
+    if (collectionShards.size() > 0) {
       return collectionShards;
     } else {
       return null;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/113459a8/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 ed45ee9..065335b 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
@@ -18,30 +18,72 @@ package org.apache.solr.client.solrj.io.eval;
 
 import java.io.IOException;
 import java.util.ArrayList;
+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;
 
+import com.google.common.collect.Lists;
+
 public class ArrayEvaluator extends ComplexEvaluator implements Expressible {
 
   private static final long serialVersionUID = 1;
-
+  private String sortOrder;
+  
   public ArrayEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
-    super(expression, factory);
+    super(expression, factory, Lists.newArrayList("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){
+      String sortOrder = ((StreamExpressionValue)sortParam.getParameter()).getValue().trim().toLowerCase(Locale.ROOT);
+      if("asc".equals(sortOrder) || "desc".equals(sortOrder)){
+        return sortOrder;
+      }
+    }
+    
+    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 = (Number)subEvaluator.evaluate(tuple);
+      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);
+      }
+      
       list.add(value);
     }
+    
+    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;
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/113459a8/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArraySortEvaluator.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArraySortEvaluator.java b/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArraySortEvaluator.java
deleted file mode 100644
index dabc615..0000000
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/io/eval/ArraySortEvaluator.java
+++ /dev/null
@@ -1,77 +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.Collections;
-import java.util.Comparator;
-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 ArraySortEvaluator extends ComplexEvaluator implements Expressible {
-
-  private static final long serialVersionUID = 1;
-
-  public ArraySortEvaluator(StreamExpression expression, StreamFactory factory) throws IOException {
-    super(expression, factory);
-  }
-
-  public List<Number> evaluate(Tuple tuple) throws IOException {
-
-    if(subEvaluators.size() != 1) {
-      throw new IOException("Array sort evaluator expects 1 parameters found: "+subEvaluators.size());
-    }
-
-    StreamEvaluator colEval1 = subEvaluators.get(0);
-
-    List<Number> numbers1 = (List<Number>)colEval1.evaluate(tuple);
-    List<Number> numbers2 = new ArrayList();
-    numbers2.addAll(numbers1);
-    Collections.sort(numbers2, new Comparator<Number>() {
-      @Override
-      public int compare(Number o1, Number o2) {
-        Double d1 = o1.doubleValue();
-        Double d2 = o2.doubleValue();
-        return d1.compareTo(d2);
-      }
-    });
-    return numbers2;
-  }
-
-  @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/113459a8/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
index ea4c88c..ca1f0de 100644
--- 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
@@ -20,7 +20,9 @@ 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;
@@ -40,6 +42,10 @@ public abstract class ComplexEvaluator implements StreamEvaluator {
   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
@@ -75,8 +81,16 @@ public abstract class ComplexEvaluator implements StreamEvaluator {
       }
     }
     
-    if(expression.getParameters().size() != subEvaluators.size()){
-      throw new IOException(String.format(Locale.ROOT,"Invalid expression %s - unknown operands found - expecting only StreamEvaluators or field names", expression));
+    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(","))));
+      }
     }
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/113459a8/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/eval/ArrayEvaluatorTest.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/eval/ArrayEvaluatorTest.java b/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/eval/ArrayEvaluatorTest.java
new file mode 100644
index 0000000..36e5e78
--- /dev/null
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/io/stream/eval/ArrayEvaluatorTest.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.stream.eval;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.solr.client.solrj.io.Tuple;
+import org.apache.solr.client.solrj.io.eval.ArrayEvaluator;
+import org.apache.solr.client.solrj.io.eval.StreamEvaluator;
+import org.apache.solr.client.solrj.io.stream.StreamContext;
+import org.apache.solr.client.solrj.io.stream.expr.StreamFactory;
+import org.junit.Test;
+
+import junit.framework.Assert;
+
+public class ArrayEvaluatorTest extends LuceneTestCase {
+
+  StreamFactory factory;
+  Map<String, Object> values;
+  
+  public ArrayEvaluatorTest() {
+    super();
+    
+    factory = new StreamFactory()
+      .withFunctionName("array", ArrayEvaluator.class);
+    values = new HashMap<String,Object>();
+  }
+
+  @Test
+  public void arrayLongSortAscTest() throws IOException{
+    StreamEvaluator evaluator = factory.constructEvaluator("array(a,b,c, sort=asc)");
+    StreamContext context = new StreamContext();
+    evaluator.setStreamContext(context);
+    Object result;
+    
+    values.put("a", 1L);
+    values.put("b", 3L);
+    values.put("c", 2L);
+    
+    result = evaluator.evaluate(new Tuple(values));
+    
+    Assert.assertTrue(result instanceof List<?>);
+    
+    Assert.assertEquals(3, ((List<?>)result).size());
+    Assert.assertEquals(1L, ((List<?>)result).get(0));
+    Assert.assertEquals(2L, ((List<?>)result).get(1));
+    Assert.assertEquals(3L, ((List<?>)result).get(2));
+  }
+
+  @Test
+  public void arrayLongSortDescTest() throws IOException{
+    StreamEvaluator evaluator = factory.constructEvaluator("array(a,b,c, sort=desc)");
+    StreamContext context = new StreamContext();
+    evaluator.setStreamContext(context);
+    Object result;
+    
+    values.put("a", 1L);
+    values.put("b", 3L);
+    values.put("c", 2L);
+    
+    result = evaluator.evaluate(new Tuple(values));
+    
+    Assert.assertTrue(result instanceof List<?>);
+    
+    Assert.assertEquals(3, ((List<?>)result).size());
+    Assert.assertEquals(3L, ((List<?>)result).get(0));
+    Assert.assertEquals(2L, ((List<?>)result).get(1));
+    Assert.assertEquals(1L, ((List<?>)result).get(2));
+  }
+  
+  @Test
+  public void arrayStringSortAscTest() throws IOException{
+    StreamEvaluator evaluator = factory.constructEvaluator("array(a,b,c, sort=asc)");
+    StreamContext context = new StreamContext();
+    evaluator.setStreamContext(context);
+    Object result;
+    
+    values.put("a", "a");
+    values.put("b", "c");
+    values.put("c", "b");
+    
+    result = evaluator.evaluate(new Tuple(values));
+    
+    Assert.assertTrue(result instanceof List<?>);
+    
+    Assert.assertEquals(3, ((List<?>)result).size());
+    Assert.assertEquals("a", ((List<?>)result).get(0));
+    Assert.assertEquals("b", ((List<?>)result).get(1));
+    Assert.assertEquals("c", ((List<?>)result).get(2));
+  }
+
+  @Test
+  public void arrayStringSortDescTest() throws IOException{
+    StreamEvaluator evaluator = factory.constructEvaluator("array(a,b,c, sort=desc)");
+    StreamContext context = new StreamContext();
+    evaluator.setStreamContext(context);
+    Object result;
+    
+    values.put("a", "a");
+    values.put("b", "c");
+    values.put("c", "b");
+    
+    result = evaluator.evaluate(new Tuple(values));
+    
+    Assert.assertTrue(result instanceof List<?>);
+    
+    Assert.assertEquals(3, ((List<?>)result).size());
+    Assert.assertEquals("c", ((List<?>)result).get(0));
+    Assert.assertEquals("b", ((List<?>)result).get(1));
+    Assert.assertEquals("a", ((List<?>)result).get(2));
+  }
+  
+  @Test
+  public void arrayStringUnsortedTest() throws IOException{
+    StreamEvaluator evaluator = factory.constructEvaluator("array(a,b,c)");
+    StreamContext context = new StreamContext();
+    evaluator.setStreamContext(context);
+    Object result;
+    
+    values.put("a", "a");
+    values.put("b", "c");
+    values.put("c", "b");
+    
+    result = evaluator.evaluate(new Tuple(values));
+    
+    Assert.assertTrue(result instanceof List<?>);
+    
+    Assert.assertEquals(3, ((List<?>)result).size());
+    Assert.assertEquals("a", ((List<?>)result).get(0));
+    Assert.assertEquals("c", ((List<?>)result).get(1));
+    Assert.assertEquals("b", ((List<?>)result).get(2));
+  }
+
+
+
+
+}