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

[13/58] [abbrv] lucene-solr:jira/solr-10879: SOLR-10123: Upgraded the Analytics Component to version 2.0

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LogFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LogFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LogFunction.java
new file mode 100644
index 0000000..313f2e2
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LogFunction.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.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A logarithm mapping function.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If one numeric Value or ValueStream is passed in, a {@link DoubleValue} or {@link DoubleValueStream}
+ * representing the natural logarithm is returned.
+ * <li>If two numeric Values are passed in, a {@link DoubleValue} representing the logarithm of the first with the second as the base is returned.
+ * <li>If a numeric ValueStream and a numeric Value are passed in, a {@link DoubleValueStream} representing the logarithm of 
+ * the Value with each of the values of the ValueStream for a document as the base is returned. 
+ * (Or the other way, since the Value and ValueStream can be used in either order)
+ * </ul>
+ */
+public class LogFunction {
+  public static final String name = "log";
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires at least 1.");
+    } else if (params.length == 1) {
+      return LambdaFunction.createDoubleLambdaFunction(name, (a) -> Math.log(a), (DoubleValueStream)params[0]);
+    } else if (params.length == 2) {
+      return LambdaFunction.createDoubleLambdaFunction(name, (a,b) -> Math.log(a)/Math.log(b), (DoubleValueStream)params[0], (DoubleValueStream)params[1]);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function accepts at most 2 paramaters, " + params.length + " 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/LogicFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LogicFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LogicFunction.java
new file mode 100644
index 0000000..806cb0a
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/LogicFunction.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.function.mapping.LambdaFunction.TwoBoolInBoolOutLambda;
+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.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * Contains all logical mapping functions.
+ * <p>
+ * Logical mapping functions can be used in the following ways:
+ * <ul>
+ * <li>If a single {@link BooleanValueStream} is passed in, a {@link BooleanValue} representing the logical operation
+ * on all of the values for each document is returned.
+ * <li>If a {@link BooleanValueStream} and a {@link BooleanValue} are passed in, a {@link BooleanValue} representing the logical operation on 
+ * the {@link BooleanValue} and each of the values of the {@link BooleanValueStream} for a document is returned.
+ * (Or the other way, since the Value and ValueStream can be used in either order)
+ * <li>If multiple {@link BooleanValue}s are passed in, a {@link BooleanValue} representing the logical operation on all values is returned.
+ * </ul>
+ */
+public class LogicFunction {
+  
+  private static BooleanValueStream createBitwiseFunction(String name, TwoBoolInBoolOutLambda comp, AnalyticsValueStream... 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 BooleanValueStream) {
+        return LambdaFunction.createBooleanLambdaFunction(name, comp, (BooleanValueStream)params[0]);
+      }
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires boolean parameters. Incorrect param: "+params[0].getExpressionStr());
+    } 
+    else if (params.length == 2) {
+      AnalyticsValueStream param1 = params[0];
+      AnalyticsValueStream param2 = params[1];
+      if (param1 instanceof BooleanValueStream && param2 instanceof BooleanValueStream) {
+        return LambdaFunction.createBooleanLambdaFunction(name, (a,b) -> a && b, (BooleanValueStream)param1, (BooleanValueStream)param2);
+      }
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires boolean parameters.");
+    }
+    BooleanValue[] castedParams = new BooleanValue[params.length];
+    for (int i = 0; i < params.length; i++) {
+      if (params[i] instanceof BooleanValue) {
+        castedParams[i] = (BooleanValue) 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.createBooleanLambdaFunction(name, comp, castedParams);
+  };
+  
+  /**
+   * A mapping function for the logical operation AND.
+   */
+  public static class AndFunction {
+    public static final String name = "and";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return LogicFunction.createBitwiseFunction(name, (a,b) -> a && b, params);
+    });
+  }
+
+  /**
+   * A mapping function for the logical operation OR.
+   */
+  public static class OrFunction {
+    public static final String name = "or";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return LogicFunction.createBitwiseFunction(name, (a,b) -> a || b, params);
+    });
+  }
+}
\ 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/MultFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/MultFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/MultFunction.java
new file mode 100644
index 0000000..4a5b173
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/MultFunction.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 multiplication mapping function.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If a single numeric ValueStream is passed in, a {@link DoubleValue} representing the multiplication 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 multiplication 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 multiplication of all values is returned.
+ * </ul>
+ */
+public class MultFunction {
+  public static final String name = "mult";
+  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/NegateFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/NegateFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/NegateFunction.java
new file mode 100644
index 0000000..65916e6
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/NegateFunction.java
@@ -0,0 +1,58 @@
+/*
+ * 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.BooleanValueStream;
+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;
+
+/**
+ * A negation mapping function.
+ * <p>
+ * Takes a numeric or boolean ValueStream or Value and returns a ValueStream or Value of the same numeric type.
+ */
+public class NegateFunction {
+  public static final String name = "neg";
+  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 BooleanValueStream) {
+      return LambdaFunction.createBooleanLambdaFunction(name, x -> !x, (BooleanValueStream)param);
+    }
+    if (param instanceof IntValueStream) {
+      return LambdaFunction.createIntLambdaFunction(name, x -> x*-1, (IntValueStream)param);
+    }
+    if (param instanceof LongValueStream) {
+      return LambdaFunction.createLongLambdaFunction(name, x -> x*-1, (LongValueStream)param);
+    }
+    if (param instanceof FloatValueStream) {
+      return LambdaFunction.createFloatLambdaFunction(name, x -> x*-1, (FloatValueStream)param);
+    }
+    if (param instanceof DoubleValueStream) {
+      return LambdaFunction.createDoubleLambdaFunction(name, x -> x*-1, (DoubleValueStream)param);
+      }
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a boolean or 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/NumericConvertFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/NumericConvertFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/NumericConvertFunction.java
new file mode 100644
index 0000000..2381f60
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/NumericConvertFunction.java
@@ -0,0 +1,256 @@
+/*
+ * 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.function.IntConsumer;
+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.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.IntValue.AbstractIntValue;
+import org.apache.solr.analytics.value.IntValueStream.AbstractIntValueStream;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.value.LongValueStream.AbstractLongValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * An abstract numeric converting mapping function. For example "round()" would convert a float to an int and a double to a long. 
+ * <p>
+ * Takes a numeric Double or Float ValueStream or Value and returns a Long or Int ValueStream or Value, repectively.
+ */
+public class NumericConvertFunction {
+  
+  private static LongValueStream createConvertFunction(String name, ConvertFloatFunction fconv, ConvertDoubleFunction dconv, AnalyticsValueStream... 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 FloatValueStream) {
+      if (param instanceof FloatValue) {
+        return new ConvertFloatValueFunction(name, (FloatValue)param, fconv);
+      }
+      return new ConvertFloatStreamFunction(name, (FloatValueStream)param, fconv);
+    } else if (param instanceof DoubleValueStream) {
+      if (param instanceof DoubleValue) {
+        return new ConvertDoubleValueFunction(name, (DoubleValue)param, dconv);
+      }
+      return new ConvertDoubleStreamFunction(name, (DoubleValueStream)param, dconv);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires a numeric parameter.");
+    }
+  }
+  
+  /**
+   * A numeric mapping function that returns the floor of the input.
+   */
+  public static class FloorFunction {
+    public static final String name = "floor";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return NumericConvertFunction.createConvertFunction(name, val -> (int)Math.floor(val), val -> (long)Math.floor(val), params);
+    });
+  }
+  
+  /**
+   * A numeric mapping function that returns the ceiling of the input.
+   */
+  public static class CeilingFunction {
+    public static final String name = "ceil";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return NumericConvertFunction.createConvertFunction(name, val -> (int)Math.ceil(val), val -> (long)Math.ceil(val), params);
+    });
+  }
+  
+  /**
+   * A numeric mapping function that returns the rounded input.
+   */
+  public static class RoundFunction {
+    public static final String name = "round";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return NumericConvertFunction.createConvertFunction(name, val -> (int)Math.round(val), val -> (long)Math.round(val), params);
+    });
+  }
+
+}
+@FunctionalInterface
+interface ConvertFloatFunction {
+  public int convert(float value);
+}
+@FunctionalInterface
+interface ConvertDoubleFunction {
+  public long convert(double value);
+}
+/**
+ * A function to convert a {@link FloatValue} to a {@link IntValue}.
+ */
+class ConvertFloatValueFunction extends AbstractIntValue {
+  private final String name;
+  private final FloatValue param;
+  private final ConvertFloatFunction conv;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public ConvertFloatValueFunction(String name, FloatValue param, ConvertFloatFunction conv) {
+    this.name = name;
+    this.param = param;
+    this.conv = conv;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,param);
+  }
+
+  @Override
+  public int getInt() {
+    return conv.convert(param.getFloat());
+  }
+  @Override
+  public boolean exists() {
+    return param.exists();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A function to convert a {@link FloatValueStream} to a {@link IntValueStream}.
+ */
+class ConvertFloatStreamFunction extends AbstractIntValueStream {
+  private final String name;
+  private final FloatValueStream param;
+  private final ConvertFloatFunction conv;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public ConvertFloatStreamFunction(String name, FloatValueStream param, ConvertFloatFunction conv) {
+    this.name = name;
+    this.param = param;
+    this.conv = conv;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,param);
+  }
+
+  @Override
+  public void streamInts(IntConsumer cons) {
+    param.streamFloats( value -> cons.accept(conv.convert(value)));
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A function to convert a {@link DoubleValue} to a {@link LongValue}.
+ */
+class ConvertDoubleValueFunction extends AbstractLongValue {
+  private final String name;
+  private final DoubleValue param;
+  private final ConvertDoubleFunction conv;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public ConvertDoubleValueFunction(String name, DoubleValue param, ConvertDoubleFunction conv) {
+    this.name = name;
+    this.param = param;
+    this.conv = conv;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,param);
+  }
+
+  @Override
+  public long getLong() {
+    return conv.convert(param.getDouble());
+  }
+  @Override
+  public boolean exists() {
+    return param.exists();
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A function to convert a {@link DoubleValueStream} to a {@link LongValueStream}.
+ */
+class ConvertDoubleStreamFunction extends AbstractLongValueStream {
+  private final String name;
+  private final DoubleValueStream param;
+  private final ConvertDoubleFunction conv;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public ConvertDoubleStreamFunction(String name, DoubleValueStream param, ConvertDoubleFunction conv) {
+    this.name = name;
+    this.param = param;
+    this.conv = conv;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,param);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,param);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    param.streamDoubles( value -> cons.accept(conv.convert(value)));
+  }
+
+  @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/PowerFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/PowerFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/PowerFunction.java
new file mode 100644
index 0000000..0688ba0
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/PowerFunction.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 power mapping function.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If two numeric Values are passed in, a {@link DoubleValue} representing the first taken to the power of the second is returned.
+ * <li>If a numeric ValueStream and a numeric Value are passed in, a {@link DoubleValueStream} representing the power of 
+ * the Value to 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 PowerFunction {
+  public static final String name = "pow";
+  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) -> Math.pow(a,b), (DoubleValueStream)param1, (DoubleValueStream)param2);
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires numeric parameters.");
+    }
+  });
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/RemoveFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/RemoveFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/RemoveFunction.java
new file mode 100644
index 0000000..5800a1b
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/RemoveFunction.java
@@ -0,0 +1,796 @@
+/*
+ * 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.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+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.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.analytics.value.AnalyticsValue.AbstractAnalyticsValue;
+import org.apache.solr.analytics.value.BooleanValue.AbstractBooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream.AbstractBooleanValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DateValueStream.AbstractDateValueStream;
+import org.apache.solr.analytics.value.DoubleValue.AbstractDoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream.AbstractDoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue.AbstractFloatValue;
+import org.apache.solr.analytics.value.FloatValueStream.AbstractFloatValueStream;
+import org.apache.solr.analytics.value.IntValue.AbstractIntValue;
+import org.apache.solr.analytics.value.IntValueStream.AbstractIntValueStream;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.value.LongValueStream.AbstractLongValueStream;
+import org.apache.solr.analytics.value.StringValue.AbstractStringValue;
+import org.apache.solr.analytics.value.StringValueStream.AbstractStringValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A mapping function to remove an {@link AnalyticsValue} from an {@link AnalyticsValue} or an {@link AnalyticsValueStream}.
+ * For each document, the value exists if it doesn't equal the value of the second parameter.
+ * <p>
+ * The first parameter can be any type of analytics expression. If the parameter is multi-valued, then the return will be multi-valued. (Required)
+ * <br>
+ * The second parameter, which is the value to remove from the first parameter, must be an {@link AnalyticsValue}, aka single-valued. (Required)
+ * <p>
+ * The resulting Value or ValueStream will be typed with the closest super-type of the two parameters.
+ * (e.g. {@value #name}(int,float) will return a float)
+ */
+public class RemoveFunction {
+  public static final String name = "remove";
+  
+  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.");
+    }
+    if (!(params[1] instanceof AnalyticsValue)) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires the remove paramater to be single-valued.");
+    }
+
+    AnalyticsValueStream baseExpr = params[0];
+    AnalyticsValue removeExpr = (AnalyticsValue)params[1];
+
+    if (baseExpr instanceof DateValue && removeExpr instanceof DateValue) {
+      return new DateRemoveFunction((DateValue)baseExpr,(DateValue)removeExpr);
+    }
+    if (baseExpr instanceof DateValueStream && removeExpr instanceof DateValue) {
+      return new DateStreamRemoveFunction((DateValueStream)baseExpr,(DateValue)removeExpr);
+    }
+    if (baseExpr instanceof BooleanValue && removeExpr instanceof BooleanValue) {
+      return new BooleanRemoveFunction((BooleanValue)baseExpr,(BooleanValue)removeExpr);
+    }
+    if (baseExpr instanceof BooleanValueStream && removeExpr instanceof BooleanValue) {
+      return new BooleanStreamRemoveFunction((BooleanValueStream)baseExpr,(BooleanValue)removeExpr);
+    }
+    if (baseExpr instanceof IntValue && removeExpr instanceof IntValue) {
+      return new IntRemoveFunction((IntValue)baseExpr,(IntValue)removeExpr);
+    }
+    if (baseExpr instanceof IntValueStream && removeExpr instanceof IntValue) {
+      return new IntStreamRemoveFunction((IntValueStream)baseExpr,(IntValue)removeExpr);
+    }
+    if (baseExpr instanceof LongValue && removeExpr instanceof LongValue) {
+      return new LongRemoveFunction((LongValue)baseExpr,(LongValue)removeExpr);
+    }
+    if (baseExpr instanceof LongValueStream && removeExpr instanceof LongValue) {
+      return new LongStreamRemoveFunction((LongValueStream)baseExpr,(LongValue)removeExpr);
+    }
+    if (baseExpr instanceof FloatValue && removeExpr instanceof FloatValue) {
+      return new FloatRemoveFunction((FloatValue)baseExpr,(FloatValue)removeExpr);
+    }
+    if (baseExpr instanceof FloatValueStream && removeExpr instanceof FloatValue) {
+      return new FloatStreamRemoveFunction((FloatValueStream)baseExpr,(FloatValue)removeExpr);
+    }
+    if (baseExpr instanceof DoubleValue && removeExpr instanceof DoubleValue) {
+      return new DoubleRemoveFunction((DoubleValue)baseExpr,(DoubleValue)removeExpr);
+    }
+    if (baseExpr instanceof DoubleValueStream && removeExpr instanceof DoubleValue) {
+      return new DoubleStreamRemoveFunction((DoubleValueStream)baseExpr,(DoubleValue)removeExpr);
+    }
+    if (baseExpr instanceof StringValue && removeExpr instanceof StringValue) {
+      return new StringRemoveFunction((StringValue)baseExpr,(StringValue)removeExpr);
+    }
+    if (baseExpr instanceof StringValueStream && removeExpr instanceof StringValue) {
+      return new StringStreamRemoveFunction((StringValueStream)baseExpr,(StringValue)removeExpr);
+    }
+    if (baseExpr instanceof AnalyticsValue) {
+      return new ValueRemoveFunction((AnalyticsValue)baseExpr,removeExpr);
+    }
+    return new StreamRemoveFunction(baseExpr,removeExpr);
+  });
+}
+class StreamRemoveFunction implements AnalyticsValueStream {
+  private final AnalyticsValueStream baseExpr;
+  private final AnalyticsValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StreamRemoveFunction(AnalyticsValueStream baseExpr, AnalyticsValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    Object removeValue = removeExpr.getObject();
+    if (removeExpr.exists()) {
+      baseExpr.streamObjects(value -> {
+        if (removeValue.equals(value)) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamObjects(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class ValueRemoveFunction extends AbstractAnalyticsValue {
+  private final AnalyticsValue baseExpr;
+  private final AnalyticsValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public ValueRemoveFunction(AnalyticsValue baseExpr, AnalyticsValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public Object getObject() {
+    Object value = baseExpr.getObject();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value.equals(removeExpr.toString()) ? removeExpr.exists()? false : true : true;
+    }
+    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 BooleanStreamRemoveFunction extends AbstractBooleanValueStream {
+  private final BooleanValueStream baseExpr;
+  private final BooleanValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanStreamRemoveFunction(BooleanValueStream baseExpr, BooleanValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    boolean removeValue = removeExpr.getBoolean();
+    if (removeExpr.exists()) {
+      baseExpr.streamBooleans(value -> {
+        if (removeValue==value) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamBooleans(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanRemoveFunction extends AbstractBooleanValue {
+  private final BooleanValue baseExpr;
+  private final BooleanValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanRemoveFunction(BooleanValue baseExpr, BooleanValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+  
+  @Override
+  public boolean getBoolean() {
+    boolean value = baseExpr.getBoolean();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value==removeExpr.getBoolean() ? removeExpr.exists()? false : true : true;
+    }
+    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 IntStreamRemoveFunction extends AbstractIntValueStream {
+  private final IntValueStream baseExpr;
+  private final IntValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntStreamRemoveFunction(IntValueStream baseExpr, IntValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamInts(IntConsumer cons) {
+    int removeValue = removeExpr.getInt();
+    if (removeExpr.exists()) {
+      baseExpr.streamInts(value -> {
+        if (removeValue==value) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamInts(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntRemoveFunction extends AbstractIntValue {
+  private final IntValue baseExpr;
+  private final IntValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntRemoveFunction(IntValue baseExpr, IntValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+  
+  @Override
+  public int getInt() {
+    int value = baseExpr.getInt();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value==removeExpr.getInt() ? removeExpr.exists()? false : true : true;
+    }
+    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 LongStreamRemoveFunction extends AbstractLongValueStream {
+  private final LongValueStream baseExpr;
+  private final LongValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongStreamRemoveFunction(LongValueStream baseExpr, LongValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    long removeValue = removeExpr.getLong();
+    if (removeExpr.exists()) {
+      baseExpr.streamLongs(value -> {
+        if (removeValue==value) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamLongs(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongRemoveFunction extends AbstractLongValue {
+  private final LongValue baseExpr;
+  private final LongValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongRemoveFunction(LongValue baseExpr, LongValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+  
+  @Override
+  public long getLong() {
+    long value = baseExpr.getLong();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value==removeExpr.getLong() ? removeExpr.exists()? false : true : true;
+    }
+    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 FloatStreamRemoveFunction extends AbstractFloatValueStream {
+  private final FloatValueStream baseExpr;
+  private final FloatValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatStreamRemoveFunction(FloatValueStream baseExpr, FloatValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    float removeValue = removeExpr.getFloat();
+    if (removeExpr.exists()) {
+      baseExpr.streamFloats(value -> {
+        if (removeValue==value) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamFloats(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatRemoveFunction extends AbstractFloatValue {
+  private final FloatValue baseExpr;
+  private final FloatValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatRemoveFunction(FloatValue baseExpr, FloatValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+  
+  @Override
+  public float getFloat() {
+    float value = baseExpr.getFloat();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value==removeExpr.getFloat() ? removeExpr.exists()? false : true : true;
+    }
+    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 DoubleStreamRemoveFunction extends AbstractDoubleValueStream {
+  private final DoubleValueStream baseExpr;
+  private final DoubleValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DoubleStreamRemoveFunction(DoubleValueStream baseExpr, DoubleValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    double removeValue = removeExpr.getDouble();
+    if (removeExpr.exists()) {
+      baseExpr.streamDoubles(value -> {
+        if (removeValue==value) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamDoubles(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DoubleRemoveFunction extends AbstractDoubleValue {
+  private final DoubleValue baseExpr;
+  private final DoubleValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DoubleRemoveFunction(DoubleValue baseExpr, DoubleValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+  
+  @Override
+  public double getDouble() {
+    double value = baseExpr.getDouble();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value==removeExpr.getDouble() ? removeExpr.exists()? false : true : true;
+    }
+    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 DateStreamRemoveFunction extends AbstractDateValueStream {
+  private final DateValueStream baseExpr;
+  private final DateValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateStreamRemoveFunction(DateValueStream baseExpr, DateValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    long removeValue = removeExpr.getLong();
+    if (removeExpr.exists()) {
+      baseExpr.streamLongs(value -> {
+        if (removeValue==value) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamLongs(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DateRemoveFunction extends AbstractDateValue {
+  private final DateValue baseExpr;
+  private final DateValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateRemoveFunction(DateValue baseExpr, DateValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public long getLong() {
+    long value = baseExpr.getLong();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value==removeExpr.getLong() ? removeExpr.exists() ? false : true : true;
+    }
+    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 StringStreamRemoveFunction extends AbstractStringValueStream {
+  private final StringValueStream baseExpr;
+  private final StringValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StringStreamRemoveFunction(StringValueStream baseExpr, StringValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    String removeValue = removeExpr.toString();
+    if (removeExpr.exists()) {
+      baseExpr.streamStrings(value -> {
+        if (removeValue.equals(value)) cons.accept(value);
+      });
+    } else {
+      baseExpr.streamStrings(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringRemoveFunction extends AbstractStringValue {
+  private final StringValue baseExpr;
+  private final StringValue removeExpr;
+  public static final String name = RemoveFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StringRemoveFunction(StringValue baseExpr, StringValue removeExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.removeExpr = removeExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,removeExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,removeExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public String getString() {
+    String value = baseExpr.getString();
+    exists = false;
+    if (baseExpr.exists()) {
+      exists = value.equals(removeExpr.getString()) ? removeExpr.exists()? false : true : true;
+    }
+    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;
+  }
+}
\ 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/ReplaceFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ReplaceFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ReplaceFunction.java
new file mode 100644
index 0000000..0d9e278
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/mapping/ReplaceFunction.java
@@ -0,0 +1,914 @@
+/*
+ * 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.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+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.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.analytics.value.AnalyticsValue.AbstractAnalyticsValue;
+import org.apache.solr.analytics.value.BooleanValue.AbstractBooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream.AbstractBooleanValueStream;
+import org.apache.solr.analytics.value.DateValue.AbstractDateValue;
+import org.apache.solr.analytics.value.DateValueStream.AbstractDateValueStream;
+import org.apache.solr.analytics.value.DoubleValue.AbstractDoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream.AbstractDoubleValueStream;
+import org.apache.solr.analytics.value.FloatValue.AbstractFloatValue;
+import org.apache.solr.analytics.value.FloatValueStream.AbstractFloatValueStream;
+import org.apache.solr.analytics.value.IntValue.AbstractIntValue;
+import org.apache.solr.analytics.value.IntValueStream.AbstractIntValueStream;
+import org.apache.solr.analytics.value.LongValue.AbstractLongValue;
+import org.apache.solr.analytics.value.LongValueStream.AbstractLongValueStream;
+import org.apache.solr.analytics.value.StringValue.AbstractStringValue;
+import org.apache.solr.analytics.value.StringValueStream.AbstractStringValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A mapping function to replace an {@link AnalyticsValue} from an {@link AnalyticsValue} or an {@link AnalyticsValueStream}
+ * with a different {@link AnalyticsValue}.
+ * For each document, all values from the base parameter matching the comparison parameter will be replaced with the fill parameter.
+ * <p>
+ * The first parameter can be any type of analytics expression. If the parameter is multi-valued, then the return will be multi-valued. (Required)
+ * <br>
+ * The second parameter, which is the value to compare to the first parameter, must be an {@link AnalyticsValue}, aka single-valued. (Required)
+ * <br>
+ * The third parameter, which is the value to fill the first parameter with, must be an {@link AnalyticsValue}, aka single-valued. (Required)
+ * <p>
+ * The resulting Value or ValueStream will be typed with the closest super-type of the three parameters.
+ * (e.g. {@value #name}(double,int,float) will return a double)
+ */
+public class ReplaceFunction {
+  public static final String name = "replace";
+
+  public static final CreatorFunction creatorFunction = (params -> {
+    if (params.length != 3) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 3 paramaters, " + params.length + " found.");
+    }
+    if (!(params[1] instanceof AnalyticsValue && params[2] instanceof AnalyticsValue)) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires the comparator and fill parameters to be single-valued.");
+    }
+
+    AnalyticsValueStream baseExpr = params[0];
+    AnalyticsValue compExpr = (AnalyticsValue)params[1];
+    AnalyticsValue fillExpr = (AnalyticsValue)params[2];
+
+    if (baseExpr instanceof DateValue && compExpr instanceof DateValue && fillExpr instanceof DateValue) {
+      return new DateReplaceFunction((DateValue)baseExpr,(DateValue)compExpr,(DateValue)fillExpr);
+    }
+    if (baseExpr instanceof DateValueStream && compExpr instanceof DateValue && fillExpr instanceof DateValue) {
+      return new DateStreamReplaceFunction((DateValueStream)baseExpr,(DateValue)compExpr,(DateValue)fillExpr);
+    }
+    if (baseExpr instanceof BooleanValue && compExpr instanceof BooleanValue && fillExpr instanceof BooleanValue) {
+      return new BooleanReplaceFunction((BooleanValue)baseExpr,(BooleanValue)compExpr,(BooleanValue)fillExpr);
+    }
+    if (baseExpr instanceof BooleanValueStream && compExpr instanceof BooleanValue && fillExpr instanceof BooleanValue) {
+      return new BooleanStreamReplaceFunction((BooleanValueStream)baseExpr,(BooleanValue)compExpr,(BooleanValue)fillExpr);
+    }
+    if (baseExpr instanceof IntValue && compExpr instanceof IntValue && fillExpr instanceof IntValue) {
+      return new IntReplaceFunction((IntValue)baseExpr,(IntValue)compExpr,(IntValue)fillExpr);
+    }
+    if (baseExpr instanceof IntValueStream && compExpr instanceof IntValue && fillExpr instanceof IntValue) {
+      return new IntStreamReplaceFunction((IntValueStream)baseExpr,(IntValue)compExpr,(IntValue)fillExpr);
+    }
+    if (baseExpr instanceof LongValue && compExpr instanceof LongValue && fillExpr instanceof LongValue) {
+      return new LongReplaceFunction((LongValue)baseExpr,(LongValue)compExpr,(LongValue)fillExpr);
+    }
+    if (baseExpr instanceof LongValueStream && compExpr instanceof LongValue && fillExpr instanceof LongValue) {
+      return new LongStreamReplaceFunction((LongValueStream)baseExpr,(LongValue)compExpr,(LongValue)fillExpr);
+    }
+    if (baseExpr instanceof FloatValue && compExpr instanceof FloatValue && fillExpr instanceof FloatValue) {
+      return new FloatReplaceFunction((FloatValue)baseExpr,(FloatValue)compExpr,(FloatValue)fillExpr);
+    }
+    if (baseExpr instanceof FloatValueStream && compExpr instanceof FloatValue && fillExpr instanceof FloatValue) {
+      return new FloatStreamReplaceFunction((FloatValueStream)baseExpr,(FloatValue)compExpr,(FloatValue)fillExpr);
+    }
+    if (baseExpr instanceof DoubleValue && compExpr instanceof DoubleValue && fillExpr instanceof DoubleValue) {
+      return new DoubleReplaceFunction((DoubleValue)baseExpr,(DoubleValue)compExpr,(DoubleValue)fillExpr);
+    }
+    if (baseExpr instanceof DoubleValueStream && compExpr instanceof DoubleValue && fillExpr instanceof DoubleValue) {
+      return new DoubleStreamReplaceFunction((DoubleValueStream)baseExpr,(DoubleValue)compExpr,(DoubleValue)fillExpr);
+    }
+    if (baseExpr instanceof StringValue && compExpr instanceof StringValue && fillExpr instanceof StringValue) {
+      return new StringReplaceFunction((StringValue)baseExpr,(StringValue)compExpr,(StringValue)fillExpr);
+    }
+    if (baseExpr instanceof StringValueStream && compExpr instanceof StringValue && fillExpr instanceof StringValue) {
+      return new StringStreamReplaceFunction((StringValueStream)baseExpr,(StringValue)compExpr,(StringValue)fillExpr);
+    }
+    if (baseExpr instanceof AnalyticsValue) {
+      return new ValueReplaceFunction((AnalyticsValue)baseExpr,(AnalyticsValue)compExpr,(AnalyticsValue)fillExpr);
+    }
+    return new StreamReplaceFunction(baseExpr,compExpr,fillExpr); 
+    
+  });
+}
+class StreamReplaceFunction implements AnalyticsValueStream {
+  private final AnalyticsValueStream baseExpr;
+  private final AnalyticsValue compExpr;
+  private final AnalyticsValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StreamReplaceFunction(AnalyticsValueStream baseExpr, AnalyticsValue compExpr, AnalyticsValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    Object compValue = compExpr.getObject();
+    if (compExpr.exists()) {
+      Object fillValue = fillExpr.getObject();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamObjects(value -> {
+        if (value.equals(compValue)) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamObjects(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class ValueReplaceFunction extends AbstractAnalyticsValue {
+  private final AnalyticsValue baseExpr;
+  private final AnalyticsValue compExpr;
+  private final AnalyticsValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public ValueReplaceFunction(AnalyticsValue baseExpr, AnalyticsValue compExpr, AnalyticsValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public Object getObject() {
+    Object value = baseExpr.getObject();
+    exists = baseExpr.exists();
+    Object comp = compExpr.getObject();
+    if (value.equals(comp) && exists==compExpr.exists()) {
+      value = fillExpr.getObject();
+      exists = fillExpr.exists();
+    }
+    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 BooleanStreamReplaceFunction extends AbstractBooleanValueStream {
+  private final BooleanValueStream baseExpr;
+  private final BooleanValue compExpr;
+  private final BooleanValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanStreamReplaceFunction(BooleanValueStream baseExpr, BooleanValue compExpr, BooleanValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    boolean compValue = compExpr.getBoolean();
+    if (compExpr.exists()) {
+      boolean fillValue = fillExpr.getBoolean();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamBooleans(value -> {
+        if (value==compValue) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamBooleans(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class BooleanReplaceFunction extends AbstractBooleanValue {
+  private final BooleanValue baseExpr;
+  private final BooleanValue compExpr;
+  private final BooleanValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public BooleanReplaceFunction(BooleanValue baseExpr, BooleanValue compExpr, BooleanValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public boolean getBoolean() {
+    boolean value = baseExpr.getBoolean();
+    exists = baseExpr.exists();
+    boolean comp = compExpr.getBoolean();
+    if (value==comp && exists==compExpr.exists()) {
+      value = fillExpr.getBoolean();
+      exists = fillExpr.exists();
+    }
+    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 IntStreamReplaceFunction extends AbstractIntValueStream {
+  private final IntValueStream baseExpr;
+  private final IntValue compExpr;
+  private final IntValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntStreamReplaceFunction(IntValueStream baseExpr, IntValue compExpr, IntValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamInts(IntConsumer cons) {
+    int compValue = compExpr.getInt();
+    if (compExpr.exists()) {
+      int fillValue = fillExpr.getInt();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamInts(value -> {
+        if (value==compValue) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamInts(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class IntReplaceFunction extends AbstractIntValue {
+  private final IntValue baseExpr;
+  private final IntValue compExpr;
+  private final IntValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public IntReplaceFunction(IntValue baseExpr, IntValue compExpr, IntValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public int getInt() {
+    int value = baseExpr.getInt();
+    exists = baseExpr.exists();
+    int comp = compExpr.getInt();
+    if (value==comp && exists==compExpr.exists()) {
+      value = fillExpr.getInt();
+      exists = fillExpr.exists();
+    }
+    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 LongStreamReplaceFunction extends AbstractLongValueStream {
+  private final LongValueStream baseExpr;
+  private final LongValue compExpr;
+  private final LongValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongStreamReplaceFunction(LongValueStream baseExpr, LongValue compExpr, LongValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    long compValue = compExpr.getLong();
+    if (compExpr.exists()) {
+      long fillValue = fillExpr.getLong();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamLongs(value -> {
+        if (value==compValue) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamLongs(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class LongReplaceFunction extends AbstractLongValue {
+  private final LongValue baseExpr;
+  private final LongValue compExpr;
+  private final LongValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public LongReplaceFunction(LongValue baseExpr, LongValue compExpr, LongValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public long getLong() {
+    long value = baseExpr.getLong();
+    exists = baseExpr.exists();
+    long comp = compExpr.getLong();
+    if (value==comp && exists==compExpr.exists()) {
+      value = fillExpr.getLong();
+      exists = fillExpr.exists();
+    }
+    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 FloatStreamReplaceFunction extends AbstractFloatValueStream {
+  private final FloatValueStream baseExpr;
+  private final FloatValue compExpr;
+  private final FloatValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatStreamReplaceFunction(FloatValueStream baseExpr, FloatValue compExpr, FloatValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    float compValue = compExpr.getFloat();
+    if (compExpr.exists()) {
+      float fillValue = fillExpr.getFloat();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamFloats(value -> {
+        if (value==compValue) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamFloats(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class FloatReplaceFunction extends AbstractFloatValue {
+  private final FloatValue baseExpr;
+  private final FloatValue compExpr;
+  private final FloatValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public FloatReplaceFunction(FloatValue baseExpr, FloatValue compExpr, FloatValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public float getFloat() {
+    float value = baseExpr.getFloat();
+    exists = baseExpr.exists();
+    float comp = compExpr.getFloat();
+    if (value==comp && exists==compExpr.exists()) {
+      value = fillExpr.getFloat();
+      exists = fillExpr.exists();
+    }
+    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 DoubleStreamReplaceFunction extends AbstractDoubleValueStream {
+  private final DoubleValueStream baseExpr;
+  private final DoubleValue compExpr;
+  private final DoubleValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DoubleStreamReplaceFunction(DoubleValueStream baseExpr, DoubleValue compExpr, DoubleValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    double compValue = compExpr.getDouble();
+    if (compExpr.exists()) {
+      double fillValue = fillExpr.getDouble();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamDoubles(value -> {
+        if (value==compValue) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamDoubles(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DoubleReplaceFunction extends AbstractDoubleValue {
+  private final DoubleValue baseExpr;
+  private final DoubleValue compExpr;
+  private final DoubleValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DoubleReplaceFunction(DoubleValue baseExpr, DoubleValue compExpr, DoubleValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public double getDouble() {
+    double value = baseExpr.getDouble();
+    exists = baseExpr.exists();
+    double comp = compExpr.getDouble();
+    if (value==comp && exists==compExpr.exists()) {
+      value = fillExpr.getDouble();
+      exists = fillExpr.exists();
+    }
+    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 DateStreamReplaceFunction extends AbstractDateValueStream {
+  private final DateValueStream baseExpr;
+  private final DateValue compExpr;
+  private final DateValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateStreamReplaceFunction(DateValueStream baseExpr, DateValue compExpr, DateValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    long compValue = compExpr.getLong();
+    if (compExpr.exists()) {
+      long fillValue = fillExpr.getLong();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamLongs(value -> {
+        if (value==compValue) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamLongs(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class DateReplaceFunction extends AbstractDateValue {
+  private final DateValue baseExpr;
+  private final DateValue compExpr;
+  private final DateValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public DateReplaceFunction(DateValue baseExpr, DateValue compExpr, DateValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public long getLong() {
+    long value = baseExpr.getLong();
+    exists = baseExpr.exists();
+    long comp = compExpr.getLong();
+    if (value==comp && exists==compExpr.exists()) {
+      value = fillExpr.getLong();
+      exists = fillExpr.exists();
+    }
+    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 StringStreamReplaceFunction extends AbstractStringValueStream {
+  private final StringValueStream baseExpr;
+  private final StringValue compExpr;
+  private final StringValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StringStreamReplaceFunction(StringValueStream baseExpr, StringValue compExpr, StringValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    String compValue = compExpr.toString();
+    if (compExpr.exists()) {
+      String fillValue = fillExpr.toString();
+      boolean fillExists = fillExpr.exists();
+      baseExpr.streamStrings(value -> {
+        if (value.equals(compValue)) {
+          if (fillExists) {
+            cons.accept(fillValue);
+          }
+        } else {
+          cons.accept(value);
+        }
+      });
+    }
+    else {
+      baseExpr.streamStrings(cons);
+    }
+  }
+  
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return exprStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+class StringReplaceFunction extends AbstractStringValue {
+  private final StringValue baseExpr;
+  private final StringValue compExpr;
+  private final StringValue fillExpr;
+  public static final String name = ReplaceFunction.name;
+  private final String exprStr;
+  private final ExpressionType funcType;
+  
+  public StringReplaceFunction(StringValue baseExpr, StringValue compExpr, StringValue fillExpr) throws SolrException {
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.fillExpr = fillExpr;
+    this.exprStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr,fillExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(exprStr,baseExpr,compExpr,fillExpr);
+  }
+  
+  boolean exists = false;
+
+  @Override
+  public String getString() {
+    String value = baseExpr.getString();
+    exists = baseExpr.exists();
+    String comp = compExpr.getString();
+    if (value.equals(comp) && exists==compExpr.exists()) {
+      value = fillExpr.getString();
+      exists = fillExpr.exists();
+    }
+    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;
+  }
+}
\ No newline at end of file