You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by sh...@apache.org on 2013/07/29 15:07:31 UTC

svn commit: r1508043 [1/2] - in /lucene/dev/trunk/lucene: ./ demo/src/java/org/apache/lucene/demo/facet/ facet/src/java/org/apache/lucene/facet/associations/ facet/src/java/org/apache/lucene/facet/params/ facet/src/java/org/apache/lucene/facet/range/ f...

Author: shaie
Date: Mon Jul 29 13:07:30 2013
New Revision: 1508043

URL: http://svn.apache.org/r1508043
Log:
LUCENE-4985: Make it easier to mix different kinds of FacetRequests

Added:
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/SumFloatAssociationFacetRequest.java
      - copied, changed from r1508041, lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationFloatSumFacetRequest.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/SumIntAssociationFacetRequest.java
      - copied, changed from r1507930, lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationIntSumFacetRequest.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/MultiFacetsAccumulator.java
      - copied, changed from r1507930, lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetsAccumulatorWrapper.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/MultiFacetsAggregator.java
      - copied, changed from r1507930, lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/MultiAssociationsFacetsAggregator.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/TaxonomyFacetsAccumulator.java
      - copied, changed from r1507698, lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java
Removed:
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationFloatSumFacetRequest.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationIntSumFacetRequest.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/MultiAssociationsFacetsAggregator.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetsAccumulatorWrapper.java
Modified:
    lucene/dev/trunk/lucene/CHANGES.txt
    lucene/dev/trunk/lucene/demo/src/java/org/apache/lucene/demo/facet/AssociationsFacetsExample.java
    lucene/dev/trunk/lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java
    lucene/dev/trunk/lucene/demo/src/java/org/apache/lucene/demo/facet/SimpleSortedSetFacetsExample.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/params/FacetSearchParams.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/range/RangeAccumulator.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetRequest.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/sampling/Sampler.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/CountFacetRequest.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/CountingFacetsAggregator.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/DrillSideways.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetRequest.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetsCollector.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/PerCategoryListAggregator.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/SumScoreFacetRequest.java
    lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesAccumulator.java
    lucene/dev/trunk/lucene/facet/src/test/org/apache/lucene/facet/associations/AssociationsFacetRequestTest.java
    lucene/dev/trunk/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeAccumulator.java
    lucene/dev/trunk/lucene/facet/src/test/org/apache/lucene/facet/search/CountingFacetsAggregatorTest.java
    lucene/dev/trunk/lucene/facet/src/test/org/apache/lucene/facet/search/FacetResultTest.java
    lucene/dev/trunk/lucene/facet/src/test/org/apache/lucene/facet/search/TestDrillSideways.java
    lucene/dev/trunk/lucene/facet/src/test/org/apache/lucene/facet/search/TestFacetsCollector.java
    lucene/dev/trunk/lucene/facet/src/test/org/apache/lucene/facet/sortedset/TestSortedSetDocValuesFacets.java

Modified: lucene/dev/trunk/lucene/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/CHANGES.txt?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/CHANGES.txt (original)
+++ lucene/dev/trunk/lucene/CHANGES.txt Mon Jul 29 13:07:30 2013
@@ -70,6 +70,13 @@ New features
 * LUCENE-5091: SpanNotQuery can now be configured with pre and post slop to act
   as a hypothetical SpanNotNearQuery. (Tim Allison via David Smiley)
 
+* LUCENE-4985: FacetsAccumulator.create() is now able to create a 
+  MultiFacetsAccumulator over a mixed set of facet requests. MultiFacetsAccumulator
+  allows wrapping multiple FacetsAccumulators, allowing to easily mix
+  existing and custom ones. TaxonomyFacetsAccumulator supports any
+  FacetRequest which implements createFacetsAggregator and was indexed
+  using the taxonomy index. (Shai Erera)
+
 Bug Fixes
 
 * LUCENE-5116: IndexWriter.addIndexes(IndexReader...) should drop empty (or all

Modified: lucene/dev/trunk/lucene/demo/src/java/org/apache/lucene/demo/facet/AssociationsFacetsExample.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/demo/src/java/org/apache/lucene/demo/facet/AssociationsFacetsExample.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/demo/src/java/org/apache/lucene/demo/facet/AssociationsFacetsExample.java (original)
+++ lucene/dev/trunk/lucene/demo/src/java/org/apache/lucene/demo/facet/AssociationsFacetsExample.java Mon Jul 29 13:07:30 2013
@@ -1,27 +1,20 @@
 package org.apache.lucene.demo.facet;
 
 import java.io.IOException;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
 import org.apache.lucene.document.Document;
-import org.apache.lucene.facet.associations.AssociationFloatSumFacetRequest;
-import org.apache.lucene.facet.associations.AssociationIntSumFacetRequest;
 import org.apache.lucene.facet.associations.AssociationsFacetFields;
 import org.apache.lucene.facet.associations.CategoryAssociation;
 import org.apache.lucene.facet.associations.CategoryAssociationsContainer;
 import org.apache.lucene.facet.associations.CategoryFloatAssociation;
 import org.apache.lucene.facet.associations.CategoryIntAssociation;
-import org.apache.lucene.facet.associations.MultiAssociationsFacetsAggregator;
-import org.apache.lucene.facet.associations.SumFloatAssociationFacetsAggregator;
-import org.apache.lucene.facet.associations.SumIntAssociationFacetsAggregator;
+import org.apache.lucene.facet.associations.SumFloatAssociationFacetRequest;
+import org.apache.lucene.facet.associations.SumIntAssociationFacetRequest;
 import org.apache.lucene.facet.index.FacetFields;
 import org.apache.lucene.facet.params.FacetSearchParams;
 import org.apache.lucene.facet.search.FacetResult;
-import org.apache.lucene.facet.search.FacetsAccumulator;
-import org.apache.lucene.facet.search.FacetsAggregator;
 import org.apache.lucene.facet.search.FacetsCollector;
 import org.apache.lucene.facet.taxonomy.CategoryPath;
 import org.apache.lucene.facet.taxonomy.TaxonomyReader;
@@ -135,22 +128,9 @@ public class AssociationsFacetsExample {
     
     CategoryPath tags = new CategoryPath("tags");
     CategoryPath genre = new CategoryPath("genre");
-    FacetSearchParams fsp = new FacetSearchParams(
-        new AssociationIntSumFacetRequest(tags, 10), 
-        new AssociationFloatSumFacetRequest(genre, 10));
-  
-    // every category has a different type of association, so use chain their
-    // respective aggregators.
-    final Map<CategoryPath,FacetsAggregator> aggregators = new HashMap<CategoryPath,FacetsAggregator>();
-    aggregators.put(tags, new SumIntAssociationFacetsAggregator());
-    aggregators.put(genre, new SumFloatAssociationFacetsAggregator());
-    FacetsAccumulator fa = new FacetsAccumulator(fsp, indexReader, taxoReader) {
-      @Override
-      public FacetsAggregator getAggregator() {
-        return new MultiAssociationsFacetsAggregator(aggregators);
-      }
-    };
-    FacetsCollector fc = FacetsCollector.create(fa);
+    FacetSearchParams fsp = new FacetSearchParams(new SumIntAssociationFacetRequest(tags, 10), 
+        new SumFloatAssociationFacetRequest(genre, 10));
+    FacetsCollector fc = FacetsCollector.create(fsp, indexReader, taxoReader);
     
     // MatchAllDocsQuery is for "browsing" (counts facets
     // for all non-deleted docs in the index); normally

Modified: lucene/dev/trunk/lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java (original)
+++ lucene/dev/trunk/lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java Mon Jul 29 13:07:30 2013
@@ -27,7 +27,6 @@ import org.apache.lucene.document.Field;
 import org.apache.lucene.document.LongField;
 import org.apache.lucene.document.NumericDocValuesField;
 import org.apache.lucene.facet.params.FacetIndexingParams;
-import org.apache.lucene.facet.params.FacetSearchParams;
 import org.apache.lucene.facet.range.LongRange;
 import org.apache.lucene.facet.range.RangeAccumulator;
 import org.apache.lucene.facet.range.RangeFacetRequest;
@@ -80,13 +79,12 @@ public class RangeFacetsExample implemen
   /** User runs a query and counts facets. */
   public List<FacetResult> search() throws IOException {
 
-    FacetSearchParams fsp = new FacetSearchParams(
-                                new RangeFacetRequest<LongRange>("timestamp",
-                                                                 new LongRange("Past hour", nowSec-3600, true, nowSec, true),
-                                                                 new LongRange("Past six hours", nowSec-6*3600, true, nowSec, true),
-                                                                 new LongRange("Past day", nowSec-24*3600, true, nowSec, true)));
+    RangeFacetRequest<LongRange> rangeFacetRequest = new RangeFacetRequest<LongRange>("timestamp",
+                                     new LongRange("Past hour", nowSec-3600, true, nowSec, true),
+                                     new LongRange("Past six hours", nowSec-6*3600, true, nowSec, true),
+                                     new LongRange("Past day", nowSec-24*3600, true, nowSec, true));
     // Aggregatses the facet counts
-    FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(fsp, searcher.getIndexReader()));
+    FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(rangeFacetRequest));
 
     // MatchAllDocsQuery is for "browsing" (counts facets
     // for all non-deleted docs in the index); normally
@@ -112,6 +110,7 @@ public class RangeFacetsExample implemen
     return searcher.search(q, 10);
   }
 
+  @Override
   public void close() throws IOException {
     searcher.getIndexReader().close();
     indexDir.close();

Modified: lucene/dev/trunk/lucene/demo/src/java/org/apache/lucene/demo/facet/SimpleSortedSetFacetsExample.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/demo/src/java/org/apache/lucene/demo/facet/SimpleSortedSetFacetsExample.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/demo/src/java/org/apache/lucene/demo/facet/SimpleSortedSetFacetsExample.java (original)
+++ lucene/dev/trunk/lucene/demo/src/java/org/apache/lucene/demo/facet/SimpleSortedSetFacetsExample.java Mon Jul 29 13:07:30 2013
@@ -91,7 +91,7 @@ public class SimpleSortedSetFacetsExampl
         new CountFacetRequest(new CategoryPath("Author"), 10));
 
     // Aggregatses the facet counts
