You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by da...@apache.org on 2018/11/02 11:16:43 UTC

[34/56] lucene-solr:jira/gradle: Add :solr:contrib:analytics module

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/DoubleField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/DoubleField.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/DoubleField.java
new file mode 100644
index 0000000..fae26e3
--- /dev/null
+++ b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/DoubleField.java
@@ -0,0 +1,96 @@
+/*
+ * 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 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.DoubleValue.CastingDoubleValue;
+import org.apache.solr.schema.DoublePointField;
+import org.apache.solr.schema.TrieDoubleField;
+
+/**
+ * An analytics wrapper for a single-valued {@link TrieDoubleField} or {@link DoublePointField} with DocValues enabled.
+ */
+public class DoubleField extends AnalyticsField implements CastingDoubleValue {
+  private NumericDocValues docValues;
+  private double value;
+  private boolean exists;
+
+  public DoubleField(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 = Double.longBitsToDouble(docValues.longValue());
+    }
+  }
+
+  @Override
+  public double getDouble() {
+    return value;
+  }
+  @Override
+  public String getString() {
+    return exists ? Double.toString(value) : null;
+  }
+  @Override
+  public Object getObject() {
+    return exists ? value : null;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    if (exists) {
+      cons.accept(Double.toString(value));
+    }
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+
+  @Override
+  public ExpressionComparator<Double> getObjectComparator(String expression) {
+    return new ExpressionComparator<>(expression);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/DoubleMultiPointField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/DoubleMultiPointField.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/DoubleMultiPointField.java
new file mode 100644
index 0000000..0933f60
--- /dev/null
+++ b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/DoubleMultiPointField.java
@@ -0,0 +1,81 @@
+/*
+ * 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 org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedNumericDocValues;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.solr.analytics.value.DoubleValueStream.CastingDoubleValueStream;
+import org.apache.solr.schema.DoublePointField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link DoublePointField} with DocValues enabled.
+ */
+public class DoubleMultiPointField extends AnalyticsField implements CastingDoubleValueStream {
+  private SortedNumericDocValues docValues;
+  private int count;
+  private double[] values;
+
+  public DoubleMultiPointField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new double[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] = NumericUtils.sortableLongToDouble(docValues.nextValue());
+      }
+    } else {
+      count = 0;
+    }
+  }
+  
+  private void resizeEmptyValues(int count) {
+    if (count > values.length) {
+      values = new double[count];
+    }
+  }
+  
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamDoubles(value -> cons.accept(Double.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamDoubles(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/DoubleMultiTrieField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/DoubleMultiTrieField.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/DoubleMultiTrieField.java
new file mode 100644
index 0000000..d6ae469
--- /dev/null
+++ b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/DoubleMultiTrieField.java
@@ -0,0 +1,87 @@
+/*
+ * 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 org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.solr.analytics.value.DoubleValueStream.CastingDoubleValueStream;
+import org.apache.solr.legacy.LegacyNumericUtils;
+import org.apache.solr.schema.TrieDoubleField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link TrieDoubleField} with DocValues enabled.
+ * @deprecated Trie fields are deprecated as of Solr 7.0
+ */
+@Deprecated
+public class DoubleMultiTrieField extends AnalyticsField implements CastingDoubleValueStream {
+  private SortedSetDocValues docValues;
+  private int count;
+  private double[] values;
+
+  public DoubleMultiTrieField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new double[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++] = NumericUtils.sortableLongToDouble(LegacyNumericUtils.prefixCodedToLong(docValues.lookupOrd(term)));
+      }
+    }
+  }
+  
+  private void resizeValues() {
+    double[] newValues = new double[values.length*2];
+    for (int i = 0; i < count; ++i) {
+      newValues[i] = values[i];
+    }
+    values = newValues;
+  }
+  
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamDoubles(value -> cons.accept(Double.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamDoubles(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/FloatField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/FloatField.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/FloatField.java
new file mode 100644
index 0000000..82e82958
--- /dev/null
+++ b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/FloatField.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 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.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.FloatValue.CastingFloatValue;
+import org.apache.solr.schema.FloatPointField;
+import org.apache.solr.schema.TrieFloatField;
+
+/**
+ * An analytics wrapper for a single-valued {@link TrieFloatField} or {@link FloatPointField} with DocValues enabled.
+ */
+public class FloatField extends AnalyticsField implements CastingFloatValue {
+  private NumericDocValues docValues;
+  private float value;
+  private boolean exists;
+
+  public FloatField(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 = Float.intBitsToFloat((int)docValues.longValue());
+    }
+  }
+
+  @Override
+  public float getFloat() {
+    return value;
+  }
+  @Override
+  public double getDouble() {
+    return (double)value;
+  }
+  @Override
+  public String getString() {
+    return exists ? Float.toString(value) : null;
+  }
+  @Override
+  public Object getObject() {
+    return exists ? value : null;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public void streamFloats(FloatConsumer 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(Float.toString(value));
+    }
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+
+  @Override
+  public ExpressionComparator<Float> getObjectComparator(String expression) {
+    return new ExpressionComparator<>(expression);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/FloatMultiPointField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/FloatMultiPointField.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/FloatMultiPointField.java
new file mode 100644
index 0000000..947035e
--- /dev/null
+++ b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/FloatMultiPointField.java
@@ -0,0 +1,87 @@
+/*
+ * 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 org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedNumericDocValues;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.FloatValueStream.CastingFloatValueStream;
+import org.apache.solr.schema.FloatPointField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link FloatPointField} with DocValues enabled.
+ */
+public class FloatMultiPointField extends AnalyticsField implements CastingFloatValueStream {
+  private SortedNumericDocValues docValues;
+  private int count;
+  private float[] values;
+
+  public FloatMultiPointField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new float[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] = NumericUtils.sortableIntToFloat((int)docValues.nextValue());
+      }
+    } else {
+      count = 0;
+    }
+  }
+  
+  private void resizeEmptyValues(int count) {
+    if (count > values.length) {
+      values = new float[count];
+    }
+  }
+  
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    streamFloats(value -> cons.accept((double)value));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamFloats(value -> cons.accept(Float.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamFloats(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/FloatMultiTrieField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/FloatMultiTrieField.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/FloatMultiTrieField.java
new file mode 100644
index 0000000..21b612c
--- /dev/null
+++ b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/FloatMultiTrieField.java
@@ -0,0 +1,93 @@
+/*
+ * 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 org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.solr.analytics.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.FloatValueStream.CastingFloatValueStream;
+import org.apache.solr.legacy.LegacyNumericUtils;
+import org.apache.solr.schema.TrieFloatField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link TrieFloatField} with DocValues enabled.
+ * @deprecated Trie fields are deprecated as of Solr 7.0
+ */
+@Deprecated
+public class FloatMultiTrieField extends AnalyticsField implements CastingFloatValueStream {
+  private SortedSetDocValues docValues;
+  private int count;
+  private float[] values;
+
+  public FloatMultiTrieField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new float[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++] = NumericUtils.sortableIntToFloat(LegacyNumericUtils.prefixCodedToInt(docValues.lookupOrd(term)));
+      }
+    }
+  }
+  
+  private void resizeValues() {
+    float[] newValues = new float[values.length*2];
+    for (int i = 0; i < count; ++i) {
+      newValues[i] = values[i];
+    }
+    values = newValues;
+  }
+  
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    streamFloats(value -> cons.accept((double)value));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamFloats(value -> cons.accept(Float.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamFloats(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/IntField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/IntField.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/IntField.java
new file mode 100644
index 0000000..e9ae52b
--- /dev/null
+++ b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/IntField.java
@@ -0,0 +1,129 @@
+/*
+ * 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.IntConsumer;
+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.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.IntValue.CastingIntValue;
+import org.apache.solr.schema.IntPointField;
+import org.apache.solr.schema.TrieIntField;
+
+/**
+ * An analytics wrapper for a single-valued {@link TrieIntField} or {@link IntPointField} with DocValues enabled.
+ */
+public class IntField extends AnalyticsField implements CastingIntValue {
+  private NumericDocValues docValues;
+  private int value;
+  private boolean exists;
+  
+  public IntField(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 = (int)docValues.longValue();
+    }
+  }
+
+  @Override
+  public int getInt() {
+    return value;
+  }
+  @Override
+  public long getLong() {
+    return (long)value;
+  }
+  @Override
+  public float getFloat() {
+    return (float)value;
+  }
+  @Override
+  public double getDouble() {
+    return (double)value;
+  }
+  @Override
+  public String getString() {
+    return exists ? Integer.toString(value) : null;
+  }
+  @Override
+  public Object getObject() {
+    return exists ? value : null;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public void streamInts(IntConsumer cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    if (exists) {
+      cons.accept((long)value);
+    }
+  }
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    if (exists) {
+      cons.accept((float)value);
+    }
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    if (exists) {
+      cons.accept((double)value);
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    if (exists) {
+      cons.accept(Integer.toString(value));
+    }
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+
+  @Override
+  public ExpressionComparator<Integer> getObjectComparator(String expression) {
+    return new ExpressionComparator<>(expression);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/IntMultiPointField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/IntMultiPointField.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/IntMultiPointField.java
new file mode 100644
index 0000000..2608fa1
--- /dev/null
+++ b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/IntMultiPointField.java
@@ -0,0 +1,96 @@
+/*
+ * 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.IntConsumer;
+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.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.IntValueStream.CastingIntValueStream;
+import org.apache.solr.schema.IntPointField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link IntPointField} with DocValues enabled.
+ */
+public class IntMultiPointField extends AnalyticsField implements CastingIntValueStream {
+  private SortedNumericDocValues docValues;
+  private int count;
+  private int[] values;
+
+  public IntMultiPointField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new int[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] = (int)docValues.nextValue();
+      }
+    } else {
+      count = 0;
+    }
+  }
+  
+  private void resizeEmptyValues(int count) {
+    if (count > values.length) {
+      values = new int[count];
+    }
+  }
+  
+  @Override
+  public void streamInts(IntConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    streamInts(value -> cons.accept((long)value));
+  }
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    streamInts(value -> cons.accept((float)value));
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    streamInts(value -> cons.accept((double)value));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamInts(value -> cons.accept(Integer.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamInts(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/IntMultiTrieField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/IntMultiTrieField.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/IntMultiTrieField.java
new file mode 100644
index 0000000..699b01d
--- /dev/null
+++ b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/IntMultiTrieField.java
@@ -0,0 +1,102 @@
+/*
+ * 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.IntConsumer;
+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.util.function.FloatConsumer;
+import org.apache.solr.analytics.value.IntValueStream.CastingIntValueStream;
+import org.apache.solr.legacy.LegacyNumericUtils;
+import org.apache.solr.schema.TrieIntField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link TrieIntField} with DocValues enabled.
+ * @deprecated Trie fields are deprecated as of Solr 7.0
+ */
+@Deprecated
+public class IntMultiTrieField extends AnalyticsField implements CastingIntValueStream {
+  private SortedSetDocValues docValues;
+  private int count;
+  private int[] values;
+
+  public IntMultiTrieField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new int[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.prefixCodedToInt(docValues.lookupOrd(term));
+      }
+    }
+  }
+  
+  private void resizeValues() {
+    int[] newValues = new int[values.length*2];
+    for (int i = 0; i < count; ++i) {
+      newValues[i] = values[i];
+    }
+    values = newValues;
+  }
+  
+  @Override
+  public void streamInts(IntConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamLongs(LongConsumer cons) {
+    streamInts(value -> cons.accept((long)value));
+  }
+  @Override
+  public void streamFloats(FloatConsumer cons) {
+    streamInts(value -> cons.accept((float)value));
+  }
+  @Override
+  public void streamDoubles(DoubleConsumer cons) {
+    streamInts(value -> cons.accept((double)value));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamInts(value -> cons.accept(Integer.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamInts(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/LongField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/LongField.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/LongField.java
new file mode 100644
index 0000000..ac4da2e
--- /dev/null
+++ b/solr/contrib/analytics/src/main/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/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/LongMultiPointField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/LongMultiPointField.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/LongMultiPointField.java
new file mode 100644
index 0000000..31d14ae
--- /dev/null
+++ b/solr/contrib/analytics/src/main/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/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/LongMultiTrieField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/LongMultiTrieField.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/LongMultiTrieField.java
new file mode 100644
index 0000000..dfeca79
--- /dev/null
+++ b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/LongMultiTrieField.java
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ * @deprecated Trie fields are deprecated as of Solr 7.0
+ */
+@Deprecated
+public class LongMultiTrieField extends AnalyticsField implements CastingLongValueStream {
+  private SortedSetDocValues docValues;
+  private int count;
+  private long[] values;
+
+  public LongMultiTrieField(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/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/StringField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/StringField.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/StringField.java
new file mode 100644
index 0000000..207a95a
--- /dev/null
+++ b/solr/contrib/analytics/src/main/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/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/StringMultiField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/StringMultiField.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/StringMultiField.java
new file mode 100644
index 0000000..39c60f0
--- /dev/null
+++ b/solr/contrib/analytics/src/main/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/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/package-info.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/field/package-info.java
new file mode 100644
index 0000000..a0e5421
--- /dev/null
+++ b/solr/contrib/analytics/src/main/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/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/mapping/AbsoluteValueFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/mapping/AbsoluteValueFunction.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/mapping/AbsoluteValueFunction.java
new file mode 100644
index 0000000..d52810f
--- /dev/null
+++ b/solr/contrib/analytics/src/main/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/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/mapping/AddFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/mapping/AddFunction.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/mapping/AddFunction.java
new file mode 100644
index 0000000..d66f84e
--- /dev/null
+++ b/solr/contrib/analytics/src/main/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/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/mapping/BottomFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/mapping/BottomFunction.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/mapping/BottomFunction.java
new file mode 100644
index 0000000..10674fd
--- /dev/null
+++ b/solr/contrib/analytics/src/main/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/f0366b94/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/mapping/ComparisonFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/mapping/ComparisonFunction.java b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/mapping/ComparisonFunction.java
new file mode 100644
index 0000000..1bbf9ae
--- /dev/null
+++ b/solr/contrib/analytics/src/main/java/org/apache/solr/analytics/function/mapping/ComparisonFunction.java
@@ -0,0 +1,318 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.analytics.function.mapping;
+
+import org.apache.solr.analytics.ExpressionFactory.CreatorFunction;
+import org.apache.solr.analytics.function.mapping.ComparisonFunction.CompResultFunction;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.value.AnalyticsValue;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+import org.apache.solr.analytics.value.BooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream;
+import org.apache.solr.analytics.value.DateValue;
+import org.apache.solr.analytics.value.DateValueStream;
+import org.apache.solr.analytics.value.DoubleValue;
+import org.apache.solr.analytics.value.DoubleValueStream;
+import org.apache.solr.analytics.value.BooleanValue.AbstractBooleanValue;
+import org.apache.solr.analytics.value.BooleanValueStream.AbstractBooleanValueStream;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * Contains all comparable functions. Comparable functions accept two comparable (numeric and date) parameters and return a BooleanValueStream.
+ * The two parameters must be able to be cast to the same type.
+ * <p>
+ * Uses:
+ * <ul>
+ * <li>If a two comparable {@link AnalyticsValue}s are passed in, a {@link BooleanValue} representing the comparison of the two values for each document is returned.
+ * <li>If a comparable {@link AnalyticsValue} and a comparable {@link AnalyticsValueStream} are passed in, 
+ * a {@link BooleanValueStream} representing the comparison of the Value and each of the values of the ValueStream for the document is returned.
+ * </ul>
+ */
+public class ComparisonFunction {
+  
+  /**
+   * Create a comparison mapping function, comparing two analytics value (streams) of the same type.
+   * 
+   * @param name name of the function
+   * @param comp function to find the result of a comparison
+   * @param params the parameters to compare
+   * @return an instance of the requested comparison function using the given parameters
+   */
+  public static BooleanValueStream createComparisonFunction(String name, CompResultFunction comp, AnalyticsValueStream... params) {
+    if (params.length != 2) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires 2 paramaters, " + params.length + " found.");
+    }
+    AnalyticsValueStream paramA = params[0];
+    AnalyticsValueStream paramB = params[1];
+    if (paramA instanceof DoubleValueStream && paramB instanceof DoubleValueStream) {
+      if (paramA instanceof DoubleValue) {
+        if (paramB instanceof DoubleValue) {
+          return new CompareDoubleValueFunction(name,(DoubleValue)paramA,(DoubleValue)paramB,comp);
+        }
+        return new CompareDoubleStreamFunction(name,(DoubleValue)paramA,(DoubleValueStream)paramB,comp);
+      }
+      if (paramB instanceof DoubleValue) {
+        return new CompareDoubleStreamFunction(name,(DoubleValue)paramB,(DoubleValueStream)paramA,reverse(comp));
+      }
+    } else if (paramA instanceof DateValueStream && paramB instanceof DateValueStream) {
+      if (paramA instanceof DateValue) {
+        if (paramB instanceof DateValue) {
+          return new CompareDateValueFunction(name,(DateValue)paramA,(DateValue)paramB,comp);
+        }
+        return new CompareDateStreamFunction(name,(DateValue)paramA,(DateValueStream)paramB,comp);
+      }
+      if (paramB instanceof DateValue) {
+        return new CompareDateStreamFunction(name,(DateValue)paramB,(DateValueStream)paramA,reverse(comp));
+      }
+    } else {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires comparable (numeric or date) parameters.");
+    }
+    throw new SolrException(ErrorCode.BAD_REQUEST,"The "+name+" function requires that at least 1 parameter be single-valued.");
+  }
+  
+  /**
+   * A comparison function that tests whether the first parameter is greater than the second parameter
+   */
+  public static class GTFunction {
+    public static final String name = "gt";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return ComparisonFunction.createComparisonFunction(name, val -> {
+        return val > 0;
+      }, params);
+    });
+  }
+
+  /**
+   * A comparison function that tests whether the first parameter is greater than or equal to the second parameter
+   */
+  public static class GTEFunction {
+    public static final String name = "gte";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return ComparisonFunction.createComparisonFunction(name, val -> {
+        return val >= 0;
+      }, params);
+    });
+  }
+
+  /**
+   * A comparison function that tests whether the first parameter is less than the second parameter
+   */
+  public static class LTFunction {
+    public static final String name = "lt";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return ComparisonFunction.createComparisonFunction(name, val -> {
+        return val < 0;
+      }, params);
+    });
+  }
+
+  /**
+   * A comparison function that tests whether the first parameter is less than or equal to the second parameter
+   */
+  public static class LTEFunction {
+    public static final String name = "lte";
+    public static final CreatorFunction creatorFunction = (params -> {
+      return ComparisonFunction.createComparisonFunction(name, val -> {
+        return val <= 0;
+      }, params);
+    });
+  }
+
+  @FunctionalInterface
+  public static interface CompResultFunction {
+    public boolean apply(int compResult);
+  }
+  
+  private static CompResultFunction reverse(CompResultFunction original) {
+    return val -> original.apply(val*-1);
+  }
+}
+/**
+ * A comparison function for two {@link DoubleValue}s.
+ */
+class CompareDoubleValueFunction extends AbstractBooleanValue {
+  private final DoubleValue exprA;
+  private final DoubleValue exprB;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareDoubleValueFunction(String name, DoubleValue exprA, DoubleValue exprB, CompResultFunction comp) {
+    this.name = name;
+    this.exprA = exprA;
+    this.exprB = exprB;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,exprA,exprB);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,exprA,exprB);
+  }
+
+  private boolean exists = false;
+  @Override
+  public boolean getBoolean() {
+    double valueA = exprA.getDouble();
+    double valueB = exprB.getDouble();
+    exists = exprA.exists() && exprB.exists();
+    return exists ? comp.apply(Double.compare(valueA,valueB)) : false;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A comparison function for a {@link DoubleValue} and a {@link DoubleValueStream}.
+ */
+class CompareDoubleStreamFunction extends AbstractBooleanValueStream {
+  private final DoubleValue baseExpr;
+  private final DoubleValueStream compExpr;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareDoubleStreamFunction(String name, DoubleValue baseExpr, DoubleValueStream compExpr, CompResultFunction comp) throws SolrException {
+    this.name = name;
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,baseExpr,compExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    double baseValue = baseExpr.getDouble();
+    if (baseExpr.exists()) {
+      compExpr.streamDoubles(compValue -> cons.accept(comp.apply(Double.compare(baseValue,compValue))));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A comparison function for two {@link DateValue}s.
+ */
+class CompareDateValueFunction extends AbstractBooleanValue {
+  private final DateValue exprA;
+  private final DateValue exprB;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareDateValueFunction(String name, DateValue exprA, DateValue exprB, CompResultFunction comp) {
+    this.name = name;
+    this.exprA = exprA;
+    this.exprB = exprB;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,exprA,exprB);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,exprA,exprB);
+  }
+
+  private boolean exists = false;
+  @Override
+  public boolean getBoolean() {
+    long valueA = exprA.getLong();
+    long valueB = exprB.getLong();
+    exists = exprA.exists() && exprB.exists();
+    return exists ? comp.apply(Long.compare(valueA,valueB)) : false;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
+/**
+ * A comparison function for a {@link DateValue} and a {@link DateValueStream}.
+ */
+class CompareDateStreamFunction extends AbstractBooleanValueStream {
+  private final DateValue baseExpr;
+  private final DateValueStream compExpr;
+  private final CompResultFunction comp;
+  private final String name;
+  private final String funcStr;
+  private final ExpressionType funcType;
+  
+  public CompareDateStreamFunction(String name, DateValue baseExpr, DateValueStream compExpr, CompResultFunction comp) throws SolrException {
+    this.name = name;
+    this.baseExpr = baseExpr;
+    this.compExpr = compExpr;
+    this.comp = comp;
+    this.funcStr = AnalyticsValueStream.createExpressionString(name,baseExpr,compExpr);
+    this.funcType = AnalyticsValueStream.determineMappingPhase(funcStr,baseExpr,compExpr);
+  }
+
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    long baseValue = baseExpr.getLong();
+    if (baseExpr.exists()) {
+      compExpr.streamLongs(compValue -> cons.accept(comp.apply(Long.compare(baseValue,compValue))));
+    }
+  }
+
+  @Override
+  public String getName() {
+    return name;
+  }
+  @Override
+  public String getExpressionStr() {
+    return funcStr;
+  }
+  @Override
+  public ExpressionType getExpressionType() {
+    return funcType;
+  }
+}
\ No newline at end of file

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