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

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

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/ConstantComparator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/ConstantComparator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/ConstantComparator.java
new file mode 100644
index 0000000..ece09f8
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/ConstantComparator.java
@@ -0,0 +1,30 @@
+/*
+ * 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.facet.compare;
+
+import org.apache.solr.analytics.facet.SortableFacet.FacetBucket;
+
+/**
+ * A results comparator that compares constants.
+ */
+public class ConstantComparator extends FacetResultsComparator {
+
+  @Override
+  public int compare(FacetBucket b1, FacetBucket b2) {
+      return 0;
+  }
+}
\ 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/facet/compare/DelegatingComparator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/DelegatingComparator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/DelegatingComparator.java
new file mode 100644
index 0000000..200e68b
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/DelegatingComparator.java
@@ -0,0 +1,62 @@
+/*
+ * 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.facet.compare;
+
+import java.util.Collection;
+
+import org.apache.solr.analytics.facet.SortableFacet.FacetBucket;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+
+/**
+ * A comparator used to sort the facet-value buckets of facet, using the delegate comparator if two values are equal.
+ */
+public class DelegatingComparator extends FacetResultsComparator {
+  private final Iterable<FacetResultsComparator> comparators;
+  
+  /**
+   * Create a delegating results comparator. This comparator will in succession use the given comparators, continuing if the values are equal.
+   * Two buckets are considered equal if and only if all comparators find them equal
+   * 
+   * @param comparators the comparators to use in succession
+   */
+  private DelegatingComparator(Iterable<FacetResultsComparator> comparators) {
+    this.comparators = comparators;
+  }
+  
+  public static FacetResultsComparator joinComparators(Collection<FacetResultsComparator> comparators) throws SolrException {
+    if (comparators.size() == 0) {
+      throw new SolrException(ErrorCode.BAD_REQUEST,"A sort must have at least 1 comparator criteria.");
+    } else if (comparators.size() == 1) {
+      return comparators.iterator().next();
+    } else {
+      return new DelegatingComparator(comparators);
+    }
+  }
+
+  @Override
+  public int compare(FacetBucket b1, FacetBucket b2) {
+    int val = 0;
+    for (FacetResultsComparator comparator : comparators) {
+      val = comparator.compare(b1, b2);
+      if (val != 0) {
+        break;
+      }
+    }
+    return val;
+  }
+}
\ 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/facet/compare/ExpressionComparator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/ExpressionComparator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/ExpressionComparator.java
new file mode 100644
index 0000000..e4c1940
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/ExpressionComparator.java
@@ -0,0 +1,46 @@
+/*
+ * 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.facet.compare;
+
+import org.apache.solr.analytics.facet.SortableFacet.FacetBucket;
+
+/**
+ * A comparator used to sort the facet-value buckets of facet.
+ */
+public class ExpressionComparator<T extends Comparable<T>> extends FacetResultsComparator {
+  private final String expression;
+  
+  /**
+   * Create an entry comparator comparing the given expression.
+   * 
+   * @param expression the name of the expression results to compare
+   */
+  public ExpressionComparator(String expression) {
+    this.expression = expression;
+  }
+  
+  @SuppressWarnings("unchecked")
+  public int compare(FacetBucket b1, FacetBucket b2) {
+    T t1 = (T)b1.getResult(expression);
+    T t2 = (T)b2.getResult(expression);
+    if (t1 == null || t2 == null) {
+      return Boolean.compare(t2 == null, t1 == null) * resultMult;
+    } else {
+      return t1.compareTo(t2) * resultMult;
+    }
+  }
+}
\ 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/facet/compare/FacetResultsComparator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/FacetResultsComparator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/FacetResultsComparator.java
new file mode 100644
index 0000000..9303f21
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/FacetResultsComparator.java
@@ -0,0 +1,52 @@
+/*
+ * 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.facet.compare;
+
+import java.util.Comparator;
+
+import org.apache.solr.analytics.facet.SortableFacet.FacetBucket;
+
+/**
+ * A comparator used to sort the buckets of facet.
+ */
+public abstract class FacetResultsComparator implements Comparator<FacetBucket> {
+  protected int resultMult;
+  
+  /**
+   * Create a results comparator assuming an ascending ordering.
+   */
+  public FacetResultsComparator() {
+    setDirection(true);
+  }
+  
+  /**
+   * Set the order direction for comparison.
+   * 
+   * @param ascending whether to compare using an ascending ordering
+   */
+  public void setDirection(boolean ascending) {
+    this.resultMult = ascending ? 1 : -1;
+  }
+  
+  /**
+   * Compare one facet bucket to another.
+   * 
+   * @param b1 the first bucket to compare
+   * @param b2 the second bucket to compare
+   */
+  public abstract int compare(FacetBucket b1, FacetBucket b2);
+}
\ 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/facet/compare/FacetValueComparator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/FacetValueComparator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/FacetValueComparator.java
new file mode 100644
index 0000000..a07ac7b
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/FacetValueComparator.java
@@ -0,0 +1,37 @@
+/*
+ * 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.facet.compare;
+
+import org.apache.solr.analytics.facet.SortableFacet.FacetBucket;
+
+/**
+ * A results comparator that compares the name of facet value buckets, which is the string value of the facet value.
+ */
+public class FacetValueComparator extends FacetResultsComparator {
+  
+  /**
+   * Create a facet value comparator.
+   */
+  public FacetValueComparator() {
+    super();
+  }
+  
+  @Override
+  public int compare(FacetBucket b1, FacetBucket b2) {
+      return b1.getFacetValue().compareTo(b2.getFacetValue()) * resultMult;
+  }
+}
\ 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/facet/compare/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/package-info.java
new file mode 100644
index 0000000..c86ad17
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/compare/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.
+ */
+ 
+/** 
+ * Comparators used to sort the buckets of an analytics facet.
+ */
+package org.apache.solr.analytics.facet.compare;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/package-info.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/package-info.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/package-info.java
new file mode 100644
index 0000000..5812e54
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/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.
+ */
+ 
+/** 
+ * Facets supported by the analytics component.
+ */
+package org.apache.solr.analytics.facet;
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ExpressionCalculator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ExpressionCalculator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ExpressionCalculator.java
new file mode 100644
index 0000000..3c44555
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ExpressionCalculator.java
@@ -0,0 +1,71 @@
+/*
+ * 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;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.solr.analytics.AnalyticsExpression;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
+import org.apache.solr.common.util.NamedList;
+
+/**
+ * A class used to generate results for a list of {@link AnalyticsExpression}s.
+ */
+public class ExpressionCalculator {
+  private final Iterable<AnalyticsExpression> expressions;
+
+  public ExpressionCalculator(Iterable<AnalyticsExpression> expressions) {
+    this.expressions = expressions;
+  }
+
+  /**
+   * Calculate results for the list of {@link AnalyticsExpression}s.
+   * <p>
+   * NOTE: This method can, and is, called multiple times to generate different responses. 
+   * <br>
+   * The results are determined by which {@link ReductionDataCollection} is passed to the {@link ReductionCollectionManager#setData}
+   * method of the {@link ReductionCollectionManager} managing the reduction for the list of {@link AnalyticsExpression}s.
+   * 
+   * @return a {@link NamedList} containing the results
+   */
+  public Map<String,Object> getResults() {
+    Map<String,Object> exprVals = new HashMap<>();
+    expressions.forEach(expr -> {
+      Object obj = expr.toObject();
+      if (expr.exists()) {
+        exprVals.put(expr.getName(), obj);
+      }
+    });
+    return exprVals;
+  }
+
+  /**
+   * Calculate results for the list of {@link AnalyticsExpression}s and add them to the given response.
+   * <p>
+   * NOTE: This method can, and is, called multiple times to generate different responses. 
+   * <br>
+   * The results are determined by which {@link ReductionDataCollection} is passed to the {@link ReductionCollectionManager#setData}
+   * method of the {@link ReductionCollectionManager} managing the reduction for the list of {@link AnalyticsExpression}s.
+   * 
+   * @param response the response to add the results map to.
+   */
+  public void addResults(Map<String,Object> response) {
+    response.put(AnalyticsResponseHeadings.RESULTS, getResults());
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/MergingReductionCollectionManager.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/MergingReductionCollectionManager.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/MergingReductionCollectionManager.java
new file mode 100644
index 0000000..1402a76
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/MergingReductionCollectionManager.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+import org.apache.solr.analytics.function.field.AnalyticsField;
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+
+/**
+ * The {@link ReductionCollectionManager} used for distributed requests.
+ */
+public class MergingReductionCollectionManager extends ReductionCollectionManager {
+  
+  public MergingReductionCollectionManager() {
+    super();
+  }
+  
+  public MergingReductionCollectionManager(final ReductionDataCollector<?>[] reductionDataCollectors, final Iterable<AnalyticsField> fields) {
+    super(reductionDataCollectors, fields);
+  }
+  
+  @Override
+  protected ReductionCollectionManager createNewManager(final ReductionDataCollector<?>[] reductionDataCollectors, final Iterable<AnalyticsField> fields) {
+    return new MergingReductionCollectionManager(reductionDataCollectors,fields);
+  }
+  
+  @Override
+  public void setData(ReductionDataCollection dataCollection) {
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].setMergedData(dataCollection.dataArr[i]);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionCollectionManager.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionCollectionManager.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionCollectionManager.java
new file mode 100644
index 0000000..b3a178c
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionCollectionManager.java
@@ -0,0 +1,320 @@
+/*
+ * 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;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.solr.analytics.function.field.AnalyticsField;
+import org.apache.solr.analytics.function.reduction.data.ReductionData;
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.analytics.stream.reservation.ReductionDataReservation;
+import org.apache.solr.analytics.stream.reservation.read.ReductionDataReader;
+import org.apache.solr.analytics.stream.reservation.write.ReductionDataWriter;
+import org.apache.solr.analytics.value.AnalyticsValue;
+
+/**
+ * The manager of reduction collection.
+ * Contains a group of {@link ReductionDataCollector}s which will be updated together.
+ * <p>
+ * The manager assumes a non-distributed request. {@link MergingReductionCollectionManager} is used for distributed requests.
+ */
+public class ReductionCollectionManager {
+  protected final ReductionDataCollector<?>[] reductionDataCollectors;
+  private final List<ReductionDataReservation<?,?>> reservations;
+
+  private final List<ReductionDataReader<?>> readers;
+  private final List<ReductionDataWriter<?>> writers;
+  
+  private final Iterable<AnalyticsField> fields;
+  
+  public ReductionCollectionManager() {
+    this(new ReductionDataCollector<?>[0], new ArrayList<>(0));
+  }
+  
+  /**
+   * Create a Manager to oversee the given {@link ReductionDataCollector}s.
+   * 
+   * @param reductionDataCollectors array of collectors that are collecting over the same set of data
+   * @param fields all fields used by the given collectors
+   */
+  public ReductionCollectionManager(final ReductionDataCollector<?>[] reductionDataCollectors, final Iterable<AnalyticsField> fields) {
+    this.reductionDataCollectors = reductionDataCollectors;
+    Arrays.sort(reductionDataCollectors, (a,b) -> a.getExpressionStr().compareTo(b.getExpressionStr()));
+    
+    reservations = new LinkedList<>();
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].submitReservations(reservation -> reservations.add(reservation));
+    }
+    
+    this.fields = fields;
+    
+    this.readers = new ArrayList<>();
+    this.writers = new ArrayList<>();
+  }
+  
+  /**
+   * Return whether or not the manager needs collection done, which is false if no collectors are 
+   * being managed and true if at least one is.
+   * 
+   * @return true if at least one collector is being managed
+   */
+  public boolean needsCollection() {
+    return reductionDataCollectors.length > 0;
+  }
+  
+  /**
+   * Merge this collection manager with others.
+   *
+   * @param reductionManagers the collection managers to merge with
+   * @return a collection manager that manages the union of data collectors from this class and the given managers
+   */
+  public ReductionCollectionManager merge(Iterable<ReductionCollectionManager> reductionManagers) {
+    HashMap<String,ReductionDataCollector<?>> mergedCollectors = new HashMap<>();
+    HashMap<String,AnalyticsField> mergedFields = new HashMap<>();
+    
+    for (ReductionDataCollector<?> collector : reductionDataCollectors) {
+      mergedCollectors.put(collector.getExpressionStr(), collector);
+    }
+    fields.forEach( field -> mergedFields.put(field.getExpressionStr(), field) );
+    
+    reductionManagers.forEach( manager -> {
+      for (ReductionDataCollector<?> collector : manager.reductionDataCollectors) {
+        mergedCollectors.put(collector.getExpressionStr(), collector);
+      }
+      manager.fields.forEach( field -> mergedFields.put(field.getExpressionStr(), field) );
+    });
+    ReductionDataCollector<?>[] collectors = new ReductionDataCollector<?>[mergedCollectors.size()];
+    mergedCollectors.values().toArray(collectors);
+    return createNewManager(collectors, mergedFields.values());
+  }
+  
+  /**
+   * Create an {@link ReductionCollectionManager} to manage the given collectors and fields.
+   * 
+   * @param reductionDataCollectors Reduction collectors
+   * @param fields fields used by the reductions
+   * @return a collection manager
+   */
+  protected ReductionCollectionManager createNewManager(final ReductionDataCollector<?>[] reductionDataCollectors, final Iterable<AnalyticsField> fields) {
+    return new ReductionCollectionManager(reductionDataCollectors,fields);
+  }
+  
+  /**
+   * Get the {@link AnalyticsField}s used in the managed expressions.
+   * 
+   * @return the fields used
+   */
+  public Iterable<AnalyticsField> getUsedFields() {
+    return fields;
+  }
+  
+  /**
+   * Set the context of the readers of the used {@link AnalyticsField}s.
+   * 
+   * @param context the reader context
+   * @throws IOException if an error occurs while setting the fields' context
+   */
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    for (AnalyticsField field : fields) {
+      field.doSetNextReader(context);
+    }
+  }
+  
+  /**
+   * Collect the values from the used {@link AnalyticsField}s.
+   * 
+   * @param doc the document to collect values for
+   * @throws IOException if an error occurs during field collection
+   */
+  public void collect(int doc) throws IOException {
+    for (AnalyticsField field : fields) {
+      field.collect(doc);
+    }
+  }
+  
+  /**
+   * Add a {@link ReductionDataCollection} to target while collecting documents.
+   * This target is valid until the lasting targets are cleared.
+   * 
+   * @param target data collection to add document data too
+   */
+  public void addLastingCollectTarget(ReductionDataCollection target) {
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].addLastingCollectTarget(target.dataArr[i]);
+    }
+  }
+  /**
+   * Clear lasting collection targets.
+   */
+  public void clearLastingCollectTargets() {
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].clearLastingCollectTargets();
+    }
+  }
+  
+  /**
+   * Add a new {@link ReductionDataCollection} to target while collecting the next document.
+   * This target is only valid for the next {@link #apply()} call.
+   * 
+   * @return the new data collection being targeted
+   */
+  public ReductionDataCollection newDataCollectionTarget() {
+    ReductionDataCollection newCol = new ReductionDataCollection();
+    newCol.dataArr = new ReductionData[reductionDataCollectors.length];
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      newCol.dataArr[i] = reductionDataCollectors[i].newDataTarget();
+    }
+    return newCol;
+  }
+  /**
+   * Add a {@link ReductionDataCollection} to target while collecting the next document.
+   * This target is only valid for the next {@link #apply()} call.
+   * 
+   * @param target data collection to add document data too
+   */
+  public void addCollectTarget(ReductionDataCollection target) {
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].addCollectTarget(target.dataArr[i]);
+    }
+  }
+  
+  /**
+   * Apply the values of the collected fields through the expressions' logic to the managed data collectors.
+   * This is called after {@link #collect(int)} has been called and the collection targets have been added.
+   */
+  public void apply() {
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].collectAndApply();;
+    }
+  }
+  
+  /**
+   * Finalize the reductions with the collected data stored in the parameter.
+   * Once the data is finalized, the {@link ReductionFunction}s that use these 
+   * {@link ReductionDataCollector}s act like regular {@link AnalyticsValue} classes that 
+   * can be accessed through their {@code get<value-type>} methods.
+   * 
+   * @param dataCollection the collection of reduction data to compute results for
+   */
+  public void setData(ReductionDataCollection dataCollection) {
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].setData(dataCollection.dataArr[i]);
+    }
+  }
+  
+  /**
+   * Construct a new data collection holding data for all managed data collectors.
+   * 
+   * @return a new data collection
+   */
+  public ReductionDataCollection newDataCollection() {
+    ReductionDataCollection newCol = new ReductionDataCollection();
+    newCol.dataArr = new ReductionData[reductionDataCollectors.length];
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      newCol.dataArr[i] = reductionDataCollectors[i].newData();
+    }
+    return newCol;
+  }
+  
+  /**
+   * Sets the stream of shard data to merge with.
+   * 
+   * @param input the stream of shard data
+   */
+  public void setShardInput(DataInput input) {
+    readers.clear();
+    reservations.forEach( resv -> readers.add(resv.createReadStream(input)));
+  }
+  /**
+   * Merge the data from the given shard input stream into the set IO data collectors.
+   * Should always be called after {@link #setShardInput(DataInput)} and either {@link #prepareReductionDataIO(ReductionDataCollection)}
+   * or {@link #newDataCollectionIO()} have been called.
+   * 
+   * @throws IOException if an error occurs while reading the shard data
+   */
+  public void mergeData() throws IOException {
+    for (ReductionDataReader<?> reader : readers) {
+      reader.read();
+    }
+  }
+  
+  /**
+   * Sets the stream to export shard data to.
+   * 
+   * @param output the stream of shard data
+   */
+  public void setShardOutput(DataOutput output) {
+    writers.clear();
+    reservations.forEach( resv -> writers.add(resv.createWriteStream(output)));
+  }
+  /**
+   * Export the data from the set IO data collectors to the given shard output stream.
+   * Should always be called after {@link #setShardOutput(DataOutput)} and {@link #prepareReductionDataIO(ReductionDataCollection)}.
+   * 
+   * @throws IOException if an error occurs while writing the shard data
+   */
+  public void exportData() throws IOException {
+    for (ReductionDataWriter<?> writer : writers) {
+      writer.write();
+    }
+  }
+  
+  /**
+   * Set the given data collection to be used for either merging or exporting
+   *  
+   * @param col collection to export from or merge to
+   */
+  public void prepareReductionDataIO(ReductionDataCollection col) {
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      reductionDataCollectors[i].dataIO(col.dataArr[i]);
+    }
+  }
+  
+  /**
+   * Create a new {@link ReductionDataCollection} to merge to or export from.
+   * Mainly used for creating facet value collectors when merging shard data.
+   *  
+   * @return the new data collection created
+   */
+  public ReductionDataCollection newDataCollectionIO() {
+    ReductionDataCollection newCol = new ReductionDataCollection();
+    newCol.dataArr = new ReductionData[reductionDataCollectors.length];
+    for (int i = 0; i < reductionDataCollectors.length; i++) {
+      newCol.dataArr[i] = reductionDataCollectors[i].newDataIO();
+    }
+    return newCol;
+  }
+  
+  /**
+   * Holds the collection of {@link ReductionData} that will be updated together.
+   * 
+   * For example each grouping will have a separate {@link ReductionDataCollection}, and
+   * ungrouped expressions will have their own as well.
+   */
+  public static class ReductionDataCollection{
+    public ReductionData[] dataArr;
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionFunction.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionFunction.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionFunction.java
new file mode 100644
index 0000000..fea01c2
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/ReductionFunction.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+import java.util.function.UnaryOperator;
+
+import org.apache.solr.analytics.function.reduction.data.ReductionDataCollector;
+import org.apache.solr.analytics.value.AnalyticsValue;
+
+/**
+ * A function that reduces the values of a mapping expression, field or constant.
+ */
+public interface ReductionFunction extends AnalyticsValue {
+
+  /**
+   * Syncs the data collectors with shared versions across the entire Analytics Request
+   * so that as little data as possible is sent across shards.
+   * 
+   * @param sync a function that takes in a {@link ReductionDataCollector} and returns a shared version
+   */
+  void synchronizeDataCollectors(UnaryOperator<ReductionDataCollector<?>> sync);
+}
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/AnalyticsField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/AnalyticsField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/AnalyticsField.java
new file mode 100644
index 0000000..dab0358
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/AnalyticsField.java
@@ -0,0 +1,69 @@
+/*
+ * 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 org.apache.lucene.index.LeafReaderContext;
+import org.apache.solr.analytics.value.AnalyticsValueStream;
+
+/**
+ * An analytics wrapper for Solr Fields. 
+ * 
+ * Currently only fields with Doc Values enabled can be used in Analytics queries.
+ */
+public abstract class AnalyticsField implements AnalyticsValueStream {
+  protected static final int initialArrayLength = 20;
+  
+  protected final String fieldName;
+  
+  protected AnalyticsField(String fieldName) {
+    this.fieldName = fieldName;
+  }
+  
+  @Override
+  public String getExpressionStr() {
+    return fieldName;
+  }
+
+  @Override
+  public String getName() {
+    return fieldName;
+  }
+  
+  @Override
+  public ExpressionType getExpressionType() {
+    return ExpressionType.FIELD;
+  }
+  
+  /**
+   * Set the segment reader context
+   * 
+   * @param context segment context
+   * @throws IOException if an error occurs while loading the leaf reader
+   */
+  public abstract void doSetNextReader(LeafReaderContext context) throws IOException;
+  
+  /**
+   * Collect the value(s) of the wrapped field for the given document, and store the value.
+   * 
+   * @param doc ID of the document to collect
+   * @throws IOException if an error occurs while reading the document.
+   */
+  public abstract void collect(int doc) throws IOException;
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanField.java
new file mode 100644
index 0000000..650bc36
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanField.java
@@ -0,0 +1,111 @@
+/*
+ * 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.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedDocValues;
+import org.apache.lucene.util.BytesRef;
+import org.apache.solr.analytics.facet.compare.ExpressionComparator;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.value.BooleanValue.CastingBooleanValue;
+import org.apache.solr.schema.BoolField;
+
+/**
+ * An analytics wrapper for a single-valued {@link BoolField} with DocValues enabled.
+ */
+public class BooleanField extends AnalyticsField implements CastingBooleanValue {
+  private SortedDocValues docValues;
+  boolean value;
+  boolean exists;
+  int trueOrd;
+
+  public BooleanField(String fieldName) {
+    super(fieldName);
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSorted(context.reader(), fieldName);
+
+    // figure out what ord maps to true
+    int numOrds = docValues.getValueCount();
+    // if no values in the segment, default trueOrd to something other then -1 (missing)
+    int trueOrd = -2;
+    for (int i=0; i<numOrds; i++) {
+      final BytesRef br = docValues.lookupOrd(i);
+      if (br.length==1 && br.bytes[br.offset]=='T') {
+        trueOrd = i;
+        break;
+      }
+    }
+
+    this.trueOrd = trueOrd;
+  }
+
+  @Override
+  public void collect(int doc) throws IOException {
+    exists = docValues.advanceExact(doc);
+    if (exists) {
+      value = trueOrd ==  docValues.ordValue();
+    }
+  }
+
+  @Override
+  public boolean getBoolean() {
+    return value;
+  }
+  @Override
+  public String getString() {
+    return exists ? Boolean.toString(value) : null;
+  }
+  @Override
+  public Object getObject() {
+    return exists ? value : null;
+  }
+  @Override
+  public boolean exists() {
+    return exists;
+  }
+  
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    if (exists) {
+      cons.accept(Boolean.toString(value));
+    }
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    if (exists) {
+      cons.accept(value);
+    }
+  }
+
+  @Override
+  public ExpressionComparator<Boolean> getObjectComparator(String expression) {
+    return new ExpressionComparator<>(expression);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanMultiField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanMultiField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanMultiField.java
new file mode 100644
index 0000000..e4eecd3
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/BooleanMultiField.java
@@ -0,0 +1,101 @@
+/*
+ * 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.DocValues;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.util.BytesRef;
+import org.apache.solr.analytics.util.function.BooleanConsumer;
+import org.apache.solr.analytics.value.BooleanValueStream.CastingBooleanValueStream;
+import org.apache.solr.schema.BoolField;
+
+
+/**
+ * An analytics wrapper for a multi-valued {@link BoolField} with DocValues enabled.
+ */
+public class BooleanMultiField extends AnalyticsField implements CastingBooleanValueStream {
+  private SortedSetDocValues docValues;
+  private int count;
+  private boolean[] values;
+  
+  private int trueOrd;
+
+  public BooleanMultiField(String fieldName) {
+    super(fieldName);
+    count = 0;
+    values = new boolean[initialArrayLength];
+  }
+  
+  @Override
+  public void doSetNextReader(LeafReaderContext context) throws IOException {
+    docValues = DocValues.getSortedSet(context.reader(), fieldName);
+
+    // figure out what ord maps to true
+    long numOrds = docValues.getValueCount();
+    // if no values in the segment, default trueOrd to something other then -1 (missing)
+    int trueOrd = -2;
+    for (int i=0; i<numOrds; i++) {
+      final BytesRef br = docValues.lookupOrd(i);
+      if (br.length==1 && br.bytes[br.offset]=='T') {
+        trueOrd = i;
+        break;
+      }
+    }
+
+    this.trueOrd = trueOrd;
+  }
+  @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++] = term == trueOrd;
+      }
+    }
+  }
+  
+  private void resizeValues() {
+    boolean[] newValues = new boolean[values.length*2];
+    for (int i = 0; i < count; ++i) {
+      newValues[i] = values[i];
+    }
+    values = newValues;
+  }
+  
+  @Override
+  public void streamBooleans(BooleanConsumer cons) {
+    for (int i = 0; i < count; ++i) {
+      cons.accept(values[i]);
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamBooleans(value -> cons.accept(Boolean.toString(value)));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamBooleans(value -> cons.accept(value));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateField.java
new file mode 100644
index 0000000..88e71bc
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateField.java
@@ -0,0 +1,108 @@
+/*
+ * 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.time.Instant;
+import java.util.Date;
+import java.util.function.Consumer;
+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.DateValue.CastingDateValue;
+import org.apache.solr.schema.DatePointField;
+import org.apache.solr.schema.TrieDateField;
+
+/**
+ * An analytics wrapper for a single-valued {@link TrieDateField} or {@link DatePointField} with DocValues enabled.
+ */
+public class DateField extends AnalyticsField implements CastingDateValue {
+  private NumericDocValues docValues; 
+  private long value;
+  private boolean exists;
+
+  public DateField(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 Date getDate() {
+    return exists ? new Date(value) : null;
+  }
+  @Override
+  public String getString() {
+    return exists ? Instant.ofEpochMilli(value).toString() : 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 streamDates(Consumer<Date> cons) {
+    if (exists) {
+      cons.accept(new Date(value));
+    }
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    if (exists) {
+      cons.accept(Instant.ofEpochMilli(value).toString());
+    }
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    if (exists) {
+      cons.accept(new Date(value));
+    }
+  }
+
+  @Override
+  public ExpressionComparator<Date> getObjectComparator(String expression) {
+    return new ExpressionComparator<>(expression);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiField.java
new file mode 100644
index 0000000..64ee489
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiField.java
@@ -0,0 +1,47 @@
+/*
+ * 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.time.Instant;
+import java.util.Date;
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.value.DateValueStream.CastingDateValueStream;
+import org.apache.solr.schema.TrieDateField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link TrieDateField} with DocValues enabled.
+ */
+public class DateMultiField extends LongMultiField implements CastingDateValueStream {
+
+  public DateMultiField(String fieldName) {
+    super(fieldName);
+  }
+  
+  @Override
+  public void streamDates(Consumer<Date> cons) {
+    streamLongs(value -> cons.accept(new Date(value)));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamLongs(value -> cons.accept(Instant.ofEpochMilli(value).toString()));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamLongs(value -> cons.accept(new Date(value)));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiPointField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiPointField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiPointField.java
new file mode 100644
index 0000000..a1560ef
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DateMultiPointField.java
@@ -0,0 +1,47 @@
+/*
+ * 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.time.Instant;
+import java.util.Date;
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.value.DateValueStream.CastingDateValueStream;
+import org.apache.solr.schema.DatePointField;
+
+/**
+ * An analytics wrapper for a multi-valued {@link DatePointField} with DocValues enabled.
+ */
+public class DateMultiPointField extends LongMultiPointField implements CastingDateValueStream {
+
+  public DateMultiPointField(String fieldName) {
+    super(fieldName);
+  }
+  
+  @Override
+  public void streamDates(Consumer<Date> cons) {
+    streamLongs(value -> cons.accept(new Date(value)));
+  }
+  @Override
+  public void streamStrings(Consumer<String> cons) {
+    streamLongs(value -> cons.accept(Instant.ofEpochMilli(value).toString()));
+  }
+  @Override
+  public void streamObjects(Consumer<Object> cons) {
+    streamLongs(value -> cons.accept(new Date(value)));
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleField.java
new file mode 100644
index 0000000..68941a4
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleField.java
@@ -0,0 +1,97 @@
+/*
+ * 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.lucene.util.NumericUtils;
+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 = NumericUtils.sortableLongToDouble(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/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleMultiField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleMultiField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleMultiField.java
new file mode 100644
index 0000000..3d58634
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleMultiField.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 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.
+ */
+public class DoubleMultiField extends AnalyticsField implements CastingDoubleValueStream {
+  private SortedSetDocValues docValues;
+  private int count;
+  private double[] values;
+
+  public DoubleMultiField(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/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleMultiPointField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleMultiPointField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/DoubleMultiPointField.java
new file mode 100644
index 0000000..0933f60
--- /dev/null
+++ b/solr/contrib/analytics/src/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/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatField.java
new file mode 100644
index 0000000..c382d61
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatField.java
@@ -0,0 +1,108 @@
+/*
+ * 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.lucene.util.NumericUtils;
+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 = NumericUtils.sortableIntToFloat((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/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatMultiField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatMultiField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatMultiField.java
new file mode 100644
index 0000000..acfce18
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatMultiField.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 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.
+ */
+public class FloatMultiField extends AnalyticsField implements CastingFloatValueStream {
+  private SortedSetDocValues docValues;
+  private int count;
+  private float[] values;
+
+  public FloatMultiField(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/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatMultiPointField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatMultiPointField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/FloatMultiPointField.java
new file mode 100644
index 0000000..947035e
--- /dev/null
+++ b/solr/contrib/analytics/src/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/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntField.java
new file mode 100644
index 0000000..e9ae52b
--- /dev/null
+++ b/solr/contrib/analytics/src/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/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntMultiField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntMultiField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntMultiField.java
new file mode 100644
index 0000000..657b1f3
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntMultiField.java
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+public class IntMultiField extends AnalyticsField implements CastingIntValueStream {
+  private SortedSetDocValues docValues;
+  private int count;
+  private int[] values;
+
+  public IntMultiField(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/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntMultiPointField.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntMultiPointField.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/function/field/IntMultiPointField.java
new file mode 100644
index 0000000..2608fa1
--- /dev/null
+++ b/solr/contrib/analytics/src/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));
+  }
+}