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

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

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/FieldFacetAccumulator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/FieldFacetAccumulator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/FieldFacetAccumulator.java
deleted file mode 100644
index fb0884b..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/FieldFacetAccumulator.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.solr.analytics.accumulator.facet;
-
-import java.io.IOException;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.NumericDocValues;
-import org.apache.lucene.index.SortedDocValues;
-import org.apache.lucene.index.SortedSetDocValues;
-import org.apache.lucene.util.BytesRef;
-import org.apache.solr.analytics.accumulator.FacetingAccumulator;
-import org.apache.solr.analytics.accumulator.ValueAccumulator;
-import org.apache.solr.analytics.util.AnalyticsParsers.NumericParser;
-import org.apache.solr.analytics.util.AnalyticsParsers.Parser;
-import org.apache.solr.analytics.util.AnalyticsParsers;
-import org.apache.solr.common.util.NamedList;
-import org.apache.solr.schema.DateValueFieldType;
-import org.apache.solr.schema.SchemaField;
-import org.apache.solr.search.SolrIndexSearcher;
-
-/**
- * An Accumulator that manages the faceting for fieldFacets.
- * Collects the field facet values.
- */
-public class FieldFacetAccumulator extends ValueAccumulator {
-  protected final Parser parser;
-  protected final FacetValueAccumulator parent;
-  protected final String name;
-  protected final SolrIndexSearcher searcher;
-  protected final SchemaField schemaField;
-  protected final boolean multiValued;
-  protected final boolean numField;
-  protected final boolean dateField;
-  protected SortedSetDocValues setValues;
-  protected SortedDocValues sortValues;
-  protected NumericDocValues numValues;
-  
-  public FieldFacetAccumulator(SolrIndexSearcher searcher, FacetValueAccumulator parent, SchemaField schemaField) throws IOException {  
-    if( !schemaField.hasDocValues() ){
-      throw new IOException("Field '"+schemaField.getName()+"' does not have docValues and therefore cannot be faceted over.");
-    }
-    this.searcher = searcher;
-    this.schemaField = schemaField;
-    this.name = schemaField.getName();
-    this.multiValued = schemaField.multiValued();
-    this.numField = schemaField.getType().getNumberType()!=null;
-    this.dateField = schemaField.getType() instanceof DateValueFieldType;
-    this.parent = parent;  
-    this.parser = AnalyticsParsers.getParser(schemaField.getType().getClass());
-  }
-
-  public static FieldFacetAccumulator create(SolrIndexSearcher searcher, FacetValueAccumulator parent, SchemaField facetField) throws IOException{
-    return new FieldFacetAccumulator(searcher,parent,facetField);
-  }
-
-  /**
-   * Move to the next set of documents to add to the field facet.
-   */
-  @Override
-  protected void doSetNextReader(LeafReaderContext context) throws IOException {
-    if (multiValued) {
-      setValues = context.reader().getSortedSetDocValues(name);
-    } else {
-      if (numField) {
-        numValues = context.reader().getNumericDocValues(name);
-      } else {
-        sortValues = context.reader().getSortedDocValues(name);
-      }
-    }
-  }
-
-  /**
-   * Tell the FacetingAccumulator to collect the doc with the 
-   * given fieldFacet and value(s).
-   */
-  @Override
-  public void collect(int doc) throws IOException {
-    if (multiValued) {
-      boolean exists = false;
-      if (setValues!=null) {
-        if (doc > setValues.docID()) {
-          setValues.advance(doc);
-        }
-        if (doc == setValues.docID()) {
-          int term;
-          while ((term = (int)setValues.nextOrd()) != SortedSetDocValues.NO_MORE_ORDS) {
-            exists = true;
-            final BytesRef value = setValues.lookupOrd(term);
-            parent.collectField(doc, name, parser.parse(value) );
-          }
-        }
-      }
-      if (!exists) {
-        parent.collectField(doc, name, FacetingAccumulator.MISSING_VALUE );
-      }
-    } else {
-      if(numField){
-        if(numValues != null) {
-          int valuesDocID = numValues.docID();
-          if (valuesDocID < doc) {
-            valuesDocID = numValues.advance(doc);
-          }
-          if (valuesDocID == doc) {
-            parent.collectField(doc, name, ((NumericParser)parser).parseNum(numValues.longValue()));
-          } else {
-            parent.collectField(doc, name, FacetingAccumulator.MISSING_VALUE );
-          }
-        } else {
-          parent.collectField(doc, name, FacetingAccumulator.MISSING_VALUE );
-        }
-      } else {
-        if(sortValues != null) {
-          if (doc > sortValues.docID()) {
-            sortValues.advance(doc);
-          }
-          if (doc == sortValues.docID()) {
-            parent.collectField(doc, name, parser.parse(sortValues.lookupOrd(sortValues.ordValue())) );
-          } else {
-            parent.collectField(doc, name, FacetingAccumulator.MISSING_VALUE );
-          }
-        } else {
-          parent.collectField(doc, name, FacetingAccumulator.MISSING_VALUE );
-        }
-      }
-    }
-  }
-
-  @Override
-  public void compute() {}
- 
-  @Override
-  public NamedList<?> export() { return null; }
-
-  @Override
-  public boolean needsScores() {
-    return true; // TODO: is this true?
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/QueryFacetAccumulator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/QueryFacetAccumulator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/QueryFacetAccumulator.java
deleted file mode 100644
index 8b92ba0..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/QueryFacetAccumulator.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.solr.analytics.accumulator.facet;
-
-import java.io.IOException;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.solr.analytics.accumulator.ValueAccumulator;
-import org.apache.solr.analytics.statistics.StatsCollector;
-import org.apache.solr.common.util.NamedList;
-
-/**
- * An Accumulator that manages a certain query of a given query facet.
- */
-public class QueryFacetAccumulator extends ValueAccumulator {
-  protected final FacetValueAccumulator parent;
-  protected final String facetName;
-  protected final String facetValue;
-
-  public QueryFacetAccumulator(FacetValueAccumulator parent, String facetName, String facetValue) {
-    this.parent = parent;
-    this.facetName = facetName;
-    this.facetValue = facetValue;
-  }
-
-  /**
-   * Tell the FacetingAccumulator to collect the doc with the 
-   * given queryFacet and query.
-   */
-  @Override
-  public void collect(int doc) throws IOException {
-    parent.collectQuery(doc, facetName, facetValue);
-  }
-
-  /**
-   * Update the readers of the queryFacet {@link StatsCollector}s in FacetingAccumulator
-   */
-  @Override
-  protected void doSetNextReader(LeafReaderContext context) throws IOException {
-    parent.setQueryStatsCollectorReaders(context);
-  }
-
-  @Override
-  public void compute() {
-    // NOP
-  }
-
-  @Override
-  public NamedList<?> export() {
-    // NOP
-    return null;
-  }
-
-  @Override
-  public boolean needsScores() {
-    return true; // TODO: is this true?
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/RangeFacetAccumulator.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/RangeFacetAccumulator.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/RangeFacetAccumulator.java
deleted file mode 100644
index 59cf428..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/accumulator/facet/RangeFacetAccumulator.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.solr.analytics.accumulator.facet;
-
-import java.io.IOException;
-
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.solr.analytics.statistics.StatsCollector;
-
-/**
- * An Accumulator that manages a certain range of a given range facet.
- */
-public class RangeFacetAccumulator extends QueryFacetAccumulator {
-  public RangeFacetAccumulator(FacetValueAccumulator parent, String facetName, String facetValue) {
-    super(parent, facetName, facetValue);
-  }
-
-  /**
-   * Tell the FacetingAccumulator to collect the doc with the 
-   * given rangeFacet and range.
-   */
-  @Override
-  public void collect(int doc) throws IOException {
-    parent.collectRange(doc, facetName, facetValue);
-  }
-
-  /**
-   * Update the readers of the rangeFacet {@link StatsCollector}s in FacetingAccumulator
-   */
-  @Override
-  protected void doSetNextReader(LeafReaderContext context) throws IOException {
-    parent.setRangeStatsCollectorReaders(context);
-  }
-
-}

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

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

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/BaseExpression.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/BaseExpression.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/BaseExpression.java
deleted file mode 100644
index 2f0326b..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/BaseExpression.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.solr.analytics.expression;
-
-import java.util.Date;
-
-import org.apache.solr.analytics.statistics.StatsCollector;
-
-
-/**
- * <code>BaseExpression</code> returns the value returned by the {@link StatsCollector} for the specified stat.
- */
-public class BaseExpression extends Expression {
-  protected final StatsCollector statsCollector;
-  protected final String stat;
-  
-  public BaseExpression(StatsCollector statsCollector, String stat) {
-    this.statsCollector = statsCollector;
-    this.stat = stat;
-  }
-  
-  public Comparable getValue() {
-    if(statsCollector.getStatsList().contains(stat)) {
-      return statsCollector.getStat(stat);
-    }
-    return null;
-  }
-}
-/**
- * <code>ConstantStringExpression</code> returns the specified constant double.
- */
-class ConstantNumberExpression extends Expression {
-  protected final Double constant;
-  
-  public ConstantNumberExpression(double d) {
-    constant = new Double(d);
-  }
-  
-  public Comparable getValue() {
-    return constant;
-  }
-}
-/**
- * <code>ConstantStringExpression</code> returns the specified constant date.
- */
-class ConstantDateExpression extends Expression {
-  protected final Date constant;
-  
-  public ConstantDateExpression(Date date) {
-    constant = date;
-  }
-  
-  public ConstantDateExpression(Long date) {
-    constant = new Date(date);
-  }
-  
-  public Comparable getValue() {
-    return constant;
-  }
-}
-/**
- * <code>ConstantStringExpression</code> returns the specified constant string.
- */
-class ConstantStringExpression extends Expression {
-  protected final String constant;
-  
-  public ConstantStringExpression(String str) {
-    constant = str;
-  }
-  
-  public Comparable getValue() {
-    return constant;
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/DualDelegateExpression.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/DualDelegateExpression.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/DualDelegateExpression.java
deleted file mode 100644
index f906b47..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/DualDelegateExpression.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.solr.analytics.expression;
-
-/**
- * Abstraction of an expression that applies a function to two delegate expressions.
- */
-public abstract class DualDelegateExpression extends Expression {
-  protected Expression a;
-  protected Expression b;
-  public DualDelegateExpression(Expression a, Expression b) {
-    this.a = a;
-    this.b = b;
-  }
-}
-/**
- * <code>DivideExpression</code> returns the quotient of 'a' and 'b'.
- */
-class DivideExpression extends DualDelegateExpression {
-  
-  /**
-   * @param a numerator
-   * @param b divisor
-   */
-  public DivideExpression(Expression a, Expression b) {
-    super(a,b);
-  }
-
-  @Override
-  public Comparable getValue() {
-    Comparable aComp = a.getValue();
-    Comparable bComp = b.getValue();
-    if (aComp==null || bComp==null) {
-      return null;
-    }
-    double div = ((Number)aComp).doubleValue();
-    div = div / ((Number)bComp).doubleValue();
-    return new Double(div);
-  }
-}
-/**
- * <code>PowerExpression</code> returns 'a' to the power of 'b'.
- */
-class PowerExpression extends DualDelegateExpression {
-
-  /**
-   * @param a base
-   * @param b exponent
-   */
-  public PowerExpression(Expression a, Expression b) {
-    super(a,b);
-  }
-
-  @Override
-  public Comparable getValue() {
-    Comparable aComp = a.getValue();
-    Comparable bComp = b.getValue();
-    if (aComp==null || bComp==null) {
-      return null;
-    }
-    return new Double(Math.pow(((Number)aComp).doubleValue(),((Number)bComp).doubleValue()));
-  }
-}
-/**
- * <code>LogExpression</code> returns the log of the delegate's value given a base number.
- */
-class LogExpression extends DualDelegateExpression {
-  /**
-   * @param a number
-   * @param b base
-   */
-  public LogExpression(Expression a, Expression b) {
-    super(a,b);
-  }
-
-  @Override
-  public Comparable getValue() {
-    Comparable aComp = a.getValue();
-    Comparable bComp = b.getValue();
-    if (aComp==null || bComp==null) {
-      return null;
-    }
-    return Math.log(((Number)aComp).doubleValue())/Math.log(((Number)bComp).doubleValue());
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/Expression.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/Expression.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/Expression.java
deleted file mode 100644
index ba26d9a..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/Expression.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.solr.analytics.expression;
-
-import java.util.Comparator;
-
-import org.apache.solr.analytics.request.FieldFacetRequest.FacetSortDirection;
-
-/**
- * Expressions map either zero, one, two or many inputs to a single value. 
- * They can be defined recursively to compute complex math.
- */
-public abstract class Expression {
-  public abstract Comparable getValue();
-
-  public Comparator<Expression> comparator(final FacetSortDirection direction) {
-    return (a, b) -> {
-      if( direction == FacetSortDirection.ASCENDING ){
-        return a.getValue().compareTo(b.getValue());
-      } else {
-        return b.getValue().compareTo(a.getValue());
-      }
-    };
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/ExpressionFactory.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/ExpressionFactory.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/ExpressionFactory.java
deleted file mode 100644
index 1f2d0e0..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/ExpressionFactory.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.solr.analytics.expression;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.solr.analytics.statistics.StatsCollector;
-import org.apache.solr.analytics.util.AnalyticsParams;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.SolrException.ErrorCode;
-import org.apache.solr.util.DateMathParser;
-
-public class ExpressionFactory {
-
-  /**
-   * Creates a single expression that contains delegate expressions and/or 
-   * a StatsCollector.
-   * StatsCollectors are given as input and not created within the method so that
-   * expressions can share the same StatsCollectors, minimizing computation.
-   * 
-   * @param expression String representation of the desired expression
-   * @param statsCollectors List of StatsCollectors to build the expression with. 
-   * @return the expression
-   */
-  @SuppressWarnings("deprecation")
-  public static Expression create(String expression, StatsCollector[] statsCollectors) {
-    int paren = expression.indexOf('(');
-    if (paren<=0) {
-      throw new SolrException(ErrorCode.BAD_REQUEST, "The expression ["+expression+"] has no arguments and is not supported.");
-    }
-    String topOperation = expression.substring(0,paren).trim();
-    String operands;
-    try {
-      operands = expression.substring(paren+1, expression.lastIndexOf(')')).trim();
-    } catch (Exception e) {
-      throw new SolrException(ErrorCode.BAD_REQUEST,"Missing closing parenthesis in ["+expression+"]",e);
-    }
-    
-    // Builds a statistic, constant or recursively builds an expression tree
-    
-    // Statistic 
-    if (AnalyticsParams.ALL_STAT_SET.contains(topOperation)) {
-      if (topOperation.equals(AnalyticsParams.STAT_PERCENTILE)) {
-        operands = expression.substring(expression.indexOf(',')+1, expression.lastIndexOf(')')).trim();
-        topOperation = topOperation+"_"+expression.substring(expression.indexOf('(')+1, expression.indexOf(',')).trim();
-      }
-      StatsCollector collector = null;
-      // Finds the desired counter and builds an expression around it and the desired statistic.
-      for (StatsCollector c : statsCollectors) {
-        if (c.valueSourceString().equals(operands)) { 
-          collector = c;
-          break;
-        }
-      }
-      if (collector == null) {
-        throw new SolrException(ErrorCode.BAD_REQUEST, "ValueSource ["+operands+"] in Expression ["+expression+"] not found.");
-      }
-      return new BaseExpression(collector, topOperation);
-    }
-    // Constant
-    if (topOperation.equals(AnalyticsParams.CONSTANT_NUMBER)) {
-      try {
-        return new ConstantNumberExpression(Double.parseDouble(operands));
-      } catch (NumberFormatException e) {
-        throw new SolrException(ErrorCode.BAD_REQUEST, "The constant "+operands+" cannot be converted into a number.",e);
-      }
-    } else if (topOperation.equals(AnalyticsParams.CONSTANT_DATE)) {
-      return new ConstantDateExpression(DateMathParser.parseMath(null, operands));
-    } else if (topOperation.equals(AnalyticsParams.CONSTANT_STRING)) {
-      operands = expression.substring(paren+1, expression.lastIndexOf(')'));
-      return new ConstantStringExpression(operands);
-    }
-    
-    // Complex Delegating Expressions
-    String[] arguments = getArguments(operands);
-    Expression[] expArgs = new Expression[arguments.length];
-    for (int count = 0; count < arguments.length; count++) {
-      // Recursively builds delegate expressions
-      expArgs[count] = create(arguments[count], statsCollectors);
-    }
-    
-    // Single Delegate Expressions
-    if (expArgs.length==1) {
-      // Numeric Expression
-      if (topOperation.equals(AnalyticsParams.NEGATE)) {
-        return new NegateExpression(expArgs[0]);
-      }
-      if (topOperation.equals(AnalyticsParams.ABSOLUTE_VALUE)) {
-        return new AbsoluteValueExpression(expArgs[0]);
-      }
-      // String Expression
-      else if (topOperation.equals(AnalyticsParams.REVERSE)) {
-        return new ReverseExpression(expArgs[0]);
-      }
-      throw new SolrException(ErrorCode.BAD_REQUEST, topOperation+" does not have the correct number of arguments.");
-    }  else {
-      // Multi Delegate Expressions
-      // Numeric Expression
-      if (topOperation.equals(AnalyticsParams.ADD)) {
-        return new AddExpression(expArgs);
-      } else if (topOperation.equals(AnalyticsParams.MULTIPLY)) {
-        return new MultiplyExpression(expArgs);
-      }
-      // Date Expression
-      else if (topOperation.equals(AnalyticsParams.DATE_MATH)) {
-        return new DateMathExpression(expArgs);
-      } 
-      // String Expression
-      else if (topOperation.equals(AnalyticsParams.CONCATENATE)) {
-        return new ConcatenateExpression(expArgs);
-      } 
-      // Dual Delegate Expressions
-      else if (expArgs.length==2 && (topOperation.equals(AnalyticsParams.DIVIDE) || topOperation.equals(AnalyticsParams.POWER) 
-          || topOperation.equals(AnalyticsParams.LOG))) {
-        // Numeric Expression
-        if (topOperation.equals(AnalyticsParams.DIVIDE)) {
-          return new DivideExpression(expArgs[0], expArgs[1]);
-        } else if (topOperation.equals(AnalyticsParams.POWER)) {
-          return new PowerExpression(expArgs[0], expArgs[1]);
-        } else if (topOperation.equals(AnalyticsParams.LOG)) {
-          return new LogExpression(expArgs[0], expArgs[1]);
-        }
-        return null;
-      }
-      throw new SolrException(ErrorCode.BAD_REQUEST, topOperation+" does not have the correct number of arguments or is unsupported.");
-    }
-    
-  }
-  
-  /**
-   * Splits up an Expression's arguments.
-   * 
-   * @param expression Current expression string
-   * @return List The list of arguments
-   */
-  public static String[] getArguments(String expression) {
-    String[] strings = new String[1];
-    int stack = 0;
-    int start = 0;
-    List<String> arguments = new ArrayList<>();
-    char[] chars = expression.toCharArray();
-    for (int count = 0; count < expression.length(); count++) {
-      char c = chars[count];
-      if (c==',' && stack == 0) {
-        arguments.add(expression.substring(start, count).replace("\\(","(").replace("\\)",")").replace("\\,",",").trim());
-        start = count+1;
-      } else if (c == '(') {
-        stack ++;
-      } else if (c == ')') {
-        stack --;
-      } else if (c == '\\') {
-        ; // Do nothing.
-      }
-    }
-    if (stack==0) {
-      arguments.add(expression.substring(start).trim());
-    }
-    return arguments.toArray(strings);
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/MultiDelegateExpression.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/MultiDelegateExpression.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/MultiDelegateExpression.java
deleted file mode 100644
index 4ea66fa..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/MultiDelegateExpression.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.solr.analytics.expression;
-
-import java.text.ParseException;
-import java.util.Date;
-
-import org.apache.solr.util.DateMathParser;
-
-/**
- * Abstraction of an expression that applies a function to an array of delegate expressions.
- */
-public abstract class MultiDelegateExpression extends Expression {
-  protected final Expression[] delegates;
-  
-  public MultiDelegateExpression(Expression[] delegates) {
-    this.delegates = delegates;
-  }
-}
-/**
- * <code>AddExpression</code> returns the sum of its components' values.
- */
-class AddExpression extends MultiDelegateExpression {
-  public AddExpression(Expression[] delegates) {
-    super(delegates);
-  }
-
-  @Override
-  public Comparable getValue() {
-    double sum = 0;
-    for (Expression delegate : delegates) {
-      Comparable dComp = delegate.getValue();
-      if (dComp==null) {
-        return null;
-      } else if (dComp.getClass().equals(Date.class)) {
-        dComp = new Long(((Date)dComp).getTime());
-      }
-      sum += ((Number)dComp).doubleValue();
-    }
-    return new Double(sum);
-  }
-}
-/**
- * <code>MultiplyExpression</code> returns the product of its delegates' values.
- */
-class MultiplyExpression extends MultiDelegateExpression {
-  public MultiplyExpression(Expression[] delegates) {
-    super(delegates);
-  }
-
-  @Override
-  public Comparable getValue() {
-    double prod = 1;
-    for (Expression delegate : delegates) {
-      Comparable dComp = delegate.getValue();
-      if (dComp==null) {
-        return null;
-      }
-      prod *= ((Number)dComp).doubleValue();
-    }
-    return new Double(prod);
-  }
-}
-/**
- * <code>DateMathExpression</code> returns the start date modified by the DateMath operations
- */
-class DateMathExpression extends MultiDelegateExpression {
-  /**
-   * @param delegates A list of Expressions. The first element in the list
-   * should be a numeric Expression which represents the starting date. 
-   * The rest of the field should be string Expression objects which contain
-   * the DateMath operations to perform on the start date.
-   */
-  public DateMathExpression(Expression[] delegates) {
-    super(delegates);
-  }
-
-  @Override
-  public Comparable getValue() {
-    DateMathParser parser = new DateMathParser();
-    parser.setNow((Date)delegates[0].getValue());
-    try {
-      for (int count = 1; count<delegates.length; count++) {
-        Comparable dComp = delegates[count].getValue();
-        if (dComp==null) {
-          return null;
-        }
-        parser.setNow(parser.parseMath((String)dComp));
-      }
-      return parser.getNow();
-    } catch (ParseException e) {
-      e.printStackTrace();
-      return parser.getNow();
-    }
-  }
-}
-/**
- * <code>ConcatenateExpression</code> returns the concatenation of its delegates' values in the order given.
- */
-class ConcatenateExpression extends MultiDelegateExpression {
-  public ConcatenateExpression(Expression[] delegates) {
-    super(delegates);
-  }
-
-  @Override
-  public Comparable getValue() {
-    StringBuilder builder = new StringBuilder();
-    for (Expression delegate : delegates) {
-      Comparable dComp = delegate.getValue();
-      if (dComp==null) {
-        return null;
-      }
-      builder.append(dComp.toString());
-    }
-    return builder.toString();
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/SingleDelegateExpression.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/SingleDelegateExpression.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/SingleDelegateExpression.java
deleted file mode 100644
index 8d94ece..0000000
--- a/solr/contrib/analytics/src/java/org/apache/solr/analytics/expression/SingleDelegateExpression.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.solr.analytics.expression;
-
-import java.util.Date;
-
-/**
- * Abstraction of an expression that applies a function to one delegate expression.
- */
-public abstract class SingleDelegateExpression extends Expression {
-  protected Expression delegate;
-  
-  public SingleDelegateExpression(Expression delegate) {
-    this.delegate = delegate;
-  }
-}
-/**
- * <code>NegateExpression</code> returns the negation of the delegate's value.
- */
-class NegateExpression extends SingleDelegateExpression {
-  public NegateExpression(Expression delegate) {
-    super(delegate);
-  }
-
-  @Override
-  public Comparable getValue() {
-    Comparable nComp = delegate.getValue();
-    if (nComp==null) {
-      return null;
-    } else if (nComp.getClass().equals(Date.class)) {
-      nComp = new Long(((Date)nComp).getTime());
-    }
-    return new Double(((Number)nComp).doubleValue()*-1);
-  }
-}
-/**
- * <code>AbsoluteValueExpression</code> returns the negation of the delegate's value.
- */
-class AbsoluteValueExpression extends SingleDelegateExpression {
-  public AbsoluteValueExpression(Expression delegate) {
-    super(delegate);
-  }
-
-  @Override
-  public Comparable getValue() {
-    Comparable nComp = delegate.getValue();
-    if (nComp==null) {
-      return null;
-    }
-    double d = ((Number)nComp).doubleValue();
-    if (d<0) {
-      return new Double(d*-1);
-    } else {
-      return new Double(d);
-    }
-  }
-}
-/**
- * <code>StringExpression</code> returns the reverse of the delegate's string value.
- */
-class ReverseExpression extends SingleDelegateExpression {
-  public ReverseExpression(Expression delegate) {
-    super(delegate);
-  }
-
-  @Override
-  public Comparable getValue() {
-    Comparable rComp = delegate.getValue();
-    if (rComp==null) {
-      return null;
-    }
-    return new StringBuilder(rComp.toString()).reverse().toString();
-  }
-}

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

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AbstractSolrQueryFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AbstractSolrQueryFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AbstractSolrQueryFacet.java
new file mode 100644
index 0000000..d06cfa3
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AbstractSolrQueryFacet.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.util.function.Consumer;
+
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.SimpleCollector;
+import org.apache.solr.analytics.AnalyticsDriver;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.search.Filter;
+import org.apache.solr.search.SolrIndexSearcher;
+
+/**
+ * Solr Query Facets are AnalyticsFacets that are calculated after the document streaming phase has occurred in the {@link AnalyticsDriver}
+ * (during which StreamingFacets and overall expressions are calculated). {@link AbstractSolrQueryFacet}s should not be confused with {@link QueryFacet}s, 
+ * which are a specific sub-type.
+ * 
+ * <p>
+ * The filtering for these facets is done through issuing additional Solr queries, and collecting on the resulting documents.
+ * Unlike streaming facets, which have an unspecified amount of facet values (facet buckets), the amount of facet values is determined by the user and
+ * a Solr query is issued for each requested facet value.
+ */
+public abstract class AbstractSolrQueryFacet extends AnalyticsFacet {
+  
+  protected AbstractSolrQueryFacet(String name) {
+    super(name);
+  }
+
+  /**
+   * Returns the set of {@link FacetValueQueryExecuter}s, one for each facet value, through the given consumer.
+   * 
+   * Each of these executors will be executed after the streaming phase in the {@link AnalyticsDriver}.
+   * 
+   * @param filter the overall filter representing the documents being used for the analytics request
+   * @param queryRequest the queryRequest 
+   * @param consumer the consumer of each facet value's executer
+   */
+  public abstract void createFacetValueExecuters(final Filter filter, SolrQueryRequest queryRequest, Consumer<FacetValueQueryExecuter> consumer);
+  
+  /**
+   * This executer is in charge of issuing the Solr query for a facet value and collecting results as the query is processed.
+   */
+  public class FacetValueQueryExecuter extends SimpleCollector {
+    private final ReductionDataCollection collection;
+    private final Query query;
+    
+    /**
+     * Create an executer to collect the given reduction data from the given Solr query.
+     * 
+     * @param collection The reduction data to collect while querying
+     * @param query The query used to filter for the facet value
+     */
+    public FacetValueQueryExecuter(ReductionDataCollection collection, Query query) {
+      this.collection = collection;
+      this.query = query;
+    }
+
+    @Override
+    public boolean needsScores() {
+      return false;
+    }
+
+    @Override
+    public void doSetNextReader(LeafReaderContext context) throws IOException {
+      collectionManager.doSetNextReader(context);
+    }
+
+    @Override
+    public void collect(int doc) throws IOException {
+      collectionManager.collect(doc);
+      collectionManager.apply();
+    }
+
+    /**
+     * Start the collection for this facet value.
+     * 
+     * @param searcher the solr searcher
+     * @throws IOException if an exception occurs during the querying
+     */
+    public void execute(SolrIndexSearcher searcher) throws IOException {
+      collectionManager.clearLastingCollectTargets();
+      collectionManager.addLastingCollectTarget(collection);
+      searcher.search(query, this);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AnalyticsFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AnalyticsFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AnalyticsFacet.java
new file mode 100644
index 0000000..d9c0f8c
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/AnalyticsFacet.java
@@ -0,0 +1,166 @@
+/*
+ * 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;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+import org.apache.solr.analytics.function.ExpressionCalculator;
+import org.apache.solr.analytics.function.ReductionCollectionManager;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.analytics.function.reduction.data.ReductionData;
+import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
+import org.apache.solr.common.util.NamedList;
+
+/**
+ * An abstract Facet to break up Analytics data over.
+ */
+public abstract class AnalyticsFacet {
+  protected final Map<String,ReductionDataCollection> reductionData;
+  protected ReductionCollectionManager collectionManager;
+  protected ExpressionCalculator expressionCalculator;
+  
+  protected final String name;
+  
+  public AnalyticsFacet(String name) {
+    this.reductionData = new LinkedHashMap<>();
+    this.name = name;
+  }
+  
+  /**
+   * Set the {@link ReductionCollectionManager} that manages the collection of the expressions
+   * calculated with this facet.
+   * 
+   * @param collectionManager The manager for relevant expressions
+   */
+  public void setReductionCollectionManager(ReductionCollectionManager collectionManager) {
+    this.collectionManager = collectionManager;
+  }
+  
+  /**
+   * Set the {@link ExpressionCalculator} that calculates the collection of the expressions
+   * requested for this facet.
+   * 
+   * @param expressionCalculator The calculator for relevant expressions
+   */
+  public void setExpressionCalculator(ExpressionCalculator expressionCalculator) {
+    this.expressionCalculator = expressionCalculator;
+  }
+
+  /**
+   * Import the shard data from a bit-stream, exported by the {@link #exportShardData} method 
+   * in the each of the collection's shards.
+   * 
+   * @param input The bit-stream to import the data from
+   * @throws IOException if an exception occurs while reading from the {@link DataInput}
+   */
+  public void importShardData(DataInput input) throws IOException {
+    int size = input.readInt();
+    for (int i = 0; i < size; ++i) {
+      importFacetValue(input, input.readUTF());
+    }
+  }
+  /**
+   * Import the next facet value's set of {@link ReductionData}.
+   * 
+   * @param input the bit-stream to import the reduction data from
+   * @param facetValue the next facet value
+   * @throws IOException if an exception occurs while reading from the input
+   */
+  protected void importFacetValue(DataInput input, String facetValue) throws IOException {
+    ReductionDataCollection dataCollection = reductionData.get(facetValue);
+    if (dataCollection == null) {
+      reductionData.put(facetValue, collectionManager.newDataCollectionIO());
+    } else {
+      collectionManager.prepareReductionDataIO(dataCollection);
+    }
+    
+    collectionManager.mergeData();
+  }
+
+  /**
+   * Export the shard data through a bit-stream, to be imported by the {@link #importShardData} method 
+   * in the originating shard.
+   * 
+   * @param output The bit-stream to output the data through
+   * @throws IOException if an exception occurs while writing to the {@link DataOutput}
+   */
+  public void exportShardData(DataOutput output) throws IOException {
+    output.writeInt(reductionData.size());
+    for (String facetValue : reductionData.keySet()) {
+      exportFacetValue(output, facetValue);
+    }
+  }
+  /**
+   * Export the next facet value's set of {@link ReductionData}.
+   * 
+   * @param output the bit-stream to output the reduction data to
+   * @param facetValue the next facet value
+   * @throws IOException if an exception occurs while reading from the input
+   */
+  protected void exportFacetValue(DataOutput output, String facetValue) throws IOException {
+    output.writeUTF(facetValue);
+    
+    collectionManager.prepareReductionDataIO(reductionData.get(facetValue));
+    collectionManager.exportData();
+  }
+  
+  /**
+   * Create the old olap-style response of the facet to be returned in the overall analytics response.
+   * 
+   * @return the response of the facet
+   */
+  public NamedList<Object> createOldResponse() {
+    NamedList<Object> nl = new NamedList<>();
+    reductionData.forEach((facetVal, dataCol) -> {
+      collectionManager.setData(dataCol);
+      nl.add(facetVal, new NamedList<>(expressionCalculator.getResults()));
+    });
+    return nl;
+  }
+  
+  /**
+   * Create the response of the facet to be returned in the overall analytics response.
+   * 
+   * @return the response of the facet
+   */
+  public Iterable<Map<String,Object>> createResponse() {
+    LinkedList<Map<String,Object>> list = new LinkedList<>();
+    reductionData.forEach((facetVal, dataCol) -> {
+      Map<String, Object> bucket = new HashMap<>();
+      bucket.put(AnalyticsResponseHeadings.FACET_VALUE, facetVal);
+      collectionManager.setData(dataCol);
+      expressionCalculator.addResults(bucket);
+      list.add(bucket);
+    });
+    return list;
+  }
+  
+  /**
+   * Get the name of the Facet. This is unique for the grouping.
+   * 
+   * @return The name of the Facet
+   */
+  public String getName() {
+    return name;
+  }
+}
\ 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/PivotFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotFacet.java
new file mode 100644
index 0000000..f9e35f7
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotFacet.java
@@ -0,0 +1,114 @@
+/*
+ * 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;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.solr.analytics.function.ExpressionCalculator;
+import org.apache.solr.analytics.function.ReductionCollectionManager;
+import org.apache.solr.common.util.NamedList;
+
+/**
+ * A facet that takes in multiple ValueFacet expressions and does analytics calculations over each dimension given. 
+ */
+public class PivotFacet extends AnalyticsFacet implements StreamingFacet {
+  private final PivotHead<?> pivotHead;
+
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  public PivotFacet(String name, PivotNode<?> topPivot) {
+    super(name);
+    this.pivotHead = new PivotHead(topPivot);
+  }
+  
+  @Override
+  public void setReductionCollectionManager(ReductionCollectionManager collectionManager) {
+    pivotHead.setReductionCollectionManager(collectionManager);
+  }
+
+  @Override
+  public void setExpressionCalculator(ExpressionCalculator expressionCalculator) {
+    pivotHead.setExpressionCalculator(expressionCalculator);
+  }
+
+  @Override
+  public void addFacetValueCollectionTargets() {
+    pivotHead.addFacetValueCollectionTargets();
+  }
+
+  @Override
+  public void importShardData(DataInput input) throws IOException {
+    pivotHead.importShardData(input);
+  }
+
+  @Override
+  public void exportShardData(DataOutput output) throws IOException {
+    pivotHead.exportShardData(output);
+  }
+  
+  @Override
+  public NamedList<Object> createOldResponse() {
+    return new NamedList<>();
+  }
+  
+  @Override
+  public Iterable<Map<String,Object>> createResponse() {
+    return pivotHead.createResponse();
+  }
+}
+/**
+ * Typed Pivot class that stores the overall Pivot data and head of the Pivot node chain.
+ * 
+ * This class exists so that the {@link PivotFacet} class doesn't have to be typed ( {@code <T>} ).
+ */
+class PivotHead<T> implements StreamingFacet {
+  private final PivotNode<T> topPivot;
+  private final Map<String, T> pivotValues;
+  
+  public PivotHead(PivotNode<T> topPivot) {
+    this.topPivot = topPivot;
+    this.pivotValues = new HashMap<>();
+  }
+  
+  public void setReductionCollectionManager(ReductionCollectionManager collectionManager) {
+    topPivot.setReductionCollectionManager(collectionManager);
+  }
+  
+  public void setExpressionCalculator(ExpressionCalculator expressionCalculator) {
+    topPivot.setExpressionCalculator(expressionCalculator);
+  }
+
+  @Override
+  public void addFacetValueCollectionTargets() {
+    topPivot.addFacetValueCollectionTargets(pivotValues);
+  }
+
+  public void importShardData(DataInput input) throws IOException {
+    topPivot.importPivot(input, pivotValues);
+  }
+
+  public void exportShardData(DataOutput output) throws IOException {
+    topPivot.exportPivot(output, pivotValues);
+  }
+  
+  public Iterable<Map<String,Object>> createResponse() {
+    return topPivot.getPivotedResponse(pivotValues);
+  }
+}
\ 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/PivotNode.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotNode.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotNode.java
new file mode 100644
index 0000000..c6c0dc4
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/PivotNode.java
@@ -0,0 +1,263 @@
+/*
+ * 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;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.AnalyticsDriver;
+import org.apache.solr.analytics.function.ExpressionCalculator;
+import org.apache.solr.analytics.function.ReductionCollectionManager;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.analytics.function.reduction.data.ReductionData;
+import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
+import org.apache.solr.analytics.value.StringValueStream;
+
+/**
+ * Representation of one layer of a Pivot Facet. A PivotFacet node is individually sortable,
+ * and is collected during the streaming phase of the {@link AnalyticsDriver}.
+ */
+public abstract class PivotNode<T> extends SortableFacet implements Consumer<String> {
+  private StringValueStream expression;
+  protected Map<String,T> currentPivot;
+
+  public PivotNode(String name, StringValueStream expression) {
+    super(name);
+    this.expression = expression;
+  }
+
+  /**
+   * Determine which facet values match the current document. Add the {@link ReductionDataCollection}s of the relevant facet values
+   * to the targets of the streaming {@link ReductionCollectionManager} so that they are updated with the current document's data.
+   */
+  public void addFacetValueCollectionTargets(Map<String,T> pivot) {
+    currentPivot = pivot;
+    expression.streamStrings(this);
+  }
+  
+  /**
+   * Import the shard data from a bit-stream for the given pivot, exported by the {@link #exportPivot} method 
+   * in the each of the collection's shards.
+   * 
+   * @param input The bit-stream to import the data from
+   * @param pivot the values for this pivot node and the pivot children (if they exist)
+   * @throws IOException if an exception occurs while reading from the {@link DataInput}
+   */
+  public void importPivot(DataInput input, Map<String,T> pivot) throws IOException {
+    int size = input.readInt();
+    currentPivot = pivot;
+    for (int i = 0; i < size; ++i) {
+      importPivotValue(input, input.readUTF());
+    }
+  }
+  /**
+   * Import the next pivot value's set of {@link ReductionData} and children's {@link ReductionData} if they exist.
+   * 
+   * @param input the bit-stream to import the reduction data from
+   * @param pivotValue the next pivot value
+   * @throws IOException if an exception occurs while reading from the input
+   */
+  protected abstract void importPivotValue(DataInput input, String pivotValue) throws IOException;
+
+  /**
+   * Export the shard data through a bit-stream for the given pivot, 
+   * to be imported by the {@link #importPivot} method in the originating shard.
+   * 
+   * @param output The bit-stream to output the data through
+   * @param pivot the values for this pivot node and the pivot children (if they exist)
+   * @throws IOException if an exception occurs while writing to the {@link DataOutput}
+   */
+  public void exportPivot(DataOutput output, Map<String,T> pivot) throws IOException {
+    output.writeInt(pivot.size());
+    for (String pivotValue : pivot.keySet()) {
+      output.writeUTF(pivotValue);
+      exportPivotValue(output, pivot.get(pivotValue));
+    }
+  }
+  /**
+   * Export the given pivot data, containing {@link ReductionData} and pivot children if they exist.
+   * 
+   * @param output the bit-stream to output the reduction data to
+   * @param pivotData the next pivot value data
+   * @throws IOException if an exception occurs while reading from the input
+   */
+  protected abstract void exportPivotValue(DataOutput output, T pivotData) throws IOException;
+  
+  /**
+   * Create the response of the facet to be returned in the overall analytics response.
+   * 
+   * @param pivot the pivot to create a response for
+   * @return the response of the facet
+   */
+  public abstract Iterable<Map<String,Object>> getPivotedResponse(Map<String,T> pivot);
+  
+  /**
+   * A pivot node that has no pivot children.
+   */
+  public static class PivotLeaf extends PivotNode<ReductionDataCollection> {
+
+    public PivotLeaf(String name, StringValueStream expression) {
+      super(name, expression);
+    }
+    
+    @Override
+    public void accept(String pivotValue) {
+      ReductionDataCollection collection = currentPivot.get(pivotValue);
+      if (collection == null) {
+        collection = collectionManager.newDataCollectionTarget();
+        currentPivot.put(pivotValue, collection);
+      } else {
+        collectionManager.addCollectTarget(collection);
+      }
+    }
+
+    @Override
+    protected void importPivotValue(DataInput input, String pivotValue) throws IOException {
+      ReductionDataCollection dataCollection = currentPivot.get(pivotValue);
+      if (dataCollection == null) {
+        currentPivot.put(pivotValue, collectionManager.newDataCollectionIO());
+      } else {
+        collectionManager.prepareReductionDataIO(dataCollection);
+      }
+      collectionManager.mergeData();
+    }
+
+    @Override
+    protected void exportPivotValue(DataOutput output, ReductionDataCollection pivotData) throws IOException {
+      collectionManager.prepareReductionDataIO(pivotData);
+      collectionManager.exportData();
+    }
+
+    @Override
+    public Iterable<Map<String,Object>> getPivotedResponse(Map<String,ReductionDataCollection> pivot) {
+      final List<FacetBucket> facetResults = new ArrayList<>();
+      pivot.forEach((facetVal, dataCol) -> {
+        collectionManager.setData(dataCol);
+        facetResults.add(new FacetBucket(facetVal,expressionCalculator.getResults()));
+      });
+
+      Iterable<FacetBucket> facetResultsIter = applyOptions(facetResults);
+      final LinkedList<Map<String,Object>> results = new LinkedList<>();
+      // Export each expression in the bucket.
+      for (FacetBucket bucket : facetResultsIter) {
+        Map<String, Object> bucketMap = new HashMap<>();
+        bucketMap.put(AnalyticsResponseHeadings.PIVOT_NAME, name);
+        bucketMap.put(AnalyticsResponseHeadings.FACET_VALUE, bucket.getFacetValue());
+        bucketMap.put(AnalyticsResponseHeadings.RESULTS, bucket.getResults());
+        results.add(bucketMap);
+      }
+      return results;
+    }
+  }
+  
+  /**
+   * A pivot node that has pivot children.
+   */
+  public static class PivotBranch<T> extends PivotNode<PivotBranch.PivotDataPair<T>> {
+    private final PivotNode<T> childPivot;
+    public PivotBranch(String name, StringValueStream expression, PivotNode<T> childPivot) {
+      super(name, expression);
+      this.childPivot = childPivot;
+    }
+    
+    @Override
+    public void setReductionCollectionManager(ReductionCollectionManager collectionManager) {
+      super.setReductionCollectionManager(collectionManager);
+      childPivot.setReductionCollectionManager(collectionManager);
+    }
+
+    @Override
+    public void setExpressionCalculator(ExpressionCalculator expressionCalculator) {
+      super.setExpressionCalculator(expressionCalculator);
+      childPivot.setExpressionCalculator(expressionCalculator);
+    }
+    
+    @Override
+    public void accept(String pivotValue) {
+      PivotDataPair<T> pivotData = currentPivot.get(pivotValue);
+      if (pivotData == null) {
+        pivotData = new PivotDataPair<>();
+        pivotData.childPivots = new HashMap<>();
+        pivotData.pivotReduction = collectionManager.newDataCollectionTarget();
+        currentPivot.put(pivotValue, pivotData);
+      } else {
+        collectionManager.addCollectTarget(pivotData.pivotReduction);
+      }
+      childPivot.addFacetValueCollectionTargets(pivotData.childPivots);
+    }
+
+    @Override
+    protected void importPivotValue(DataInput input, String pivotValue) throws IOException {
+      PivotDataPair<T> pivotData = currentPivot.get(pivotValue);
+      if (pivotData == null) {
+        pivotData = new PivotDataPair<>();
+        pivotData.childPivots = new HashMap<>();
+        pivotData.pivotReduction = collectionManager.newDataCollectionIO();
+        currentPivot.put(pivotValue, pivotData);
+      } else {
+        collectionManager.prepareReductionDataIO(pivotData.pivotReduction);
+      }
+      collectionManager.mergeData();
+      childPivot.importPivot(input, pivotData.childPivots);
+    }
+
+    @Override
+    protected void exportPivotValue(DataOutput output, PivotDataPair<T> pivotData) throws IOException {
+      collectionManager.prepareReductionDataIO(pivotData.pivotReduction);
+      collectionManager.exportData();
+      
+      childPivot.exportPivot(output, pivotData.childPivots);
+    }
+
+    @Override
+    public Iterable<Map<String,Object>> getPivotedResponse(Map<String,PivotDataPair<T>> pivot) {
+      final List<FacetBucket> facetResults = new ArrayList<>();
+      pivot.forEach((facetVal, dataPair) -> {
+        collectionManager.setData(dataPair.pivotReduction);
+        facetResults.add(new FacetBucket(facetVal,expressionCalculator.getResults()));
+      });
+
+      Iterable<FacetBucket> facetResultsIter = applyOptions(facetResults);
+      final LinkedList<Map<String,Object>> results = new LinkedList<>();
+      // Export each expression in the bucket.
+      for (FacetBucket bucket : facetResultsIter) {
+        Map<String, Object> bucketMap = new HashMap<>();
+        bucketMap.put(AnalyticsResponseHeadings.PIVOT_NAME, name);
+        bucketMap.put(AnalyticsResponseHeadings.FACET_VALUE, bucket.getFacetValue());
+        bucketMap.put(AnalyticsResponseHeadings.RESULTS, bucket.getResults());
+        bucketMap.put(AnalyticsResponseHeadings.PIVOT_CHILDREN, childPivot.getPivotedResponse(pivot.get(bucket.getFacetValue()).childPivots));
+        results.add(bucketMap);
+      }
+      return results;
+    }
+    
+    /**
+     * Contains pivot data for {@link PivotNode.PivotBranch} classes.
+     */
+    protected static class PivotDataPair<T> {
+      ReductionDataCollection pivotReduction;
+      Map<String,T> childPivots;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/QueryFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/QueryFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/QueryFacet.java
new file mode 100644
index 0000000..f880809
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/QueryFacet.java
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+import java.util.Map;
+import java.util.function.Consumer;
+
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrException.ErrorCode;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.search.Filter;
+import org.apache.solr.search.QParser;
+
+/**
+ * A facet that breaks down the data by additional Solr Queries.
+ */
+public class QueryFacet extends AbstractSolrQueryFacet {
+  private final Map<String,String> queries;
+  
+  public QueryFacet(String name, Map<String, String> queries) {
+    super(name);
+    this.queries = queries;
+  }
+  
+  @Override
+  public void createFacetValueExecuters(final Filter filter, SolrQueryRequest queryRequest, Consumer<FacetValueQueryExecuter> consumer) {
+    queries.forEach( (queryName, query) -> {
+      final Query q;
+      try {
+        q = QParser.getParser(query, queryRequest).getQuery();
+      } catch( Exception e ){
+        throw new SolrException(ErrorCode.BAD_REQUEST,"Invalid query '"+query+"' in query facet '" + getName() + "'",e);
+      }
+      // The searcher sends docIds to the QueryFacetAccumulator which forwards
+      // them to <code>collectQuery()</code> in this class for collection.
+      Query queryQuery = new BooleanQuery.Builder()
+          .add(q, Occur.MUST)
+          .add(filter, Occur.FILTER)
+          .build();
+
+      ReductionDataCollection dataCol = collectionManager.newDataCollection();
+      reductionData.put(queryName, dataCol);
+      consumer.accept(new FacetValueQueryExecuter(dataCol, queryQuery));
+    });
+  }
+}
\ 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/RangeFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/RangeFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/RangeFacet.java
new file mode 100644
index 0000000..80e8d21
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/RangeFacet.java
@@ -0,0 +1,119 @@
+/*
+ * 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;
+
+import java.util.EnumSet;
+import java.util.List;
+import java.util.function.Consumer;
+
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.analytics.util.FacetRangeGenerator;
+import org.apache.solr.analytics.util.FacetRangeGenerator.FacetRange;
+import org.apache.solr.common.params.FacetParams.FacetRangeInclude;
+import org.apache.solr.common.params.FacetParams.FacetRangeOther;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.search.Filter;
+
+/**
+ * A facet that groups data by a discrete set of ranges.
+ */
+public class RangeFacet extends AbstractSolrQueryFacet {
+  protected final SchemaField field;
+  protected final String start;
+  protected final String end;
+  protected final List<String> gaps;
+  protected boolean hardEnd = false;
+  protected EnumSet<FacetRangeInclude> include;
+  protected EnumSet<FacetRangeOther> others;
+  
+  public RangeFacet(String name, SchemaField field, String start, String end, List<String> gaps) {
+    super(name);
+    this.field = field;
+    this.start = start;
+    this.end = end;
+    this.gaps = gaps;
+    include = EnumSet.of(FacetRangeInclude.LOWER);
+    others = EnumSet.of(FacetRangeOther.NONE);
+  }
+
+  @Override
+  public void createFacetValueExecuters(final Filter filter, SolrQueryRequest queryRequest, Consumer<FacetValueQueryExecuter> consumer) {
+    // Computes the end points of the ranges in the rangeFacet
+    final FacetRangeGenerator<? extends Comparable<?>> rec = FacetRangeGenerator.create(this);
+    final SchemaField sf = field;
+    
+    // Create a rangeFacetAccumulator for each range and 
+    // collect the documents for that range.
+    for (FacetRange range : rec.getRanges()) {
+      Query q = sf.getType().getRangeQuery(null, sf, range.lower, range.upper, range.includeLower,range.includeUpper);
+      // The searcher sends docIds to the RangeFacetAccumulator which forwards
+      // them to <code>collectRange()</code> in this class for collection.
+      Query rangeQuery = new BooleanQuery.Builder()
+          .add(q, Occur.MUST)
+          .add(filter, Occur.FILTER)
+          .build();
+      
+      ReductionDataCollection dataCol = collectionManager.newDataCollection();
+      reductionData.put(range.toString(), dataCol);
+      consumer.accept(new FacetValueQueryExecuter(dataCol, rangeQuery));
+    }
+  }
+
+  public String getStart() {
+    return start;
+  }
+
+  public String getEnd() {
+    return end;
+  }
+
+  public EnumSet<FacetRangeInclude> getInclude() {
+    return include;
+  }
+
+  public void setInclude(EnumSet<FacetRangeInclude> include) {
+    this.include = include;
+  }
+
+  public List<String> getGaps() {
+    return gaps;
+  }
+
+  public boolean isHardEnd() {
+    return hardEnd;
+  }
+
+  public void setHardEnd(boolean hardEnd) {
+    this.hardEnd = hardEnd;
+  }
+
+  public EnumSet<FacetRangeOther> getOthers() {
+    return others;
+  }
+
+  public void setOthers(EnumSet<FacetRangeOther> others) {
+    this.others = others;
+  }
+  
+  public SchemaField getField() {
+    return field;
+  }
+}
\ 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/SortableFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/SortableFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/SortableFacet.java
new file mode 100644
index 0000000..ef1e04b
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/SortableFacet.java
@@ -0,0 +1,178 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.solr.analytics.facet.compare.FacetResultsComparator;
+import org.apache.solr.analytics.util.AnalyticsResponseHeadings;
+import org.apache.solr.common.util.NamedList;
+
+import com.google.common.collect.Iterables;
+
+/**
+ * A facet that can be sorted by either the facet value or an expression value.
+ */
+public abstract class SortableFacet extends AnalyticsFacet {
+  protected FacetSortSpecification sort = null;
+  
+  protected SortableFacet(String name) {
+    super(name);
+  }
+  
+  @Override
+  public NamedList<Object> createOldResponse() {
+    final NamedList<Object> results = new NamedList<>();
+    // Export each expression in the bucket.
+    for (FacetBucket bucket : getBuckets()) {
+      results.add(bucket.getFacetValue(), new NamedList<>(bucket.getResults()));
+    }
+    return results;
+  }
+  
+  @Override
+  public Iterable<Map<String,Object>> createResponse() {
+    final LinkedList<Map<String,Object>> results = new LinkedList<>();
+    // Export each expression in the bucket.
+    for (FacetBucket bucket : getBuckets()) {
+      Map<String, Object> bucketMap = new HashMap<>();
+      bucketMap.put(AnalyticsResponseHeadings.FACET_VALUE, bucket.getFacetValue());
+      bucketMap.put(AnalyticsResponseHeadings.RESULTS, bucket.getResults());
+      results.add(bucketMap);
+    }
+    return results;
+  }
+  
+  private Iterable<FacetBucket> getBuckets() {
+    final List<FacetBucket> facetResults = new ArrayList<>();
+    reductionData.forEach((facetVal, dataCol) -> {
+      collectionManager.setData(dataCol);
+      facetResults.add(new FacetBucket(facetVal,expressionCalculator.getResults()));
+    });
+    
+    return applyOptions(facetResults);
+  }
+
+  /**
+   * Apply the sorting options to the given facet results.
+   * 
+   * @param facetResults to apply sorting options to
+   * @return the sorted results
+   */
+  @SuppressWarnings({"unchecked", "rawtypes"})
+  protected Iterable<FacetBucket> applyOptions(List<FacetBucket> facetResults) {
+    // Sorting the buckets if a sort specification is provided
+    if (sort == null || facetResults.isEmpty()) {
+      return facetResults;
+    }
+    Comparator comp = sort.getComparator();
+    Collections.sort(facetResults, comp);
+    
+    Iterable<FacetBucket> facetResultsIter = facetResults;
+    // apply the limit
+    if (sort.getLimit() > 0) {
+      if (sort.getOffset() > 0) {
+        facetResultsIter = Iterables.skip(facetResultsIter, sort.getOffset());
+      }
+      facetResultsIter = Iterables.limit(facetResultsIter, sort.getLimit());
+    } else if (sort.getLimit() == 0) {
+      return new LinkedList<FacetBucket>();
+    }
+    return facetResultsIter;
+  }
+  
+  /**
+   * Specifies how to sort the buckets of a sortable facet.
+   */
+  public static class FacetSortSpecification {
+    private FacetResultsComparator comparator;
+    protected int limit;
+    protected int offset;
+    
+    public FacetSortSpecification(FacetResultsComparator comparator, int limit, int offset) {
+      this.comparator = comparator;
+      this.limit = limit;
+      this.offset = offset;
+    }
+
+    public FacetResultsComparator getComparator() {
+      return comparator;
+    }
+
+    /**
+     * Get the maximum number of buckets to be returned.
+     * 
+     * @return the limit
+     */
+    public int getLimit() {
+      return limit;
+    }
+    /**
+     * Set the maximum number of buckets to be returned.
+     * 
+     * @param limit the maximum number of buckets
+     */
+    public void setLimit(int limit) {
+      this.limit = limit;
+    }
+    
+    /**
+     * Get the first bucket to return, has to be used with the {@code limit} option.
+     * 
+     * @return the bucket offset
+     */
+    public int getOffset() {
+      return offset;
+    }
+  }
+
+  public SortableFacet.FacetSortSpecification getSort() {
+    return sort;
+  }
+
+  public void setSort(SortableFacet.FacetSortSpecification sort) {
+    this.sort = sort;
+  }
+  
+  public static class FacetBucket {
+    private final String facetValue;
+    private final Map<String,Object> expressionResults;
+    
+    public FacetBucket(String facetValue, Map<String,Object> expressionResults) {
+      this.facetValue = facetValue;
+      this.expressionResults = expressionResults;
+    }
+    
+    public Object getResult(String expression) {
+      return expressionResults.get(expression);
+    }
+    
+    public Map<String,Object> getResults() {
+      return expressionResults;
+    }
+    
+    public String getFacetValue() {
+      return facetValue;
+    }
+  }
+}
\ 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/StreamingFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/StreamingFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/StreamingFacet.java
new file mode 100644
index 0000000..6cca041
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/StreamingFacet.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+import org.apache.solr.analytics.AnalyticsDriver;
+import org.apache.solr.analytics.function.ReductionCollectionManager;
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+
+/**
+ * A facet that is collected during the streaming phase of the {@link AnalyticsDriver}.
+ */
+public interface StreamingFacet {
+  /**
+   * Determine which facet values match the current document. Add the {@link ReductionDataCollection}s of the relevant facet values
+   * to the targets of the streaming {@link ReductionCollectionManager} so that they are updated with the current document's data.
+   */
+  void addFacetValueCollectionTargets();
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/d5963beb/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/ValueFacet.java
----------------------------------------------------------------------
diff --git a/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/ValueFacet.java b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/ValueFacet.java
new file mode 100644
index 0000000..b1d84ba
--- /dev/null
+++ b/solr/contrib/analytics/src/java/org/apache/solr/analytics/facet/ValueFacet.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+import java.util.function.Consumer;
+
+import org.apache.solr.analytics.function.ReductionCollectionManager.ReductionDataCollection;
+import org.apache.solr.analytics.value.StringValueStream;
+
+/**
+ * A facet that breaks up data by the values of a mapping expression or field.
+ * The mapping expression must be castable to a {@link StringValueStream}.
+ */
+public class ValueFacet extends SortableFacet implements StreamingFacet, Consumer<String> {
+  private StringValueStream expression;
+
+  public ValueFacet(String name, StringValueStream expression) {
+    super(name);
+    this.expression = expression;
+  }
+
+  @Override
+  public void addFacetValueCollectionTargets() {
+    expression.streamStrings(this);
+  }
+    
+  @Override
+  public void accept(String t) {
+    ReductionDataCollection collection = reductionData.get(t);
+    if (collection == null) {
+      collection = collectionManager.newDataCollectionTarget();
+      reductionData.put(t, collection);
+    } else {
+      collectionManager.addCollectTarget(collection);
+    }
+  }
+  
+  /**
+   * Get the expression used to create the facet values.
+   * 
+   * @return a string mapping expression
+   */
+  public StringValueStream getExpression() {
+    return expression;
+  }
+}
\ No newline at end of file