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

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

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongField.java
new file mode 100644
index 0000000..ac4da2e
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongField.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.NumericDocValues;
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+import org.apache.solr.analytics.value.LongValue.CastingLongValue;
+import org.apache.solr.schema.LongPointField;
+import org.apache.solr.schema.TrieLongField;
+
+/**
+ * An analytics wrapper for a single-valued {@link TrieLongField} or {@link LongPointField} with DocValues enabled.
+ */
+public class LongField extends AnalyticsField implements CastingLongValue {
+  private NumericDocValues docValues;
+  private long value;
+  private boolean exists;
+
+  public LongField(String fieldName) {
+    super(fieldName);
+  }
+
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getNumeric(context.reader(), fieldName);
+  }
+
+  @Override
+  public void collect(int doc) throws IOException {
+    exists = docValues.advanceExact(doc);
+    if (exists) {
+      value = docValues.longValue();
+    }
+  }
+
+  @Override
+  public long getLong() {
+    return value;
+  }
+  @Override
+  public double getDouble() {
+    return (double)value;
+  }
+  @Override
+  public String getString() {
+    return exists ? Long.toString(value) : null;
+  }
+  @Override
+  public Object getObject() {
+    return exists ? value : null;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    if (exists) {
+      cons.accept((double)value);
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    if (exists) {
+      cons.accept(Long.toString(value));
+    }
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+
+  @Override
+  public ExpressionComparator<Long> getObjectComparator(String expression) {
+    return new ExpressionComparator<>(expression);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiField.java
new file mode 100644
index 0000000..dc2a953
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiField.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.solr.analytics.value.LongValueStream.CastingLongValueStream;
+import org.apache.solr.legacy.LegacyNumericUtils;
+import org.apache.solr.schema.TrieLongField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link TrieLongField} with DocValues enabled.
+ */
+public class LongMultiField extends AnalyticsField implements CastingLongValueStream {
+  private SortedSetDocValues docValues;
+  private int count;
+  private long[] values;
+
+  public LongMultiField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new long[initialArrayLength];
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSortedSet(context.reader(), fieldName);
+  }
+  @Override
+  public void collect(int doc) throws IOException {
+    count = 0;
+    if (docValues.advanceExact(doc)) {
+      int term;
+      while ((term = (int)docValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) {
+        if (count == values.length) {
+          resizeValues();
+        }
+        values[count++] = LegacyNumericUtils.prefixCodedToLong(docValues.lookupOrd(term));
+      }
+    }
+  }
+  
+  private void resizeValues() {
+    long[] newValues = new long[values.length*2];
+    for (int i = 0; i < count; ++i) {
+      newValues[i] = values[i];
+    }
+    values = newValues;
+  }
+  
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    streamLongs(value -> cons.accept((double)value));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamLongs(value -> cons.accept(Long.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamLongs(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiPointField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiPointField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiPointField.java
new file mode 100644
index 0000000..31d14ae
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/LongMultiPointField.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedNumericDocValues;
+import org.apache.solr.analytics.value.LongValueStream.CastingLongValueStream;
+import org.apache.solr.schema.LongPointField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link LongPointField} with DocValues enabled.
+ */
+public class LongMultiPointField extends AnalyticsField implements CastingLongValueStream {
+  private SortedNumericDocValues docValues;
+  private int count;
+  private long[] values;
+
+  public LongMultiPointField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new long[initialArrayLength];
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSortedNumeric(context.reader(), fieldName);
+  }
+  
+  @Override
+  public void collect(int doc) throws IOException {
+    if (docValues.advanceExact(doc)) {
+      count = docValues.docValueCount();
+      resizeEmptyValues(count);
+      for (int i = 0; i < count; ++i) {
+        values[i] = docValues.nextValue();
+      }
+    } else {
+      count = 0;
+    }
+  }
+  
+  private void resizeEmptyValues(int count) {
+    if (count > values.length) {
+      values = new long[count];
+    }
+  }
+  
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    streamLongs(value -> cons.accept((double)value));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamLongs(value -> cons.accept(Long.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamLongs(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringField.java
new file mode 100644
index 0000000..207a95a
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringField.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+
+import org.apache.lucene.index.BinaryDocValues;
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+import org.apache.solr.analytics.value.StringValue.CastingStringValue;
+import org.apache.solr.schema.StrField;
+
+/**
+ * An analytics wrapper for a single-valued {@link StrField} with DocValues enabled.
+ */
+public class StringField extends AnalyticsField implements CastingStringValue {
+  private BinaryDocValues docValues;
+  String value;
+  boolean exists;
+
+  public StringField(String fieldName) {
+    super(fieldName);
+    exists = false;
+  }
+
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getBinary(context.reader(), fieldName);
+  }
+
+  @Override
+  public void collect(int doc) throws IOException {
+    exists = docValues.advanceExact(doc);
+    if (exists) {
+      value = docValues.binaryValue().utf8ToString();
+    }
+  }
+
+  @Override
+  public String getString() {
+    return exists ? value : null;
+  }
+  @Override
+  public Object getObject() {
+    return exists ? value : null;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+
+  @Override
+  public ExpressionComparator<String> getObjectComparator(String expression) {
+    return new ExpressionComparator<>(expression);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringMultiField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringMultiField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringMultiField.java
new file mode 100644
index 0000000..39c60f0
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/StringMultiField.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.analytics.function.field;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.solr.analytics.value.StringValueStream.CastingStringValueStream;
+import org.apache.solr.schema.StrField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link StrField} with DocValues enabled.
+ */
+public class StringMultiField extends AnalyticsField implements CastingStringValueStream {
+  private SortedSetDocValues docValues;
+  private ArrayList<String> values;
+
+  public StringMultiField(String fieldName) {
+    super(fieldName);
+    values = new ArrayList<>(initialArrayLength);
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSortedSet(context.reader(), fieldName);
+  }
+  @Override
+  public void collect(int doc) throws IOException {
+    values.clear();
+    if (docValues.advanceExact(doc)) {
+      int term;
+      while ((term = (int)docValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) {
+        values.add(docValues.lookupOrd(term).utf8ToString());
+      }
+    }
+  }
+  
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    values.forEach(value -> cons.accept(value));
+  }
+
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    values.forEach(value -> cons.accept(value));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/package-info.java
new file mode 100644
index 0000000..a0e5421
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+/** 
+ * Fields to use for analytics expressions.
+ */
+package org.apache.solr.analytics.function.field;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AbsoluteValueFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AbsoluteValueFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AbsoluteValueFunction.java
new file mode 100644
index 0000000..d52810f
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AbsoluteValueFunction.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * An absolute value mapping function.
+ * <p>
+ * Takes a numeric ValueStream or Value and returns a ValueStream or Value of the same numeric type.
+ */
+public class AbsoluteValueFunction {
+  public static final String name = "abs";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 1) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 1 paramaters, " + params.length + " found.");
+    }
+    AnalyticsValueStream param = params[0];
+    if (param instanceof IntValueStream) {
+      return LambdaFunction.createIntLambdaFunction(name, x -> (x<0)? x*-1:x, (IntValueStream)param);
+    }
+    if (param instanceof LongValueStream) {
+      return LambdaFunction.createLongLambdaFunction(name, x -> (x<0)? x*-1:x, (LongValueStream)param);
+    }
+    if (param instanceof FloatValueStream) {
+      return LambdaFunction.createFloatLambdaFunction(name, x -> (x<0)? x*-1:x, (FloatValueStream)param);
+    }
+    if (param instanceof DoubleValueStream) {
+      return LambdaFunction.createDoubleLambdaFunction(name, x -> (x<0)? x*-1:x, (DoubleValueStream)param);
+      }
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a numeric parameter, "+param.getExpressionStr()+" found.");
+  });
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AddFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AddFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AddFunction.java
new file mode 100644
index 0000000..d66f84e
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/AddFunction.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.analytics.function.mapping;
+
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+
+/**
+ * An addition mapping function.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If a single numeric ValueStream is passed in, a {@link DoubleValue} representing the sum of the values for each document is returned.
+ * <li>If a numeric ValueStream and a numeric Value are passed in, a {@link DoubleValueStream} representing the sum of 
+ * the Value and each of the values of the ValueStream for a document is returned.
+ * (Or the other way, since the Value and ValueStream can be used in either order)
+ * <li>If multiple numeric Values are passed in, a {@link DoubleValue} representing the sum of all values is returned.
+ * </ul>
+ */
+public class AddFunction {
+  public static final String name = "add";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires parameters.");
+    } 
+    else if (params.length == 1) {
+      if (params[0] instanceof DoubleValueStream) {
+        return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> a+b, (DoubleValueStream)params[0]);
+      }
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires numeric parameters. Incorrect param: "+params[0].getExpressionStr());
+    } 
+    else if (params.length == 2) {
+      AnalyticsValueStream param1 = params[0];
+      AnalyticsValueStream param2 = params[1];
+      if (param1 instanceof DoubleValueStream && param2 instanceof DoubleValueStream) {
+        return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> a+b, (DoubleValueStream)param1, (DoubleValueStream)param2);
+      }
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires numeric parameters.");
+    }
+    DoubleValue[] castedParams = new DoubleValue[params.length];
+    for (int i = 0; i < params.length; i++) {
+      if (params[i] instanceof DoubleValue) {
+        castedParams[i] = (DoubleValue) params[i];
+      } else {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires that all parameters be single-valued if more than 2 are given.");
+      }
+    }
+    return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> a+b, castedParams);
+  });
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/BottomFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/BottomFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/BottomFunction.java
new file mode 100644
index 0000000..f896da4
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/BottomFunction.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue;
+import org.apache.solr.analytics.value.FloatValueStream;
+import org.apache.solr.analytics.value.IntValue;
+import org.apache.solr.analytics.value.IntValueStream;
+import org.apache.solr.analytics.value.LongValue;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A bottom mapping function, returning the lowest value found.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If a single comparable ValueStream is passed in, a Value (of the same type) representing the minimum of the values for each document is returned.
+ * <li>If multiple comparable Values are passed in, a Value (of the same type) representing the minimum of all values is returned.
+ * </ul>
+ */
+public class BottomFunction {
+  public static final String name = "bottom";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires paramaters.");
+    }
+    AnalyticsValueStream param = params[0];
+    if (param instanceof DateValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createDateLambdaFunction(name, (a,b) -> (a<b)? a:b, (DateValueStream)param);
+      }
+      DateValue[] castedParams = new DateValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof DateValue) {
+          castedParams[i] = (DateValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createDateLambdaFunction(name, (a,b) -> (a<b)? a:b, castedParams, false);
+      }
+    }
+    if (param instanceof IntValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createIntLambdaFunction(name, (a,b) -> (a<b)? a:b, (IntValueStream)param);
+      }
+      IntValue[] castedParams = new IntValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof IntValue) {
+          castedParams[i] = (IntValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createIntLambdaFunction(name, (a,b) -> (a<b)? a:b, castedParams, false);
+      }
+    }
+    if (param instanceof LongValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createLongLambdaFunction(name, (a,b) -> (a<b)? a:b, (LongValueStream)param);
+      }
+      LongValue[] castedParams = new LongValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof LongValue) {
+          castedParams[i] = (LongValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createLongLambdaFunction(name, (a,b) -> (a<b)? a:b, castedParams, false);
+      }
+    }
+    if (param instanceof FloatValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createFloatLambdaFunction(name, (a,b) -> (a<b)? a:b, (FloatValueStream)param);
+      }
+      FloatValue[] castedParams = new FloatValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof FloatValue) {
+          castedParams[i] = (FloatValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createFloatLambdaFunction(name, (a,b) -> (a<b)? a:b, castedParams, false);
+      }
+    }
+    if (param instanceof DoubleValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> (a<b)? a:b, (DoubleValueStream)param);
+      }
+      DoubleValue[] castedParams = new DoubleValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof DoubleValue) {
+          castedParams[i] = (DoubleValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> (a<b)? a:b, castedParams, false);
+      }
+    }
+    if (param instanceof StringValueStream) {
+      if (params.length == 1) {
+        return LambdaFunction.createStringLambdaFunction(name, (a,b) -> (a.compareTo(b)<=0)? a:b, (StringValueStream)param);
+      }
+      StringValue[] castedParams = new StringValue[params.length];
+      boolean tryNextType = false;
+      for (int i = 0; i < params.length; i++) {
+        if (params[i] instanceof StringValue) {
+          castedParams[i] = (StringValue) params[i];
+        } else {
+          tryNextType = true;
+          break;
+        }
+      }
+      if (!tryNextType) {
+        return LambdaFunction.createStringLambdaFunction(name, (a,b) -> (a.compareTo(b)<=0)? a:b, castedParams, false);
+      }
+    }
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a comparable parameter. " + 
+          "Incorrect parameter: "+params[0].getExpressionStr());
+  });
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/CompareFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/CompareFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/CompareFunction.java
new file mode 100644
index 0000000..a06f7b8
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/CompareFunction.java
@@ -0,0 +1,614 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.value.AnalyticsValue;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.BooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.BooleanValue.AbstractBooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream.AbstractBooleanValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * Contains all comparable functions. Comparable functions accept two parameters and return a BooleanValueStream.
+ * The two parameters must be able to be cast to the same type.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If a two comparable {@link AnalyticsValue}s are passed in, a {@link BooleanValue} representing the comparison of the two values for each document is returned.
+ * <li>If a comparable {@link AnalyticsValue} and a comparable {@link AnalyticsValueStream} are passed in, 
+ * a {@link BooleanValueStream} representing the comparison of the Value and each of the values of the ValueStream for the document is returned.
+ * </ul>
+ */
+public class CompareFunction {
+  
+  private static BooleanValueStream createCompareFunction(String name, CompResultFunction comp, AnalyticsValueStream... params) {
+    if (params.length != 2) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 2 paramaters, " + params.length + " found.");
+    }
+    AnalyticsValueStream paramA = params[0];
+    AnalyticsValueStream paramB = params[1];
+    if (paramA instanceof DateValueStream && paramB instanceof DateValueStream) {
+      if (paramA instanceof DateValue) {
+        if (paramB instanceof DateValue) {
+          return new CompareDateValueFunction(name,(DateValue)paramA,(DateValue)paramB,comp);
+        }
+        return new CompareDateStreamFunction(name,(DateValue)paramA,(DateValueStream)paramB,comp);
+      }
+      if (paramB instanceof DateValue) {
+        return new CompareDateStreamFunction(name,(DateValue)paramB,(DateValueStream)paramA,reverse(comp));
+      }
+    } else if (paramA instanceof DoubleValueStream && paramB instanceof DoubleValueStream) {
+      if (paramA instanceof DoubleValue) {
+        if (paramB instanceof DoubleValue) {
+          return new CompareDoubleValueFunction(name,(DoubleValue)paramA,(DoubleValue)paramB,comp);
+        }
+        return new CompareDoubleStreamFunction(name,(DoubleValue)paramA,(DoubleValueStream)paramB,comp);
+      }
+      if (paramB instanceof DoubleValue) {
+        return new CompareDoubleStreamFunction(name,(DoubleValue)paramB,(DoubleValueStream)paramA,reverse(comp));
+      }
+    } else if (paramA instanceof StringValueStream && paramB instanceof StringValueStream) {
+      if (paramA instanceof StringValue) {
+        if (paramB instanceof StringValue) {
+          return new CompareStringValueFunction(name,(StringValue)paramA,(StringValue)paramB,comp);
+        }
+        return new CompareStringStreamFunction(name,(StringValue)paramA,(StringValueStream)paramB,comp);
+      }
+      if (paramB instanceof StringValue) {
+        return new CompareStringStreamFunction(name,(StringValue)paramB,(StringValueStream)paramA,reverse(comp));
+      }
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires comparable parameters.");
+    }
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires that at least 1 parameter be single-valued.");
+  }
+  
+  /**
+   * A comparison function that tests equality.
+   */
+  public static class EqualFunction {
+    public static final String name = "equal";
+    public static final CreatorFunction creatorFunction = (params -> {
+      try {
+        return CompareFunction.createCompareFunction(name, val -> {
+          return val == 0;
+        }, params);
+      } catch (SolrException e) {
+        if (params.length != 2) {
+          throw e;
+        }
+        
+        AnalyticsValueStream paramA = params[0];
+        AnalyticsValueStream paramB = params[1];
+        
+        // Booleans aren't really comparable, so just enable the equal function
+        if (paramA instanceof BooleanValueStream && paramB instanceof BooleanValueStream) {
+          if (paramA instanceof BooleanValue) {
+            if (paramB instanceof BooleanValue) {
+              return new BooleanValueEqualFunction((BooleanValue)paramA,(BooleanValue)paramB);
+            }
+            return new BooleanStreamEqualFunction((BooleanValue)paramA,(BooleanValueStream)paramB);
+          } else if (paramB instanceof BooleanValue) {
+            return new BooleanStreamEqualFunction((BooleanValue)paramB,(BooleanValueStream)paramA);
+          }
+        }
+        
+        // This means that the Objects created by the AnalyticsValueStreams are not comparable, so use the .equals() method instead
+        else if (paramA instanceof AnalyticsValue) {
+          if (paramB instanceof AnalyticsValue) {
+            return new ValueEqualFunction((AnalyticsValue)paramA,(AnalyticsValue)paramB);
+          }
+          return new StreamEqualFunction((AnalyticsValue)paramA,paramB);
+        }
+        if (paramB instanceof AnalyticsValue) {
+          return new StreamEqualFunction((AnalyticsValue)paramB,paramA);
+        }
+      }
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires that at least 1 parameter be single-valued.");
+    });
+  }
+  
+  /**
+   * A comparison function that tests whether the first parameter is greater than the second parameter
+   */
+  public static class GTFunction {
+    public static final String name = "gt";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return CompareFunction.createCompareFunction(name, val -> {
+        return val > 0;
+      }, params);
+    });
+  }
+
+  /**
+   * A comparison function that tests whether the first parameter is greater than or equal to the second parameter
+   */
+  public static class GTEFunction {
+    public static final String name = "gte";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return CompareFunction.createCompareFunction(name, val -> {
+        return val >= 0;
+      }, params);
+    });
+  }
+
+  /**
+   * A comparison function that tests whether the first parameter is less than the second parameter
+   */
+  public static class LTFunction {
+    public static final String name = "lt";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return CompareFunction.createCompareFunction(name, val -> {
+        return val < 0;
+      }, params);
+    });
+  }
+
+  /**
+   * A comparison function that tests whether the first parameter is less than or equal to the second parameter
+   */
+  public static class LTEFunction {
+    public static final String name = "lte";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return CompareFunction.createCompareFunction(name, val -> {
+        return val <= 0;
+      }, params);
+    });
+  }
+  
+  private static CompResultFunction reverse(CompResultFunction original) {
+    return val -> original.apply(val*-1);
+  }
+}
+@FunctionalInterface
+interface CompResultFunction {
+  public boolean apply(int compResult);
+}
+/**
+ * A comparison function for two {@link DoubleValue}s.
+ */
+class CompareDoubleValueFunction extends AbstractBooleanValue {
+  private final DoubleValue exprA;
+  private final DoubleValue exprB;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareDoubleValueFunction(String name, DoubleValue exprA, DoubleValue exprB, CompResultFunction comp) {
+    this.name = name;
+    this.exprA = exprA;
+    this.exprB = exprB;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,exprA,exprB);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,exprA,exprB);
+  }
+
+  private boolean exists = false;
+  @Override
+  public boolean getBoolean() {
+    double valueA = exprA.getDouble();
+    double valueB = exprB.getDouble();
+    exists = exprA.exists() && exprB.exists();
+    return exists ? comp.apply(Double.compare(valueA,valueB)) : false;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A comparison function for a {@link DoubleValue} and a {@link DoubleValueStream}.
+ */
+class CompareDoubleStreamFunction extends AbstractBooleanValueStream {
+  private final DoubleValue baseExpr;
+  private final DoubleValueStream compExpr;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareDoubleStreamFunction(String name, DoubleValue baseExpr, DoubleValueStream compExpr, CompResultFunction comp) throws SolrException {
+    this.name = name;
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,baseExpr,compExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    double baseValue = baseExpr.getDouble();
+    if (baseExpr.exists()) {
+      compExpr.streamDoubles(compValue -> cons.accept(comp.apply(Double.compare(baseValue,compValue))));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A comparison function for two {@link DateValue}s.
+ */
+class CompareDateValueFunction extends AbstractBooleanValue {
+  private final DateValue exprA;
+  private final DateValue exprB;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareDateValueFunction(String name, DateValue exprA, DateValue exprB, CompResultFunction comp) {
+    this.name = name;
+    this.exprA = exprA;
+    this.exprB = exprB;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,exprA,exprB);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,exprA,exprB);
+  }
+
+  private boolean exists = false;
+  @Override
+  public boolean getBoolean() {
+    long valueA = exprA.getLong();
+    long valueB = exprB.getLong();
+    exists = exprA.exists() && exprB.exists();
+    return exists ? comp.apply(Long.compare(valueA,valueB)) : false;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A comparison function for a {@link DateValue} and a {@link DateValueStream}.
+ */
+class CompareDateStreamFunction extends AbstractBooleanValueStream {
+  private final DateValue baseExpr;
+  private final DateValueStream compExpr;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareDateStreamFunction(String name, DateValue baseExpr, DateValueStream compExpr, CompResultFunction comp) throws SolrException {
+    this.name = name;
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,baseExpr,compExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    long baseValue = baseExpr.getLong();
+    if (baseExpr.exists()) {
+      compExpr.streamLongs(compValue -> cons.accept(comp.apply(Long.compare(baseValue,compValue))));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A comparison function for two {@link StringValue}s.
+ */
+class CompareStringValueFunction extends AbstractBooleanValue {
+  private final StringValue exprA;
+  private final StringValue exprB;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareStringValueFunction(String name, StringValue exprA, StringValue exprB, CompResultFunction comp) {
+    this.name = name;
+    this.exprA = exprA;
+    this.exprB = exprB;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,exprA,exprB);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,exprA,exprB);
+  }
+
+  private boolean exists = false;
+  @Override
+  public boolean getBoolean() {
+    String valueA = exprA.toString();
+    String valueB = exprB.toString();
+    exists = exprA.exists() && exprB.exists();
+    return exists ? comp.apply(valueA.compareTo(valueB)) : false;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A comparison function for a {@link StringValue} and a {@link StringValueStream}.
+ */
+class CompareStringStreamFunction extends AbstractBooleanValueStream {
+  private final StringValue baseExpr;
+  private final StringValueStream compExpr;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareStringStreamFunction(String name, StringValue baseExpr, StringValueStream compExpr, CompResultFunction comp) throws SolrException {
+    this.name = name;
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,baseExpr,compExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    String baseValue = baseExpr.toString();
+    if (baseExpr.exists()) {
+      compExpr.streamStrings(compValue -> cons.accept(comp.apply(baseValue.compareTo(compValue))));
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * An equal function for two {@link BooleanValue}s.
+ */
+class BooleanValueEqualFunction extends AbstractBooleanValue {
+  private final BooleanValue exprA;
+  private final BooleanValue exprB;
+  public static final String name = CompareFunction.EqualFunction.name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public BooleanValueEqualFunction(BooleanValue exprA, BooleanValue exprB) {
+    this.exprA = exprA;
+    this.exprB = exprB;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,exprA,exprB);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,exprA,exprB);
+  }
+
+  private boolean exists = false;
+  @Override
+  public boolean getBoolean() {
+    boolean valueA = exprA.getBoolean();
+    boolean valueB = exprB.getBoolean();
+    exists = exprA.exists() && exprB.exists();
+    return exists ? valueA == valueB : false;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * An equal function for a {@link BooleanValue} and a {@link BooleanValueStream}.
+ */
+class BooleanStreamEqualFunction extends AbstractBooleanValueStream {
+  private final BooleanValue baseExpr;
+  private final BooleanValueStream compExpr;
+  public static final String name = CompareFunction.EqualFunction.name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public BooleanStreamEqualFunction(BooleanValue baseExpr, BooleanValueStream compExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,baseExpr,compExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    boolean baseValue = baseExpr.getBoolean();
+    if (baseExpr.exists()) {
+      compExpr.streamBooleans(compValue -> cons.accept(baseValue == compValue));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A catch-all equal function for two {@link AnalyticsValue}s.
+ */
+class ValueEqualFunction extends AbstractBooleanValue {
+  private final AnalyticsValue exprA;
+  private final AnalyticsValue exprB;
+  public static final String name = CompareFunction.EqualFunction.name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public ValueEqualFunction(AnalyticsValue exprA, AnalyticsValue exprB) {
+    this.exprA = exprA;
+    this.exprB = exprB;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,exprA,exprB);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,exprA,exprB);
+  }
+
+  private boolean exists = false;
+  @Override
+  public boolean getBoolean() {
+    Object valueA = exprA.getObject();
+    Object valueB = exprB.getObject();
+    exists = exprA.exists() && exprB.exists();
+    return exists ? valueA.equals(valueB) : false;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A catch-all equal function for an {@link AnalyticsValue} and an {@link AnalyticsValueStream}.
+ */
+class StreamEqualFunction extends AbstractBooleanValueStream {
+  private final AnalyticsValue baseExpr;
+  private final AnalyticsValueStream compExpr;
+  public static final String name = CompareFunction.EqualFunction.name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public StreamEqualFunction(AnalyticsValue baseExpr, AnalyticsValueStream compExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,baseExpr,compExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    Object baseValue = baseExpr.getObject();
+    if (baseExpr.exists()) {
+      compExpr.streamObjects(compValue -> cons.accept(baseValue.equals(compValue)));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ConcatFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ConcatFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ConcatFunction.java
new file mode 100644
index 0000000..e54e8c5
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ConcatFunction.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.analytics.function.mapping;
+
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.constant.ConstantStringValue;
+
+/**
+ * A concatenation mapping function, combining the string values of the given parameters. (At least 1 parameter is required)
+ * <p>
+ * Multiple comparable {@link StringValue}s are passed in and a {@link StringValue} representing the concatenation of all values is returned. 
+ */
+public class ConcatFunction {
+  public static final String name = "concat";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires parameters.");
+    } 
+    else if (params.length == 1 && params[0] instanceof StringValue) {
+      return params[0];
+    } 
+    StringValue[] castedParams = new StringValue[params.length];
+    for (int i = 0; i < params.length; i++) {
+      if (params[i] instanceof StringValue) {
+        castedParams[i] = (StringValue) params[i];
+      } else {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires that all parameters be single-valued and convertible to string values.");
+      }
+    }
+    return LambdaFunction.createStringLambdaFunction(name, (a,b) -> a+b, castedParams, false);
+  });
+  
+  /**
+   * A concatenation mapping function, combining the string values of the given parameters with a given separating string.
+   * <br>
+   * Multiple comparable {@link StringValue}s are passed in and a {@link StringValue} representing the separated concatenation of all values is returned.
+   * <p>
+   * The first parameter must be a constant string (e.g. ",").
+   * The remaining parameters are the {@link StringValue} expressions to concatenate. (At least 1 expression is required)
+   */
+  public static class ConcatSeparatedFunction {
+    public static final String name = "concatsep";
+    public static final CreatorFunction creatorFunction = (params -> {
+      if (params.length < 2) {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires at least 2 parameters.");
+      } else if (!(params[0] instanceof ConstantStringValue)) {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires that the first parameter to be a constant string.");
+      }
+      final String sep = ((ConstantStringValue)params[0]).getString();
+      StringValue[] castedParams = new StringValue[params.length - 1];
+      for (int i = 0; i < castedParams.length; i++) {
+        if (params[i + 1] instanceof StringValue) {
+          castedParams[i] = (StringValue) params[i + 1];
+        } else {
+          throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires that all non-separator parameters be single-valued and convertible to string values.");
+        }
+      }
+      return LambdaFunction.createStringLambdaFunction(name, (a,b) -> a + sep + b, castedParams, false);
+    });
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateMathFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateMathFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateMathFunction.java
new file mode 100644
index 0000000..9901625
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateMathFunction.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.analytics.function.mapping;
+
+import java.util.Date;
+import java.util.function.Consumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DateValueStream.AbstractDateValueStream;
+import org.apache.solr.analytics.value.constant.ConstantStringValue;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.util.DateMathParser;
+
+/**
+ * A mapping function that computes date math.
+ * <p>
+ * The first parameter is the {@link DateValue} or {@link DateValueStream} to compute date math on. (Required)
+ * <br>
+ * The trailing parameters must be constant date math strings (e.g. "+1DAY"). (At least 1 required)
+ */
+public class DateMathFunction {
+  public static final String name = "date_math";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length < 2) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires at least 2 paramaters, " + params.length + " found.");
+    }
+    StringBuilder mathParam = new StringBuilder();
+    for (int i = 1; i < params.length; ++i) {
+      if (params[i] instanceof ConstantStringValue) {
+        mathParam.append(((ConstantStringValue) params[i]).getString());
+      } else {
+        throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires all math parameters to be a constant strings.");
+      }
+    }
+    if (params[0] instanceof DateValue) {
+      return new DateMathValueFunction((DateValue)params[0], new ConstantStringValue(mathParam.toString()));
+    } else if (params[0] instanceof DateValueStream) {
+      return new DateMathStreamFunction((DateValueStream)params[0], new ConstantStringValue(mathParam.toString()));
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a date as the first parameter.");
+    }
+  });
+}
+/**
+ * DateMath function that supports {@link DateValue}s.
+ */
+class DateMathValueFunction extends AbstractDateValue {
+  private final DateValue dateParam;
+  private final String mathParam;
+  DateMathParser parser = new DateMathParser();
+  public static final String name = DateMathFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateMathValueFunction(DateValue dateParam, ConstantStringValue mathParam) throws SolrException {
+    this.dateParam = dateParam;
+    this.mathParam = "NOW" + mathParam.getString();
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,dateParam,mathParam);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,dateParam);
+  }
+
+  private boolean exists = false;
+  
+  @Override
+  public long getLong() {
+    Date date = getDate();
+    return (date == null) ? 0 : date.getTime();
+  }
+  @Override
+  public Date getDate() {
+    Date date = dateParam.getDate();
+    if (dateParam.exists()) {
+      exists = true;
+      return DateMathParser.parseMath(date,mathParam);
+    } else {
+      exists = false;
+      return null;
+    }
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * DateMath function that supports {@link DateValueStream}s.
+ */
+class DateMathStreamFunction extends AbstractDateValueStream {
+  private final DateValueStream dateParam;
+  private final String mathParam;
+  public static final String name = DateMathFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateMathStreamFunction(DateValueStream dateParam, ConstantStringValue mathParam) throws SolrException {
+    this.dateParam = dateParam;
+    this.mathParam = "NOW" + mathParam.getString();
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,dateParam,mathParam);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,dateParam);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    streamDates(value -> cons.accept(value.getTime()));
+  }
+  @Override
+  public void streamDates(Consumer<Date> cons) {
+    dateParam.streamDates(value -> cons.accept(DateMathParser.parseMath(value, mathParam)));
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateParseFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateParseFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateParseFunction.java
new file mode 100644
index 0000000..5929b88
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DateParseFunction.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.analytics.function.mapping;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.LongValue;
+import org.apache.solr.analytics.value.LongValueStream;
+import org.apache.solr.analytics.value.StringValue;
+import org.apache.solr.analytics.value.StringValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DateValueStream.AbstractDateValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A mapping function that converts long or string representations of dates to actual date objects.
+ * <p>
+ * The only parameter is the {@link LongValue}, {@link LongValueStream}, {@link DateValue}, or {@link DateValueStream} to convert. (Required)
+ */
+public class DateParseFunction {
+  public static final String name = "date";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 1) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 1 paramater, " + params.length + " found.");
+    }
+    if (params[0] instanceof LongValue) {
+      return new LongToDateParseFunction((LongValue)params[0]);
+    }
+    else if (params[0] instanceof LongValueStream) {
+      return new LongStreamToDateParseFunction((LongValueStream)params[0]);
+    }
+    else if (params[0] instanceof StringValue) {
+      return new StringToDateParseFunction((StringValue)params[0]);
+    }
+    else if (params[0] instanceof StringValueStream) {
+      return new StringStreamToDateParseFunction((StringValueStream)params[0]);
+    }
+    else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a string or long parameter. " + 
+          "Incorrect parameter: "+params[0].getExpressionStr());
+    }
+  });
+}
+class LongToDateParseFunction extends AbstractDateValue {
+  private final LongValue param;
+  public static final String name = DateParseFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongToDateParseFunction(LongValue param) throws SolrException {
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  @Override
+  public long getLong() {
+    return param.getLong();
+  }
+  @Override
+  public boolean exists() {
+    return param.exists();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongStreamToDateParseFunction extends AbstractDateValueStream {
+  private final LongValueStream param;
+  public static final String name = DateParseFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  public static final DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss.SSS", Locale.ROOT);
+  
+  public LongStreamToDateParseFunction(LongValueStream param) throws SolrException {
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    param.streamLongs(cons);
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringToDateParseFunction extends AbstractDateValue {
+  private final StringValue param;
+  public static final String name = DateParseFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  public static final DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss.SSS", Locale.ROOT);
+  
+  public StringToDateParseFunction(StringValue param) throws SolrException {
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  private boolean exists = false;
+  @Override
+  public long getLong() {
+    long value = 0;
+    try {
+      value = formatter.parse(param.toString()).getTime();
+      exists = param.exists();
+    } catch (ParseException e) {
+      exists = false;
+    }
+    return value;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringStreamToDateParseFunction extends AbstractDateValueStream {
+  private final StringValueStream param;
+  public static final String name = DateParseFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  public static final DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss.SSS", Locale.ROOT);
+  
+  public StringStreamToDateParseFunction(StringValueStream param) throws SolrException {
+    this.param = param;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,param);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    param.streamStrings(value -> {
+      try {
+        cons.accept(formatter.parse(value).getTime());
+      } catch (ParseException e) {
+      }
+    });
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DivideFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DivideFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DivideFunction.java
new file mode 100644
index 0000000..e644812
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/DivideFunction.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A division mapping function. No checking on divisor value is done. An error will occur if a zero divisor is used.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If two numeric Values are passed in, a {@link DoubleValue} representing the divison of the two values is returned.
+ * <li>If a numeric ValueStream and a numeric Value are passed in, a {@link DoubleValueStream} representing the division of 
+ * the Value and each of the values of the ValueStream for a document is returned. 
+ * (Or the other way, since the Value and ValueStream can be used in either order)
+ * </ul>
+ */
+public class DivideFunction {
+  public static final String name = "div";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 2) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 2 paramaters, " + params.length + " found.");
+    }
+    AnalyticsValueStream param1 = params[0];
+    AnalyticsValueStream param2 = params[1];
+    if (param1 instanceof DoubleValueStream && param2 instanceof DoubleValueStream) {
+      return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> a/b, (DoubleValueStream)param1, (DoubleValueStream)param2);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires numeric parameters.");
+    }
+  });
+}
\ No newline at end of file