-    FacetsCollector fc = FacetsCollector.create(new SortedSetDocValuesAccumulator(fsp, state));
+    FacetsCollector fc = FacetsCollector.create(new SortedSetDocValuesAccumulator(state, fsp));
 
     // MatchAllDocsQuery is for "browsing" (counts facets
     // for all non-deleted docs in the index); normally
@@ -117,7 +117,7 @@ public class SimpleSortedSetFacetsExampl
     FacetSearchParams fsp = new FacetSearchParams(new CountFacetRequest(new CategoryPath("Author"), 10));
     DrillDownQuery q = new DrillDownQuery(fsp.indexingParams, new MatchAllDocsQuery());
     q.add(new CategoryPath("Publish Year/2010", '/'));
-    FacetsCollector fc = FacetsCollector.create(new SortedSetDocValuesAccumulator(fsp, state));
+    FacetsCollector fc = FacetsCollector.create(new SortedSetDocValuesAccumulator(state, fsp));
     searcher.search(q, fc);
 
     // Retrieve results

Copied: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/SumFloatAssociationFacetRequest.java (from r1508041, lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationFloatSumFacetRequest.java)
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/SumFloatAssociationFacetRequest.java?p2=lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/SumFloatAssociationFacetRequest.java&p1=lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationFloatSumFacetRequest.java&r1=1508041&r2=1508043&rev=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationFloatSumFacetRequest.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/SumFloatAssociationFacetRequest.java Mon Jul 29 13:07:30 2013
@@ -1,7 +1,9 @@
 package org.apache.lucene.facet.associations;
 
+import org.apache.lucene.facet.params.FacetIndexingParams;
 import org.apache.lucene.facet.search.FacetArrays;
 import org.apache.lucene.facet.search.FacetRequest;
+import org.apache.lucene.facet.search.FacetsAggregator;
 import org.apache.lucene.facet.taxonomy.CategoryPath;
 
 /*
@@ -27,17 +29,22 @@ import org.apache.lucene.facet.taxonomy.
  * 
  * @lucene.experimental
  */
-public class AssociationFloatSumFacetRequest extends FacetRequest {
+public class SumFloatAssociationFacetRequest extends FacetRequest {
 
   /**
    * Create a float association facet request for a given node in the
    * taxonomy.
    */
-  public AssociationFloatSumFacetRequest(CategoryPath path, int num) {
+  public SumFloatAssociationFacetRequest(CategoryPath path, int num) {
     super(path, num);
   }
 
   @Override
+  public FacetsAggregator createFacetsAggregator(FacetIndexingParams fip) {
+    return new SumFloatAssociationFacetsAggregator();
+  }
+  
+  @Override
   public double getValueOf(FacetArrays arrays, int ordinal) {
     return arrays.getFloatArray()[ordinal];
   }

Copied: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/SumIntAssociationFacetRequest.java (from r1507930, lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationIntSumFacetRequest.java)
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/SumIntAssociationFacetRequest.java?p2=lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/SumIntAssociationFacetRequest.java&p1=lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationIntSumFacetRequest.java&r1=1507930&r2=1508043&rev=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationIntSumFacetRequest.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/SumIntAssociationFacetRequest.java Mon Jul 29 13:07:30 2013
@@ -1,7 +1,9 @@
 package org.apache.lucene.facet.associations;
 
+import org.apache.lucene.facet.params.FacetIndexingParams;
 import org.apache.lucene.facet.search.FacetArrays;
 import org.apache.lucene.facet.search.FacetRequest;
+import org.apache.lucene.facet.search.FacetsAggregator;
 import org.apache.lucene.facet.taxonomy.CategoryPath;
 
 /*
@@ -27,17 +29,22 @@ import org.apache.lucene.facet.taxonomy.
  * 
  * @lucene.experimental
  */
-public class AssociationIntSumFacetRequest extends FacetRequest {
+public class SumIntAssociationFacetRequest extends FacetRequest {
 
   /**
    * Create an integer association facet request for a given node in the
    * taxonomy.
    */
-  public AssociationIntSumFacetRequest(CategoryPath path, int num) {
+  public SumIntAssociationFacetRequest(CategoryPath path, int num) {
     super(path, num);
   }
 
   @Override
+  public FacetsAggregator createFacetsAggregator(FacetIndexingParams fip) {
+    return new SumIntAssociationFacetsAggregator();
+  }
+  
+  @Override
   public FacetArraysSource getFacetArraysSource() {
     return FacetArraysSource.INT;
   }

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/params/FacetSearchParams.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/params/FacetSearchParams.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/params/FacetSearchParams.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/params/FacetSearchParams.java Mon Jul 29 13:07:30 2013
@@ -23,13 +23,9 @@ import org.apache.lucene.facet.search.Fa
  */
 
 /**
- * Defines parameters that are needed for faceted search. The list of
- * {@link FacetRequest facet requests} denotes the facets for which aggregated
- * should be done.
- * <p>
- * One can pass {@link FacetIndexingParams} in order to tell the search code how
- * to read the facets information. Note that you must use the same
- * {@link FacetIndexingParams} that were used for indexing.
+ * Defines parameters that are needed for faceted search: the list of facet
+ * {@link FacetRequest facet requests} which should be aggregated as well as the
+ * {@link FacetIndexingParams indexing params} that were used to index them.
  * 
  * @lucene.experimental
  */

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/range/RangeAccumulator.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/range/RangeAccumulator.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/range/RangeAccumulator.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/range/RangeAccumulator.java Mon Jul 29 13:07:30 2013
@@ -19,6 +19,7 @@ package org.apache.lucene.facet.range;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import org.apache.lucene.facet.params.FacetSearchParams;
@@ -26,10 +27,8 @@ import org.apache.lucene.facet.search.Fa
 import org.apache.lucene.facet.search.FacetResult;
 import org.apache.lucene.facet.search.FacetResultNode;
 import org.apache.lucene.facet.search.FacetsAccumulator;
-import org.apache.lucene.facet.search.FacetsAggregator;
 import org.apache.lucene.facet.search.FacetsCollector.MatchingDocs;
 import org.apache.lucene.facet.taxonomy.CategoryPath;
-import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.NumericDocValues;
 
 /** Uses a {@link NumericDocValues} and accumulates
@@ -51,38 +50,34 @@ public class RangeAccumulator extends Fa
 
   final List<RangeSet> requests = new ArrayList<RangeSet>();
 
-  public RangeAccumulator(FacetSearchParams fsp, IndexReader reader) {
-    super(fsp, reader, null, null);
-
-    for(FacetRequest fr : fsp.facetRequests) {
-
+  public RangeAccumulator(FacetRequest... facetRequests) {
+    this(Arrays.asList(facetRequests));
+  }
+  
+  public RangeAccumulator(List<FacetRequest> facetRequests) {
+    super(new FacetSearchParams(facetRequests));
+    for (FacetRequest fr : facetRequests) {
       if (!(fr instanceof RangeFacetRequest)) {
-        throw new IllegalArgumentException("only RangeFacetRequest is supported; got " + fsp.facetRequests.get(0).getClass());
+        throw new IllegalArgumentException("this accumulator only supports RangeFacetRequest; got " + fr);
       }
 
       if (fr.categoryPath.length != 1) {
         throw new IllegalArgumentException("only flat (dimension only) CategoryPath is allowed");
       }
-
+      
       RangeFacetRequest<?> rfr = (RangeFacetRequest<?>) fr;
-
-      requests.add(new RangeSet(rfr.ranges, rfr.categoryPath.components[0]));
+      requests.add(new RangeSet(rfr.ranges, fr.categoryPath.components[0]));
     }
   }
 
   @Override
-  public FacetsAggregator getAggregator() {
-    throw new UnsupportedOperationException();
-  }
-
-  @Override
   public List<FacetResult> accumulate(List<MatchingDocs> matchingDocs) throws IOException {
 
     // TODO: test if this is faster (in the past it was
     // faster to do MachingDocs on the inside) ... see
     // patches on LUCENE-4965):
     List<FacetResult> results = new ArrayList<FacetResult>();
-    for(int i=0;i<requests.size();i++) {
+    for (int i = 0; i < requests.size(); i++) {
       RangeSet ranges = requests.get(i);
 
       int[] counts = new int[ranges.ranges.length];
@@ -100,7 +95,7 @@ public class RangeAccumulator extends Fa
           // (really, a specialized case of the interval
           // tree)
           // TODO: use interval tree instead of linear search:
-          for(int j=0;j<ranges.ranges.length;j++) {
+          for (int j = 0; j < ranges.ranges.length; j++) {
             if (ranges.ranges[j].accept(v)) {
               counts[j]++;
             }

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetRequest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetRequest.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetRequest.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetRequest.java Mon Jul 29 13:07:30 2013
@@ -19,9 +19,11 @@ package org.apache.lucene.facet.range;
 
 import java.util.List;
 
+import org.apache.lucene.facet.params.FacetIndexingParams;
 import org.apache.lucene.facet.search.Aggregator;
 import org.apache.lucene.facet.search.FacetArrays;
 import org.apache.lucene.facet.search.FacetRequest;
+import org.apache.lucene.facet.search.FacetsAggregator;
 import org.apache.lucene.facet.taxonomy.CategoryPath;
 import org.apache.lucene.facet.taxonomy.TaxonomyReader;
 
@@ -53,6 +55,11 @@ public class RangeFacetRequest<T extends
   }
 
   @Override
+  public FacetsAggregator createFacetsAggregator(FacetIndexingParams fip) {
+    return null;
+  }
+  
+  @Override
   public double getValueOf(FacetArrays arrays, int ordinal) {
     throw new UnsupportedOperationException();
   }

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/sampling/Sampler.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/sampling/Sampler.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/sampling/Sampler.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/sampling/Sampler.java Mon Jul 29 13:07:30 2013
@@ -4,12 +4,14 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.lucene.facet.params.FacetIndexingParams;
 import org.apache.lucene.facet.params.FacetSearchParams;
 import org.apache.lucene.facet.search.Aggregator;
 import org.apache.lucene.facet.search.FacetArrays;
 import org.apache.lucene.facet.search.FacetRequest;
 import org.apache.lucene.facet.search.FacetResult;
 import org.apache.lucene.facet.search.FacetResultNode;
+import org.apache.lucene.facet.search.FacetsAggregator;
 import org.apache.lucene.facet.search.ScoredDocIDs;
 import org.apache.lucene.facet.taxonomy.TaxonomyReader;
 
@@ -215,6 +217,11 @@ public abstract class Sampler {
     }
     
     @Override
+    public FacetsAggregator createFacetsAggregator(FacetIndexingParams fip) {
+      return orig.createFacetsAggregator(fip);
+    }
+    
+    @Override
     public Aggregator createAggregator(boolean useComplements, FacetArrays arrays, TaxonomyReader taxonomy) 
         throws IOException {
       return orig.createAggregator(useComplements, arrays, taxonomy);

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/CountFacetRequest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/CountFacetRequest.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/CountFacetRequest.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/CountFacetRequest.java Mon Jul 29 13:07:30 2013
@@ -1,6 +1,7 @@
 package org.apache.lucene.facet.search;
 
 import org.apache.lucene.facet.complements.ComplementCountingAggregator;
+import org.apache.lucene.facet.params.FacetIndexingParams;
 import org.apache.lucene.facet.taxonomy.CategoryPath;
 import org.apache.lucene.facet.taxonomy.TaxonomyReader;
 
@@ -32,6 +33,7 @@ public class CountFacetRequest extends F
     super(path, num);
   }
 
+  // TODO nuke Aggregator and move this logic to StandardFacetsAccumulator -- it should only be used for counting
   @Override
   public Aggregator createAggregator(boolean useComplements, FacetArrays arrays, TaxonomyReader taxonomy) {
     // we rely on that, if needed, result is cleared by arrays!
@@ -43,6 +45,11 @@ public class CountFacetRequest extends F
   }
 
   @Override
+  public FacetsAggregator createFacetsAggregator(FacetIndexingParams fip) {
+    return CountingFacetsAggregator.create(fip.getCategoryListParams(categoryPath));
+  }
+  
+  @Override
   public double getValueOf(FacetArrays arrays, int ordinal) {
     return arrays.getIntArray()[ordinal];
   }

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/CountingFacetsAggregator.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/CountingFacetsAggregator.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/CountingFacetsAggregator.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/CountingFacetsAggregator.java Mon Jul 29 13:07:30 2013
@@ -2,6 +2,7 @@ package org.apache.lucene.facet.search;
 
 import java.io.IOException;
 
+import org.apache.lucene.facet.encoding.DGapVInt8IntDecoder;
 import org.apache.lucene.facet.params.CategoryListParams;
 import org.apache.lucene.facet.search.FacetsCollector.MatchingDocs;
 import org.apache.lucene.util.IntsRef;
@@ -34,6 +35,18 @@ import org.apache.lucene.util.IntsRef;
  */
 public class CountingFacetsAggregator extends IntRollupFacetsAggregator {
   
+  /**
+   * Returns a {@link FacetsAggregator} suitable for counting categories given
+   * the {@link CategoryListParams}.
+   */
+  public static FacetsAggregator create(CategoryListParams clp) {
+    if (clp.createEncoder().createMatchingDecoder().getClass() == DGapVInt8IntDecoder.class) {
+      return new FastCountingFacetsAggregator();
+    } else {
+      return new CountingFacetsAggregator();
+    }
+  }
+
   private final IntsRef ordinals = new IntsRef(32);
   
   @Override

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/DrillSideways.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/DrillSideways.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/DrillSideways.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/DrillSideways.java Mon Jul 29 13:07:30 2013
@@ -25,7 +25,10 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.lucene.facet.index.FacetFields;
 import org.apache.lucene.facet.params.FacetSearchParams;
+import org.apache.lucene.facet.sortedset.SortedSetDocValuesFacetFields;
+import org.apache.lucene.facet.sortedset.SortedSetDocValuesReaderState;
 import org.apache.lucene.facet.taxonomy.TaxonomyReader;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.Term;
@@ -70,11 +73,26 @@ public class DrillSideways {
 
   protected final IndexSearcher searcher;
   protected final TaxonomyReader taxoReader;
-
-  /** Create a new {@code DrillSideways} instance. */
+  protected final SortedSetDocValuesReaderState state;
+  
+  /**
+   * Create a new {@code DrillSideways} instance, assuming the categories were
+   * indexed with {@link FacetFields}.
+   */
   public DrillSideways(IndexSearcher searcher, TaxonomyReader taxoReader) {
     this.searcher = searcher;
     this.taxoReader = taxoReader;
+    this.state = null;
+  }
+  
+  /**
+   * Create a new {@code DrillSideways} instance, assuming the categories were
+   * indexed with {@link SortedSetDocValuesFacetFields}.
+   */
+  public DrillSideways(IndexSearcher searcher, SortedSetDocValuesReaderState state) {
+    this.searcher = searcher;
+    this.taxoReader = null;
+    this.state = state;
   }
 
   /** Moves any drill-downs that don't have a corresponding
@@ -440,13 +458,21 @@ public class DrillSideways {
   /** Override this to use a custom drill-down {@link
    *  FacetsAccumulator}. */
   protected FacetsAccumulator getDrillDownAccumulator(FacetSearchParams fsp) throws IOException {
-    return FacetsAccumulator.create(fsp, searcher.getIndexReader(), taxoReader);
+    if (taxoReader != null) {
+      return FacetsAccumulator.create(fsp, searcher.getIndexReader(), taxoReader, null);
+    } else {
+      return FacetsAccumulator.create(fsp, state, null);
+    }
   }
 
   /** Override this to use a custom drill-sideways {@link
    *  FacetsAccumulator}. */
   protected FacetsAccumulator getDrillSidewaysAccumulator(String dim, FacetSearchParams fsp) throws IOException {
-    return FacetsAccumulator.create(fsp, searcher.getIndexReader(), taxoReader);
+    if (taxoReader != null) {
+      return FacetsAccumulator.create(fsp, searcher.getIndexReader(), taxoReader, null);
+    } else {
+      return FacetsAccumulator.create(fsp, state, null);
+    }
   }
 
   /** Override this and return true if your collector

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetRequest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetRequest.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetRequest.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetRequest.java Mon Jul 29 13:07:30 2013
@@ -3,6 +3,8 @@ package org.apache.lucene.facet.search;
 import java.io.IOException;
 
 import org.apache.lucene.facet.params.CategoryListParams.OrdinalPolicy;
+import org.apache.lucene.facet.params.FacetIndexingParams;
+import org.apache.lucene.facet.range.RangeFacetRequest;
 import org.apache.lucene.facet.taxonomy.CategoryPath;
 import org.apache.lucene.facet.taxonomy.TaxonomyReader;
 
@@ -139,6 +141,15 @@ public abstract class FacetRequest {
         "you should override FacetsAccumulator to return the proper FacetsAggregator");
   }
   
+  /**
+   * Returns the {@link FacetsAggregator} which can aggregate the categories of
+   * this facet request. The aggregator is expected to aggregate category values
+   * into {@link FacetArrays}. If the facet request does not support that, e.g.
+   * {@link RangeFacetRequest}, it can return {@code null}. Note though that
+   * such requests require a dedicated {@link FacetsAccumulator}.
+   */
+  public abstract FacetsAggregator createFacetsAggregator(FacetIndexingParams fip);
+  
   @Override
   public boolean equals(Object o) {
     if (o instanceof FacetRequest) {

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java Mon Jul 29 13:07:30 2013
@@ -7,6 +7,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.lucene.facet.params.FacetIndexingParams;
 import org.apache.lucene.facet.taxonomy.CategoryPath;
 import org.apache.lucene.facet.taxonomy.TaxonomyReader;
 import org.apache.lucene.util.CollectionUtil;
@@ -153,6 +154,11 @@ public class FacetResult {
         }
         FacetRequest dummy = new FacetRequest(min, frs.get(0).getFacetRequest().numResults) {
           @Override
+          public FacetsAggregator createFacetsAggregator(FacetIndexingParams fip) {
+            throw new UnsupportedOperationException("not supported by this request");
+          }
+          
+          @Override
           public double getValueOf(FacetArrays arrays, int idx) {
             throw new UnsupportedOperationException("not supported by this request");
           }

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java Mon Jul 29 13:07:30 2013
@@ -2,20 +2,15 @@ package org.apache.lucene.facet.search;
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
-import org.apache.lucene.facet.encoding.DGapVInt8IntDecoder;
-import org.apache.lucene.facet.params.CategoryListParams;
+import org.apache.lucene.facet.params.FacetIndexingParams;
 import org.apache.lucene.facet.params.FacetSearchParams;
-import org.apache.lucene.facet.params.CategoryListParams.OrdinalPolicy;
-import org.apache.lucene.facet.search.FacetRequest.FacetArraysSource;
-import org.apache.lucene.facet.search.FacetRequest.ResultMode;
-import org.apache.lucene.facet.search.FacetRequest.SortOrder;
+import org.apache.lucene.facet.range.RangeAccumulator;
+import org.apache.lucene.facet.range.RangeFacetRequest;
 import org.apache.lucene.facet.search.FacetsCollector.MatchingDocs;
-import org.apache.lucene.facet.taxonomy.ParallelTaxonomyArrays;
+import org.apache.lucene.facet.sortedset.SortedSetDocValuesAccumulator;
+import org.apache.lucene.facet.sortedset.SortedSetDocValuesReaderState;
 import org.apache.lucene.facet.taxonomy.TaxonomyReader;
 import org.apache.lucene.index.IndexReader;
 
@@ -37,48 +32,117 @@ import org.apache.lucene.index.IndexRead
  */
 
 /**
- * Driver for Accumulating facets of faceted search requests over given
- * documents.
+ * Accumulates the facets defined in the {@link FacetSearchParams}.
  * 
  * @lucene.experimental
  */
-public class FacetsAccumulator {
+public abstract class FacetsAccumulator {
 
-  public final TaxonomyReader taxonomyReader;
-  public final IndexReader indexReader;
-  public final FacetArrays facetArrays;
-  public FacetSearchParams searchParams;
+  // TODO this should be final, but currently SamplingAccumulator modifies the params.
+  // need to review the class and if it's resolved, make it final
+  public /*final*/ FacetSearchParams searchParams;
+
+  /** Constructor with the given search params. */
+  protected FacetsAccumulator(FacetSearchParams fsp) {
+    this.searchParams = fsp;
+  }
 
   /**
-   * Initializes the accumulator with the given search params, index reader and
-   * taxonomy reader. This constructor creates the default {@link FacetArrays},
-   * which do not support reuse. If you want to use {@link ReusingFacetArrays},
-   * you should use the
-   * {@link #FacetsAccumulator(FacetSearchParams, IndexReader, TaxonomyReader, FacetArrays)}
-   * constructor.
+   * Creates a {@link FacetsAccumulator} for the given facet requests. This
+   * method supports {@link RangeAccumulator} and
+   * {@link TaxonomyFacetsAccumulator} by dividing the facet requests into
+   * {@link RangeFacetRequest} and the rest.
+   * <p>
+   * If both types of facet requests are used, it returns a
+   * {@link MultiFacetsAccumulator} and the facet results returned from
+   * {@link #accumulate(List)} may not be in the same order as the given facet
+   * requests.
+   * 
+   * @param fsp
+   *          the search params define the facet requests and the
+   *          {@link FacetIndexingParams}
+   * @param indexReader
+   *          the {@link IndexReader} used for search
+   * @param taxoReader
+   *          the {@link TaxonomyReader} used for search
+   * @param arrays
+   *          the {@link FacetArrays} which the accumulator should use to store
+   *          the categories weights in. Can be {@code null}.
    */
-  public FacetsAccumulator(FacetSearchParams searchParams, IndexReader indexReader, TaxonomyReader taxonomyReader) {
-    this(searchParams, indexReader, taxonomyReader, new FacetArrays(taxonomyReader.getSize()));
-  }
+  public static FacetsAccumulator create(FacetSearchParams fsp, IndexReader indexReader, TaxonomyReader taxoReader, 
+      FacetArrays arrays) {
+    if (fsp.indexingParams.getPartitionSize() != Integer.MAX_VALUE) {
+      return new StandardFacetsAccumulator(fsp, indexReader, taxoReader, arrays);
+    }
+    
+    List<FacetRequest> rangeRequests = new ArrayList<FacetRequest>();
+    List<FacetRequest> nonRangeRequests = new ArrayList<FacetRequest>();
+    for (FacetRequest fr : fsp.facetRequests) {
+      if (fr instanceof RangeFacetRequest) {
+        rangeRequests.add(fr);
+      } else {
+        nonRangeRequests.add(fr);
+      }
+    }
 
+    if (rangeRequests.isEmpty()) {
+      return new TaxonomyFacetsAccumulator(fsp, indexReader, taxoReader, arrays);
+    } else if (nonRangeRequests.isEmpty()) {
+      return new RangeAccumulator(rangeRequests);
+    } else {
+      FacetSearchParams searchParams = new FacetSearchParams(fsp.indexingParams, nonRangeRequests);
+      FacetsAccumulator accumulator = new TaxonomyFacetsAccumulator(searchParams, indexReader, taxoReader, arrays);
+      RangeAccumulator rangeAccumulator = new RangeAccumulator(rangeRequests);
+      return MultiFacetsAccumulator.wrap(accumulator, rangeAccumulator);
+    }
+  }
+  
   /**
-   * Creates an appropriate {@link FacetsAccumulator},
-   * returning {@link FacetsAccumulator} when all requests
-   * are {@link CountFacetRequest} and only one partition is
-   * in use, otherwise {@link StandardFacetsAccumulator}.
+   * Creates a {@link FacetsAccumulator} for the given facet requests. This
+   * method supports {@link RangeAccumulator} and
+   * {@link SortedSetDocValuesAccumulator} by dividing the facet requests into
+   * {@link RangeFacetRequest} and the rest.
+   * <p>
+   * If both types of facet requests are used, it returns a
+   * {@link MultiFacetsAccumulator} and the facet results returned from
+   * {@link #accumulate(List)} may not be in the same order as the given facet
+   * requests.
+   * 
+   * @param fsp
+   *          the search params define the facet requests and the
+   *          {@link FacetIndexingParams}
+   * @param state
+   *          the {@link SortedSetDocValuesReaderState} needed for accumulating
+   *          the categories
+   * @param arrays
+   *          the {@link FacetArrays} which the accumulator should use to
+   *          store the categories weights in. Can be {@code null}.
    */
-  public static FacetsAccumulator create(FacetSearchParams fsp, IndexReader indexReader, TaxonomyReader taxoReader) {
+  public static FacetsAccumulator create(FacetSearchParams fsp, SortedSetDocValuesReaderState state, FacetArrays arrays) throws IOException {
     if (fsp.indexingParams.getPartitionSize() != Integer.MAX_VALUE) {
-      return new StandardFacetsAccumulator(fsp, indexReader, taxoReader);
+      throw new IllegalArgumentException("only default partition size is supported by this method: " + fsp.indexingParams.getPartitionSize());
     }
     
+    List<FacetRequest> rangeRequests = new ArrayList<FacetRequest>();
+    List<FacetRequest> nonRangeRequests = new ArrayList<FacetRequest>();
     for (FacetRequest fr : fsp.facetRequests) {
-      if (!(fr instanceof CountFacetRequest)) {
-        return new StandardFacetsAccumulator(fsp, indexReader, taxoReader);
+      if (fr instanceof RangeFacetRequest) {
+        rangeRequests.add(fr);
+      } else {
+        nonRangeRequests.add(fr);
       }
     }
     
-    return new FacetsAccumulator(fsp, indexReader, taxoReader);
+    if (rangeRequests.isEmpty()) {
+      return new SortedSetDocValuesAccumulator(state, fsp, arrays);
+    } else if (nonRangeRequests.isEmpty()) {
+      return new RangeAccumulator(rangeRequests);
+    } else {
+      FacetSearchParams searchParams = new FacetSearchParams(fsp.indexingParams, nonRangeRequests);
+      FacetsAccumulator accumulator = new SortedSetDocValuesAccumulator(state, searchParams, arrays);
+      RangeAccumulator rangeAccumulator = new RangeAccumulator(rangeRequests);
+      return MultiFacetsAccumulator.wrap(accumulator, rangeAccumulator);
+    }
   }
   
   /** Returns an empty {@link FacetResult}. */
@@ -89,69 +153,6 @@ public class FacetsAccumulator {
   }
   
   /**
-   * Initializes the accumulator with the given parameters as well as
-   * {@link FacetArrays}. Note that the accumulator doesn't call
-   * {@link FacetArrays#free()}. If you require that (only makes sense if you
-   * use {@link ReusingFacetArrays}, you should do it after you've finished with
-   * the accumulator.
-   */
-  public FacetsAccumulator(FacetSearchParams searchParams, IndexReader indexReader, TaxonomyReader taxonomyReader, 
-      FacetArrays facetArrays) {
-    this.facetArrays = facetArrays;
-    this.indexReader = indexReader;
-    this.taxonomyReader = taxonomyReader;
-    this.searchParams = searchParams;
-  }
-  
-  /**
-   * Returns the {@link FacetsAggregator} to use for aggregating the categories
-   * found in the result documents. The default implementation returns
-   * {@link CountingFacetsAggregator}, or {@link FastCountingFacetsAggregator}
-   * if all categories can be decoded with {@link DGapVInt8IntDecoder}.
-   */
-  public FacetsAggregator getAggregator() {
-    if (FastCountingFacetsAggregator.verifySearchParams(searchParams)) {
-      return new FastCountingFacetsAggregator();
-    } else {
-      return new CountingFacetsAggregator();
-    }
-  }
-  
-  /**
-   * Creates a {@link FacetResultsHandler} that matches the given
-   * {@link FacetRequest}.
-   */
-  protected FacetResultsHandler createFacetResultsHandler(FacetRequest fr) {
-    if (fr.getDepth() == 1 && fr.getSortOrder() == SortOrder.DESCENDING) {
-      FacetArraysSource fas = fr.getFacetArraysSource();
-      if (fas == FacetArraysSource.INT) {
-        return new IntFacetResultsHandler(taxonomyReader, fr, facetArrays);
-      }
-      
-      if (fas == FacetArraysSource.FLOAT) {
-        return new FloatFacetResultsHandler(taxonomyReader, fr, facetArrays);
-      }
-    }
-
-    if (fr.getResultMode() == ResultMode.PER_NODE_IN_TREE) {
-      return new TopKInEachNodeHandler(taxonomyReader, fr, facetArrays);
-    } 
-    return new TopKFacetResultsHandler(taxonomyReader, fr, facetArrays);
-  }
-
-  protected Set<CategoryListParams> getCategoryLists() {
-    if (searchParams.indexingParams.getAllCategoryListParams().size() == 1) {
-      return Collections.singleton(searchParams.indexingParams.getCategoryListParams(null));
-    }
-    
-    HashSet<CategoryListParams> clps = new HashSet<CategoryListParams>();
-    for (FacetRequest fr : searchParams.facetRequests) {
-      clps.add(searchParams.indexingParams.getCategoryListParams(fr.categoryPath));
-    }
-    return clps;
-  }
-
-  /**
    * Used by {@link FacetsCollector} to build the list of {@link FacetResult
    * facet results} that match the {@link FacetRequest facet requests} that were
    * given in the constructor.
@@ -159,44 +160,12 @@ public class FacetsAccumulator {
    * @param matchingDocs
    *          the documents that matched the query, per-segment.
    */
-  public List<FacetResult> accumulate(List<MatchingDocs> matchingDocs) throws IOException {
-    // aggregate facets per category list (usually onle one category list)
-    FacetsAggregator aggregator = getAggregator();
-    for (CategoryListParams clp : getCategoryLists()) {
-      for (MatchingDocs md : matchingDocs) {
-        aggregator.aggregate(md, clp, facetArrays);
-      }
-    }
-    
-    ParallelTaxonomyArrays arrays = taxonomyReader.getParallelTaxonomyArrays();
-    
-    // compute top-K
-    final int[] children = arrays.children();
-    final int[] siblings = arrays.siblings();
-    List<FacetResult> res = new ArrayList<FacetResult>();
-    for (FacetRequest fr : searchParams.facetRequests) {
-      int rootOrd = taxonomyReader.getOrdinal(fr.categoryPath);
-      if (rootOrd == TaxonomyReader.INVALID_ORDINAL) { // category does not exist
-        // Add empty FacetResult
-        res.add(emptyResult(rootOrd, fr));
-        continue;
-      }
-      CategoryListParams clp = searchParams.indexingParams.getCategoryListParams(fr.categoryPath);
-      if (fr.categoryPath.length > 0) { // someone might ask to aggregate the ROOT category
-        OrdinalPolicy ordinalPolicy = clp.getOrdinalPolicy(fr.categoryPath.components[0]);
-        if (ordinalPolicy == OrdinalPolicy.NO_PARENTS) {
-          // rollup values
-          aggregator.rollupValues(fr, rootOrd, children, siblings, facetArrays);
-        }
-      }
-      
-      FacetResultsHandler frh = createFacetResultsHandler(fr);
-      res.add(frh.compute());
-    }
-    return res;
-  }
+  public abstract List<FacetResult> accumulate(List<MatchingDocs> matchingDocs) throws IOException;
 
-  public boolean requiresDocScores() {
-    return getAggregator().requiresDocScores();
-  }
+  /**
+   * Used by {@link FacetsCollector} to determine if document scores need to be
+   * collected in addition to matching documents.
+   */
+  public abstract boolean requiresDocScores();
+  
 }

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetsCollector.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetsCollector.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetsCollector.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetsCollector.java Mon Jul 29 13:07:30 2013
@@ -167,7 +167,7 @@ public abstract class FacetsCollector ex
    * FacetsAccumulator} from {@link FacetsAccumulator#create}.
    */
   public static FacetsCollector create(FacetSearchParams fsp, IndexReader indexReader, TaxonomyReader taxoReader) {
-    return create(FacetsAccumulator.create(fsp, indexReader, taxoReader));
+    return create(FacetsAccumulator.create(fsp, indexReader, taxoReader, null));
   }
 
   /**

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java Mon Jul 29 13:07:30 2013
@@ -5,7 +5,6 @@ import java.io.IOException;
 import org.apache.lucene.facet.encoding.DGapVInt8IntDecoder;
 import org.apache.lucene.facet.encoding.DGapVInt8IntEncoder;
 import org.apache.lucene.facet.params.CategoryListParams;
-import org.apache.lucene.facet.params.FacetSearchParams;
 import org.apache.lucene.facet.search.FacetsCollector.MatchingDocs;
 import org.apache.lucene.index.BinaryDocValues;
 import org.apache.lucene.util.BytesRef;
@@ -40,23 +39,6 @@ public final class FastCountingFacetsAgg
   
   private final BytesRef buf = new BytesRef(32);
   
-  /**
-   * Asserts that this {@link FacetsCollector} can handle the given
-   * {@link FacetSearchParams}. Returns {@code null} if true, otherwise an error
-   * message.
-   */
-  final static boolean verifySearchParams(FacetSearchParams fsp) {
-    // verify that all category lists were encoded with DGapVInt
-    for (FacetRequest fr : fsp.facetRequests) {
-      CategoryListParams clp = fsp.indexingParams.getCategoryListParams(fr.categoryPath);
-      if (clp.createEncoder().createMatchingDecoder().getClass() != DGapVInt8IntDecoder.class) {
-        return false;
-      }
-    }
-    
-    return true;
-  }
-
   @Override
   public final void aggregate(MatchingDocs matchingDocs, CategoryListParams clp, FacetArrays facetArrays) 
       throws IOException {

Copied: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/MultiFacetsAccumulator.java (from r1507930, lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetsAccumulatorWrapper.java)
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/MultiFacetsAccumulator.java?p2=lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/MultiFacetsAccumulator.java&p1=lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetsAccumulatorWrapper.java&r1=1507930&r2=1508043&rev=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetsAccumulatorWrapper.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/MultiFacetsAccumulator.java Mon Jul 29 13:07:30 2013
@@ -1,4 +1,4 @@
-package org.apache.lucene.facet.range;
+package org.apache.lucene.facet.search;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
@@ -20,98 +20,50 @@ package org.apache.lucene.facet.range;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Set;
 
-import org.apache.lucene.facet.params.CategoryListParams;
 import org.apache.lucene.facet.params.FacetSearchParams;
-import org.apache.lucene.facet.search.FacetArrays;
-import org.apache.lucene.facet.search.FacetRequest;
 import org.apache.lucene.facet.search.FacetResult;
-import org.apache.lucene.facet.search.FacetResultsHandler;
 import org.apache.lucene.facet.search.FacetsAccumulator;
-import org.apache.lucene.facet.search.FacetsAggregator;
 import org.apache.lucene.facet.search.FacetsCollector.MatchingDocs;
-import org.apache.lucene.facet.taxonomy.TaxonomyReader;
-import org.apache.lucene.index.IndexReader;
 
-/** Takes multiple facet requests and if necessary splits
- *  them between the normal {@link FacetsAccumulator} and a
- *  {@link RangeAccumulator} */
-public class RangeFacetsAccumulatorWrapper extends FacetsAccumulator {
-  // TODO: somehow handle SortedSetDVAccumulator as
-  // well... but it's tricky because SSDV just uses an
-  // "ordinary" flat CountFacetRequest so we can't switch
-  // based on that.
-  private final FacetsAccumulator accumulator;
-  private final RangeAccumulator rangeAccumulator;
-
-  public static FacetsAccumulator create(FacetSearchParams fsp, IndexReader indexReader, TaxonomyReader taxoReader) {
-    return create(fsp, indexReader, taxoReader, new FacetArrays(taxoReader.getSize()));
-  }
-
-  public static FacetsAccumulator create(FacetSearchParams fsp, IndexReader indexReader, TaxonomyReader taxoReader, FacetArrays arrays) {
-    List<FacetRequest> rangeRequests = new ArrayList<FacetRequest>();
-    List<FacetRequest> nonRangeRequests = new ArrayList<FacetRequest>();
-    for(FacetRequest fr : fsp.facetRequests) {
-      if (fr instanceof RangeFacetRequest) {
-        rangeRequests.add(fr);
-      } else {
-        nonRangeRequests.add(fr);
-      }
-    }
-
-    if (rangeRequests.isEmpty()) {
-      return new FacetsAccumulator(fsp, indexReader, taxoReader, arrays);
-    } else if (nonRangeRequests.isEmpty()) {
-      return new RangeAccumulator(fsp, indexReader);
+/**
+ * Wraps multiple {@link FacetsAccumulator} and returns a merged list of
+ * {@link FacetResult}, in the order the accumulators were given.
+ */
+public class MultiFacetsAccumulator extends FacetsAccumulator {
+  
+  private final FacetsAccumulator[] accumulators;
+  
+  /** Wraps the given {@link FacetsAccumulator accumulators}. */
+  public static FacetsAccumulator wrap(FacetsAccumulator... accumulators) {
+    if (accumulators.length == 0) {
+      return accumulators[0];
     } else {
-      FacetsAccumulator accumulator = new FacetsAccumulator(new FacetSearchParams(fsp.indexingParams, nonRangeRequests), indexReader, taxoReader, arrays);
-      RangeAccumulator rangeAccumulator = new RangeAccumulator(new FacetSearchParams(fsp.indexingParams, rangeRequests), indexReader);
-      return new RangeFacetsAccumulatorWrapper(accumulator, rangeAccumulator, fsp);
+      return new MultiFacetsAccumulator(accumulators);
     }
   }
 
-  private RangeFacetsAccumulatorWrapper(FacetsAccumulator accumulator, RangeAccumulator rangeAccumulator, FacetSearchParams fsp) {
-    super(fsp, accumulator.indexReader, accumulator.taxonomyReader);
-    this.accumulator = accumulator;
-    this.rangeAccumulator = rangeAccumulator;
-  }
-
-  @Override
-  public FacetsAggregator getAggregator() {
-    throw new UnsupportedOperationException();
-  }
-
-  @Override
-  protected FacetResultsHandler createFacetResultsHandler(FacetRequest fr) {
-    throw new UnsupportedOperationException();
-  }
-
-  @Override
-  protected Set<CategoryListParams> getCategoryLists() {
-    throw new UnsupportedOperationException();
+  private MultiFacetsAccumulator(FacetsAccumulator... accumulators) {
+    super((FacetSearchParams) null);
+    this.accumulators = accumulators;
   }
 
   @Override
   public boolean requiresDocScores() {
-    return accumulator.requiresDocScores();
+    for (FacetsAccumulator fa : accumulators) {
+      if (fa.requiresDocScores()) {
+        return true;
+      }
+    }
+    return false;
   }
 
+  @Override
   public List<FacetResult> accumulate(List<MatchingDocs> matchingDocs) throws IOException {
-    List<FacetResult> results = accumulator.accumulate(matchingDocs);
-    List<FacetResult> rangeResults = rangeAccumulator.accumulate(matchingDocs);
-
-    int aUpto = 0;
-    int raUpto = 0;
     List<FacetResult> merged = new ArrayList<FacetResult>();
-    for(FacetRequest fr : searchParams.facetRequests) {
-      if (fr instanceof RangeFacetRequest) {
-        merged.add(rangeResults.get(raUpto++));
-      } else {
-        merged.add(results.get(aUpto++));
-      }
+    for (FacetsAccumulator fa : accumulators) {
+      merged.addAll(fa.accumulate(matchingDocs));
     }
-
     return merged;
   }
 }

Copied: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/MultiFacetsAggregator.java (from r1507930, lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/MultiAssociationsFacetsAggregator.java)
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/MultiFacetsAggregator.java?p2=lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/MultiFacetsAggregator.java&p1=lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/MultiAssociationsFacetsAggregator.java&r1=1507930&r2=1508043&rev=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/associations/MultiAssociationsFacetsAggregator.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/MultiFacetsAggregator.java Mon Jul 29 13:07:30 2013
@@ -1,4 +1,4 @@
-package org.apache.lucene.facet.associations;
+package org.apache.lucene.facet.search;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -39,22 +39,21 @@ import org.apache.lucene.facet.taxonomy.
  * 
  * @lucene.experimental
  */
-public class MultiAssociationsFacetsAggregator implements FacetsAggregator {
+public class MultiFacetsAggregator implements FacetsAggregator {
   
   private final Map<CategoryPath,FacetsAggregator> categoryAggregators;
   private final List<FacetsAggregator> aggregators;
   
   /**
-   * Creates a new {@link MultiAssociationsFacetsAggregator} over the given
-   * aggregators. The mapping is used by
-   * {@link #rollupValues(FacetRequest, int, int[], int[], FacetArrays)} to
-   * rollup the values of the specific category by the corresponding
-   * {@link FacetsAggregator}. However, since each {@link FacetsAggregator}
-   * handles the associations of a specific type, which could cover multiple
-   * categories, the aggregation is done on the unique set of aggregators, which
-   * are identified by their class.
+   * Constructor.
+   * <p>
+   * The mapping is used to rollup the values of the specific category by the
+   * corresponding {@link FacetsAggregator}. It is ok to pass differnet
+   * {@link FacetsAggregator} instances for each {@link CategoryPath} - the
+   * constructor ensures that each aggregator <u>type</u> (determined by its
+   * class) is invoked only once.
    */
-  public MultiAssociationsFacetsAggregator(Map<CategoryPath,FacetsAggregator> aggregators) {
+  public MultiFacetsAggregator(Map<CategoryPath,FacetsAggregator> aggregators) {
     this.categoryAggregators = aggregators;
     
     // make sure that each FacetsAggregator class is invoked only once, or

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/PerCategoryListAggregator.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/PerCategoryListAggregator.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/PerCategoryListAggregator.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/PerCategoryListAggregator.java Mon Jul 29 13:07:30 2013
@@ -27,6 +27,9 @@ import org.apache.lucene.facet.search.Fa
 /**
  * A {@link FacetsAggregator} which invokes the proper aggregator per
  * {@link CategoryListParams}.
+ * {@link #rollupValues(FacetRequest, int, int[], int[], FacetArrays)} is
+ * delegated to the proper aggregator which handles the
+ * {@link CategoryListParams} the given {@link FacetRequest} belongs to.
  */
 public class PerCategoryListAggregator implements FacetsAggregator {
   

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java Mon Jul 29 13:07:30 2013
@@ -43,7 +43,7 @@ import org.apache.lucene.util.IntsRef;
  */
 
 /**
- * Standard implementation for {@link FacetsAccumulator}, utilizing partitions to save on memory.
+ * Standard implementation for {@link TaxonomyFacetsAccumulator}, utilizing partitions to save on memory.
  * <p>
  * Why partitions? Because if there are say 100M categories out of which 
  * only top K are required, we must first compute value for all 100M categories
@@ -64,7 +64,7 @@ import org.apache.lucene.util.IntsRef;
  * 
  * @lucene.experimental
  */
-public class StandardFacetsAccumulator extends FacetsAccumulator {
+public class StandardFacetsAccumulator extends TaxonomyFacetsAccumulator {
 
   private static final Logger logger = Logger.getLogger(StandardFacetsAccumulator.class.getName());
 
@@ -96,15 +96,18 @@ public class StandardFacetsAccumulator e
 
   private double complementThreshold = DEFAULT_COMPLEMENT_THRESHOLD;
   
-  public StandardFacetsAccumulator(FacetSearchParams searchParams, IndexReader indexReader, 
+  private static FacetArrays createFacetArrays(FacetSearchParams searchParams, TaxonomyReader taxoReader) {
+    return new FacetArrays(PartitionsUtils.partitionSize(searchParams.indexingParams, taxoReader)); 
+  }
+  
+  public StandardFacetsAccumulator(FacetSearchParams searchParams, IndexReader indexReader,  
       TaxonomyReader taxonomyReader) {
-    this(searchParams, indexReader, taxonomyReader, new FacetArrays(
-        PartitionsUtils.partitionSize(searchParams.indexingParams, taxonomyReader)));
+    this(searchParams, indexReader, taxonomyReader, null);
   }
 
   public StandardFacetsAccumulator(FacetSearchParams searchParams, IndexReader indexReader,
       TaxonomyReader taxonomyReader, FacetArrays facetArrays) {
-    super(searchParams, indexReader, taxonomyReader, facetArrays);
+    super(searchParams, indexReader, taxonomyReader, facetArrays == null ? createFacetArrays(searchParams, taxonomyReader) : facetArrays);
     
     // can only be computed later when docids size is known
     isUsingComplements = false;
@@ -126,8 +129,7 @@ public class StandardFacetsAccumulator e
 
       if (isUsingComplements) {
         try {
-          totalFacetCounts = TotalFacetCountsCache.getSingleton().getTotalCounts(indexReader, taxonomyReader, 
-              searchParams.indexingParams);
+          totalFacetCounts = TotalFacetCountsCache.getSingleton().getTotalCounts(indexReader, taxonomyReader, searchParams.indexingParams);
           if (totalFacetCounts != null) {
             docids = ScoredDocIdsUtils.getComplementSet(docids, indexReader);
           } else {

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/SumScoreFacetRequest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/SumScoreFacetRequest.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/SumScoreFacetRequest.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/SumScoreFacetRequest.java Mon Jul 29 13:07:30 2013
@@ -1,5 +1,6 @@
 package org.apache.lucene.facet.search;
 
+import org.apache.lucene.facet.params.FacetIndexingParams;
 import org.apache.lucene.facet.taxonomy.CategoryPath;
 import org.apache.lucene.facet.taxonomy.TaxonomyReader;
 
@@ -34,6 +35,11 @@ public class SumScoreFacetRequest extend
   }
 
   @Override
+  public FacetsAggregator createFacetsAggregator(FacetIndexingParams fip) {
+    return new SumScoreFacetsAggregator();
+  }
+  
+  @Override
   public Aggregator createAggregator(boolean useComplements, FacetArrays arrays, TaxonomyReader taxonomy) {
     assert !useComplements : "complements are not supported by this FacetRequest";
     return new ScoringAggregator(arrays.getFloatArray());

Copied: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/TaxonomyFacetsAccumulator.java (from r1507698, lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java)
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/TaxonomyFacetsAccumulator.java?p2=lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/TaxonomyFacetsAccumulator.java&p1=lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java&r1=1507698&r2=1508043&rev=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/search/TaxonomyFacetsAccumulator.java Mon Jul 29 13:07:30 2013
@@ -3,18 +3,19 @@ package org.apache.lucene.facet.search;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.List;
-import java.util.Set;
+import java.util.Map;
+import java.util.Map.Entry;
 
-import org.apache.lucene.facet.encoding.DGapVInt8IntDecoder;
 import org.apache.lucene.facet.params.CategoryListParams;
-import org.apache.lucene.facet.params.FacetSearchParams;
 import org.apache.lucene.facet.params.CategoryListParams.OrdinalPolicy;
+import org.apache.lucene.facet.params.FacetSearchParams;
 import org.apache.lucene.facet.search.FacetRequest.FacetArraysSource;
 import org.apache.lucene.facet.search.FacetRequest.ResultMode;
 import org.apache.lucene.facet.search.FacetRequest.SortOrder;
 import org.apache.lucene.facet.search.FacetsCollector.MatchingDocs;
+import org.apache.lucene.facet.taxonomy.CategoryPath;
 import org.apache.lucene.facet.taxonomy.ParallelTaxonomyArrays;
 import org.apache.lucene.facet.taxonomy.TaxonomyReader;
 import org.apache.lucene.index.IndexReader;
@@ -37,84 +38,116 @@ import org.apache.lucene.index.IndexRead
  */
 
 /**
- * Driver for Accumulating facets of faceted search requests over given
- * documents.
+ * A {@link FacetsAccumulator} suitable for accumulating categories that were
+ * indexed into a taxonomy index.
  * 
  * @lucene.experimental
  */
-public class FacetsAccumulator {
+public class TaxonomyFacetsAccumulator extends FacetsAccumulator {
 
   public final TaxonomyReader taxonomyReader;
   public final IndexReader indexReader;
   public final FacetArrays facetArrays;
-  public FacetSearchParams searchParams;
-
+  
   /**
    * Initializes the accumulator with the given search params, index reader and
    * taxonomy reader. This constructor creates the default {@link FacetArrays},
    * which do not support reuse. If you want to use {@link ReusingFacetArrays},
    * you should use the
-   * {@link #FacetsAccumulator(FacetSearchParams, IndexReader, TaxonomyReader, FacetArrays)}
+   * {@link #TaxonomyFacetsAccumulator(FacetSearchParams, IndexReader, TaxonomyReader)}
    * constructor.
    */
-  public FacetsAccumulator(FacetSearchParams searchParams, IndexReader indexReader, TaxonomyReader taxonomyReader) {
-    this(searchParams, indexReader, taxonomyReader, new FacetArrays(taxonomyReader.getSize()));
+  public TaxonomyFacetsAccumulator(FacetSearchParams searchParams, IndexReader indexReader, 
+      TaxonomyReader taxonomyReader) {
+    this(searchParams, indexReader, taxonomyReader, null);
   }
 
   /**
-   * Creates an appropriate {@link FacetsAccumulator},
-   * returning {@link FacetsAccumulator} when all requests
-   * are {@link CountFacetRequest} and only one partition is
-   * in use, otherwise {@link StandardFacetsAccumulator}.
-   */
-  public static FacetsAccumulator create(FacetSearchParams fsp, IndexReader indexReader, TaxonomyReader taxoReader) {
-    if (fsp.indexingParams.getPartitionSize() != Integer.MAX_VALUE) {
-      return new StandardFacetsAccumulator(fsp, indexReader, taxoReader);
-    }
-    
-    for (FacetRequest fr : fsp.facetRequests) {
-      if (!(fr instanceof CountFacetRequest)) {
-        return new StandardFacetsAccumulator(fsp, indexReader, taxoReader);
-      }
-    }
-    
-    return new FacetsAccumulator(fsp, indexReader, taxoReader);
-  }
-  
-  /** Returns an empty {@link FacetResult}. */
-  protected static FacetResult emptyResult(int ordinal, FacetRequest fr) {
-    FacetResultNode root = new FacetResultNode(ordinal, 0);
-    root.label = fr.categoryPath;
-    return new FacetResult(fr, root, 0);
-  }
-  
-  /**
    * Initializes the accumulator with the given parameters as well as
    * {@link FacetArrays}. Note that the accumulator doesn't call
    * {@link FacetArrays#free()}. If you require that (only makes sense if you
    * use {@link ReusingFacetArrays}, you should do it after you've finished with
    * the accumulator.
    */
-  public FacetsAccumulator(FacetSearchParams searchParams, IndexReader indexReader, TaxonomyReader taxonomyReader, 
-      FacetArrays facetArrays) {
-    this.facetArrays = facetArrays;
+  public TaxonomyFacetsAccumulator(FacetSearchParams searchParams, IndexReader indexReader, 
+      TaxonomyReader taxonomyReader, FacetArrays facetArrays) {
+    super(searchParams);
+    this.facetArrays = facetArrays == null ? new FacetArrays(taxonomyReader.getSize()) : facetArrays;
     this.indexReader = indexReader;
     this.taxonomyReader = taxonomyReader;
-    this.searchParams = searchParams;
   }
-  
+
+  /** Group all requests that belong to the same {@link CategoryListParams}. */
+  protected Map<CategoryListParams,List<FacetRequest>> groupRequests() {
+    if (searchParams.indexingParams.getAllCategoryListParams().size() == 1) {
+      return Collections.singletonMap(searchParams.indexingParams.getCategoryListParams(null), searchParams.facetRequests);
+    }
+    
+    HashMap<CategoryListParams,List<FacetRequest>> requestsPerCLP = new HashMap<CategoryListParams,List<FacetRequest>>();
+    for (FacetRequest fr : searchParams.facetRequests) {
+      CategoryListParams clp = searchParams.indexingParams.getCategoryListParams(fr.categoryPath);
+      List<FacetRequest> requests = requestsPerCLP.get(clp);
+      if (requests == null) {
+        requests = new ArrayList<FacetRequest>();
+        requestsPerCLP.put(clp, requests);
+      }
+      requests.add(fr);
+    }
+    return requestsPerCLP;
+  }
+
   /**
    * Returns the {@link FacetsAggregator} to use for aggregating the categories
-   * found in the result documents. The default implementation returns
-   * {@link CountingFacetsAggregator}, or {@link FastCountingFacetsAggregator}
-   * if all categories can be decoded with {@link DGapVInt8IntDecoder}.
+   * found in the result documents.
    */
   public FacetsAggregator getAggregator() {
-    if (FastCountingFacetsAggregator.verifySearchParams(searchParams)) {
-      return new FastCountingFacetsAggregator();
-    } else {
-      return new CountingFacetsAggregator();
+    Map<CategoryListParams,List<FacetRequest>> requestsPerCLP = groupRequests();
+
+    // optimize for all-CountFacetRequest and single category list (common case)
+    if (requestsPerCLP.size() == 1) {
+      boolean allCount = true;
+      for (FacetRequest fr : searchParams.facetRequests) {
+        if (!(fr instanceof CountFacetRequest)) {
+          allCount = false;
+          break;
+        }
+      }
+      if (allCount) {
+        return requestsPerCLP.values().iterator().next().get(0).createFacetsAggregator(searchParams.indexingParams);
+      }
+    }
+    
+    // If we're here it means the facet requests are spread across multiple
+    // category lists, or there are multiple types of facet requests, or both.
+    // Therefore create a per-CategoryList mapping of FacetsAggregators.
+    Map<CategoryListParams,FacetsAggregator> perCLPAggregator = new HashMap<CategoryListParams,FacetsAggregator>();
+    for (Entry<CategoryListParams,List<FacetRequest>> e : requestsPerCLP.entrySet()) {
+      CategoryListParams clp = e.getKey();
+      List<FacetRequest> requests = e.getValue();
+      Map<Class<? extends FacetsAggregator>,FacetsAggregator> aggClasses = new HashMap<Class<? extends FacetsAggregator>,FacetsAggregator>();
+      Map<CategoryPath,FacetsAggregator> perCategoryAggregator = new HashMap<CategoryPath,FacetsAggregator>();
+      for (FacetRequest fr : requests) {
+        FacetsAggregator fa = fr.createFacetsAggregator(searchParams.indexingParams);
+        if (fa == null) {
+          throw new IllegalArgumentException("this accumulator only supports requests that create a FacetsAggregator: " + fr);
+        }
+        Class<? extends FacetsAggregator> faClass = fa.getClass();
+        if (!aggClasses.containsKey(faClass)) {
+          aggClasses.put(faClass, fa);
+        } else {
+          fa = aggClasses.get(faClass);
+        }
+        perCategoryAggregator.put(fr.categoryPath, fa);
+      }
+      
+      if (aggClasses.size() == 1) { // only one type of facet request
+        perCLPAggregator.put(clp, aggClasses.values().iterator().next());
+      } else {
+        perCLPAggregator.put(clp, new MultiFacetsAggregator(perCategoryAggregator));
+      }
     }
+
+    return new PerCategoryListAggregator(perCLPAggregator, searchParams.indexingParams);
   }
   
   /**
@@ -139,18 +172,6 @@ public class FacetsAccumulator {
     return new TopKFacetResultsHandler(taxonomyReader, fr, facetArrays);
   }
 
-  protected Set<CategoryListParams> getCategoryLists() {
-    if (searchParams.indexingParams.getAllCategoryListParams().size() == 1) {
-      return Collections.singleton(searchParams.indexingParams.getCategoryListParams(null));
-    }
-    
-    HashSet<CategoryListParams> clps = new HashSet<CategoryListParams>();
-    for (FacetRequest fr : searchParams.facetRequests) {
-      clps.add(searchParams.indexingParams.getCategoryListParams(fr.categoryPath));
-    }
-    return clps;
-  }
-
   /**
    * Used by {@link FacetsCollector} to build the list of {@link FacetResult
    * facet results} that match the {@link FacetRequest facet requests} that were
@@ -159,10 +180,11 @@ public class FacetsAccumulator {
    * @param matchingDocs
    *          the documents that matched the query, per-segment.
    */
+  @Override
   public List<FacetResult> accumulate(List<MatchingDocs> matchingDocs) throws IOException {
     // aggregate facets per category list (usually onle one category list)
     FacetsAggregator aggregator = getAggregator();
-    for (CategoryListParams clp : getCategoryLists()) {
+    for (CategoryListParams clp : groupRequests().keySet()) {
       for (MatchingDocs md : matchingDocs) {
         aggregator.aggregate(md, clp, facetArrays);
       }
@@ -196,6 +218,7 @@ public class FacetsAccumulator {
     return res;
   }
 
+  @Override
   public boolean requiresDocScores() {
     return getAggregator().requiresDocScores();
   }

Modified: lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesAccumulator.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesAccumulator.java?rev=1508043&r1=1508042&r2=1508043&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesAccumulator.java (original)
+++ lucene/dev/trunk/lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesAccumulator.java Mon Jul 29 13:07:30 2013
@@ -32,19 +32,19 @@ import org.apache.lucene.facet.search.Fa
 import org.apache.lucene.facet.search.FacetResult;
 import org.apache.lucene.facet.search.FacetResultNode;
 import org.apache.lucene.facet.search.FacetsAccumulator;
-import org.apache.lucene.facet.search.FacetsAggregator;
 import org.apache.lucene.facet.search.FacetsCollector.MatchingDocs;
+import org.apache.lucene.facet.search.TaxonomyFacetsAccumulator;
 import org.apache.lucene.facet.taxonomy.CategoryPath;
 import org.apache.lucene.index.AtomicReader;
 import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.MultiDocValues.MultiSortedSetDocValues;
 import org.apache.lucene.index.MultiDocValues;
+import org.apache.lucene.index.MultiDocValues.MultiSortedSetDocValues;
 import org.apache.lucene.index.ReaderUtil;
 import org.apache.lucene.index.SortedSetDocValues;
 import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.PriorityQueue;
 
-/** A {@link FacetsAccumulator} that uses previously
+/** A {@link TaxonomyFacetsAccumulator} that uses previously
  *  indexed {@link SortedSetDocValuesFacetFields} to perform faceting,
  *  without require a separate taxonomy index.  Faceting is
  *  a bit slower (~25%), and there is added cost on every
@@ -57,25 +57,34 @@ public class SortedSetDocValuesAccumulat
   final SortedSetDocValuesReaderState state;
   final SortedSetDocValues dv;
   final String field;
-
-  public SortedSetDocValuesAccumulator(FacetSearchParams fsp, SortedSetDocValuesReaderState state) throws IOException {
-    super(fsp, null, null, new FacetArrays(state.getSize()));
+  final FacetArrays facetArrays;
+  
+  /** Constructor with the given facet search params. */
+  public SortedSetDocValuesAccumulator(SortedSetDocValuesReaderState state, FacetSearchParams fsp) 
+      throws IOException {
+    this(state, fsp, null);
+  }
+  
+  public SortedSetDocValuesAccumulator(SortedSetDocValuesReaderState state, FacetSearchParams fsp, FacetArrays arrays) 
+      throws IOException {
+    super(fsp);
     this.state = state;
     this.field = state.getField();
+    this.facetArrays = arrays == null ? new FacetArrays(state.getSize()) : arrays;
     dv = state.getDocValues();
 
     // Check params:
-    for(FacetRequest request : fsp.facetRequests) {
-      if (!(request instanceof CountFacetRequest)) {
-        throw new IllegalArgumentException("this collector only supports CountFacetRequest; got " + request);
+    for (FacetRequest fr : fsp.facetRequests) {
+      if (!(fr instanceof CountFacetRequest)) {
+        throw new IllegalArgumentException("this accumulator only supports CountFacetRequest; got " + fr);
       }
-      if (request.categoryPath.length != 1) {
-        throw new IllegalArgumentException("this collector only supports depth 1 CategoryPath; got " + request.categoryPath);
+      if (fr.categoryPath.length != 1) {
+        throw new IllegalArgumentException("this accumulator only supports 1-level CategoryPath; got " + fr.categoryPath);
       }
-      if (request.getDepth() != 1) {
-        throw new IllegalArgumentException("this collector only supports depth=1; got " + request.getDepth());
+      if (fr.getDepth() != 1) {
+        throw new IllegalArgumentException("this accumulator only supports depth=1; got " + fr.getDepth());
       }
-      String dim = request.categoryPath.components[0];
+      String dim = fr.categoryPath.components[0];
 
       SortedSetDocValuesReaderState.OrdRange ordRange = state.getOrdRange(dim);
       if (ordRange == null) {
@@ -84,131 +93,123 @@ public class SortedSetDocValuesAccumulat
     }
   }
 
-  @Override
-  public FacetsAggregator getAggregator() {
+  /** Keeps highest count results. */
+  static class TopCountPQ extends PriorityQueue<FacetResultNode> {
+    public TopCountPQ(int topN) {
+      super(topN, false);
+    }
+
+    @Override
+    protected boolean lessThan(FacetResultNode a, FacetResultNode b) {
+      if (a.value < b.value) {
+        return true;
+      } else if (a.value > b.value) {
+        return false;
+      } else {
+        return a.ordinal > b.ordinal;
+      }
+    }
+  }
 
-    return new FacetsAggregator() {
+  static class SortedSetAggregator {
 
-      @Override
-      public void aggregate(MatchingDocs matchingDocs, CategoryListParams clp, FacetArrays facetArrays) throws IOException {
+    private final SortedSetDocValuesReaderState state;
+    private final String field;
+    private final SortedSetDocValues dv;
+    
+    public SortedSetAggregator(String field, SortedSetDocValuesReaderState state, SortedSetDocValues dv) {
+      this.field = field;
+      this.state = state;
+      this.dv = dv;
+    }
+    
+    public void aggregate(MatchingDocs matchingDocs, FacetArrays facetArrays) throws IOException {
 
-        AtomicReader reader = matchingDocs.context.reader();
+      AtomicReader reader = matchingDocs.context.reader();
 
-        // LUCENE-5090: make sure the provided reader context "matches"
-        // the top-level reader passed to the
-        // SortedSetDocValuesReaderState, else cryptic
-        // AIOOBE can happen:
-        if (ReaderUtil.getTopLevelContext(matchingDocs.context).reader() != state.origReader) {
-          throw new IllegalStateException("the SortedSetDocValuesReaderState provided to this class does not match the reader being searched; you must create a new SortedSetDocValuesReaderState every time you open a new IndexReader");
-        }
-        
-        SortedSetDocValues segValues = reader.getSortedSetDocValues(field);
-        if (segValues == null) {
-          return;
-        }
+      // LUCENE-5090: make sure the provided reader context "matches"
+      // the top-level reader passed to the
+      // SortedSetDocValuesReaderState, else cryptic
+      // AIOOBE can happen:
+      if (ReaderUtil.getTopLevelContext(matchingDocs.context).reader() != state.origReader) {
+        throw new IllegalStateException("the SortedSetDocValuesReaderState provided to this class does not match the reader being searched; you must create a new SortedSetDocValuesReaderState every time you open a new IndexReader");
+      }
+      
+      SortedSetDocValues segValues = reader.getSortedSetDocValues(field);
+      if (segValues == null) {
+        return;
+      }
 
-        final int[] counts = facetArrays.getIntArray();
-        final int maxDoc = reader.maxDoc();
-        assert maxDoc == matchingDocs.bits.length();
-
-        if (dv instanceof MultiSortedSetDocValues) {
-          MultiDocValues.OrdinalMap ordinalMap = ((MultiSortedSetDocValues) dv).mapping;
-          int segOrd = matchingDocs.context.ord;
-
-          int numSegOrds = (int) segValues.getValueCount();
-
-          if (matchingDocs.totalHits < numSegOrds/10) {
-            // Remap every ord to global ord as we iterate:
-            int doc = 0;
-            while (doc < maxDoc && (doc = matchingDocs.bits.nextSetBit(doc)) != -1) {
-              segValues.setDocument(doc);
-              int term = (int) segValues.nextOrd();
-              while (term != SortedSetDocValues.NO_MORE_ORDS) {
-                counts[(int) ordinalMap.getGlobalOrd(segOrd, term)]++;
-                term = (int) segValues.nextOrd();
-              }
-              ++doc;
-            }
-          } else {
+      final int[] counts = facetArrays.getIntArray();
+      final int maxDoc = reader.maxDoc();
+      assert maxDoc == matchingDocs.bits.length();
 
-            // First count in seg-ord space:
-            final int[] segCounts = new int[numSegOrds];
-            int doc = 0;
-            while (doc < maxDoc && (doc = matchingDocs.bits.nextSetBit(doc)) != -1) {
-              segValues.setDocument(doc);
-              int term = (int) segValues.nextOrd();
-              while (term != SortedSetDocValues.NO_MORE_ORDS) {
-                segCounts[term]++;
-                term = (int) segValues.nextOrd();
-              }
-              ++doc;
-            }
+      if (dv instanceof MultiSortedSetDocValues) {
+        MultiDocValues.OrdinalMap ordinalMap = ((MultiSortedSetDocValues) dv).mapping;
+        int segOrd = matchingDocs.context.ord;
 
-            // Then, migrate to global ords:
-            for(int ord=0;ord<numSegOrds;ord++) {
-              int count = segCounts[ord];
-              if (count != 0) {
-                counts[(int) ordinalMap.getGlobalOrd(segOrd, ord)] += count;
-              }
+        int numSegOrds = (int) segValues.getValueCount();
+
+        if (matchingDocs.totalHits < numSegOrds/10) {
+          // Remap every ord to global ord as we iterate:
+          int doc = 0;
+          while (doc < maxDoc && (doc = matchingDocs.bits.nextSetBit(doc)) != -1) {
+            segValues.setDocument(doc);
+            int term = (int) segValues.nextOrd();
+            while (term != SortedSetDocValues.NO_MORE_ORDS) {
+              counts[(int) ordinalMap.getGlobalOrd(segOrd, term)]++;
+              term = (int) segValues.nextOrd();
             }
+            ++doc;
           }
         } else {
-          // No ord mapping (e.g., single segment index):
-          // just aggregate directly into counts:
 
+          // First count in seg-ord space:
+          final int[] segCounts = new int[numSegOrds];
           int doc = 0;
           while (doc < maxDoc && (doc = matchingDocs.bits.nextSetBit(doc)) != -1) {
             segValues.setDocument(doc);
             int term = (int) segValues.nextOrd();
             while (term != SortedSetDocValues.NO_MORE_ORDS) {
-              counts[term]++;
+              segCounts[term]++;
               term = (int) segValues.nextOrd();
             }
             ++doc;
           }
-        }
-      }
 
-      @Override
-      public void rollupValues(FacetRequest fr, int ordinal, int[] children, int[] siblings, FacetArrays facetArrays) {
-        // Nothing to do here: we only support flat (dim +
-        // label) facets, and in accumulate we sum up the
-        // count for the dimension.
-      }
+          // Then, migrate to global ords:
+          for(int ord=0;ord<numSegOrds;ord++) {
+            int count = segCounts[ord];
+            if (count != 0) {
+              counts[(int) ordinalMap.getGlobalOrd(segOrd, ord)] += count;
+            }
+          }
+        }
+      } else {
+        // No ord mapping (e.g., single segment index):
+        // just aggregate directly into counts:
 
-      @Override
-      public boolean requiresDocScores() {
-        return false;
+        int doc = 0;
+        while (doc < maxDoc && (doc = matchingDocs.bits.nextSetBit(doc)) != -1) {
+          segValues.setDocument(doc);
+          int term = (int) segValues.nextOrd();
+          while (term != SortedSetDocValues.NO_MORE_ORDS) {
+            counts[term]++;
+            term = (int) segValues.nextOrd();
+          }
+          ++doc;
+        }
       }
-    };
-  }
-
-  /** Keeps highest count results. */
-  static class TopCountPQ extends PriorityQueue<FacetResultNode> {
-    public TopCountPQ(int topN) {
-      super(topN, false);
     }
 
-    @Override
-    protected boolean lessThan(FacetResultNode a, FacetResultNode b) {
-      if (a.value < b.value) {
-        return true;
-      } else if (a.value > b.value) {
-        return false;
-      } else {
-        return a.ordinal > b.ordinal;
-      }
-    }
   }
-
+  
   @Override
   public List<FacetResult> accumulate(List<MatchingDocs> matchingDocs) throws IOException {
 
-    FacetsAggregator aggregator = getAggregator();
-    for (CategoryListParams clp : getCategoryLists()) {
-      for (MatchingDocs md : matchingDocs) {
-        aggregator.aggregate(md, clp, facetArrays);
-      }
+    SortedSetAggregator aggregator = new SortedSetAggregator(field, state, dv);
+    for (MatchingDocs md : matchingDocs) {
+      aggregator.aggregate(md, facetArrays);
     }
 
     // compute top-K
@@ -218,7 +219,7 @@ public class SortedSetDocValuesAccumulat
 
     BytesRef scratch = new BytesRef();
 
-    for(FacetRequest request : searchParams.facetRequests) {
+    for (FacetRequest request : searchParams.facetRequests) {
       String dim = request.categoryPath.components[0];
       SortedSetDocValuesReaderState.OrdRange ordRange = state.getOrdRange(dim);
       // checked in ctor:
@@ -315,4 +316,10 @@ public class SortedSetDocValuesAccumulat
 
     return results;
   }
+  
+  @Override
+  public boolean requiresDocScores() {
+    return false;
+  }
+  
 }