Index: lucene/demo/src/java/org/apache/lucene/demo/facet/AssociationsFacetsExample.java =================================================================== --- lucene/demo/src/java/org/apache/lucene/demo/facet/AssociationsFacetsExample.java (revision 1507979) +++ lucene/demo/src/java/org/apache/lucene/demo/facet/AssociationsFacetsExample.java (working copy) @@ -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 @@ 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 aggregators = new HashMap(); - 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 Index: lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java =================================================================== --- lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java (revision 1507979) +++ lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java (working copy) @@ -27,7 +27,6 @@ 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 @@ /** User runs a query and counts facets. */ public List search() throws IOException { - FacetSearchParams fsp = new FacetSearchParams( - new RangeFacetRequest("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 rangeFacetRequest = new RangeFacetRequest("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 @@ return searcher.search(q, 10); } + @Override public void close() throws IOException { searcher.getIndexReader().close(); indexDir.close(); Index: lucene/demo/src/java/org/apache/lucene/demo/facet/SimpleSortedSetFacetsExample.java =================================================================== --- lucene/demo/src/java/org/apache/lucene/demo/facet/SimpleSortedSetFacetsExample.java (revision 1507979) +++ lucene/demo/src/java/org/apache/lucene/demo/facet/SimpleSortedSetFacetsExample.java (working copy) @@ -91,7 +91,7 @@ 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 @@ 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 Index: lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationFloatSumFacetRequest.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationFloatSumFacetRequest.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationFloatSumFacetRequest.java (working copy) @@ -1,50 +0,0 @@ -package org.apache.lucene.facet.associations; - -import org.apache.lucene.facet.search.FacetArrays; -import org.apache.lucene.facet.search.FacetRequest; -import org.apache.lucene.facet.taxonomy.CategoryPath; - -/* - * 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. - */ - -/** - * A {@link FacetRequest} for weighting facets according to their float - * association by summing the association values. - * - * @lucene.experimental - */ -public class AssociationFloatSumFacetRequest extends FacetRequest { - - /** - * Create a float association facet request for a given node in the - * taxonomy. - */ - public AssociationFloatSumFacetRequest(CategoryPath path, int num) { - super(path, num); - } - - @Override - public double getValueOf(FacetArrays arrays, int ordinal) { - return arrays.getFloatArray()[ordinal]; - } - - @Override - public FacetArraysSource getFacetArraysSource() { - return FacetArraysSource.FLOAT; - } - -} Index: lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationIntSumFacetRequest.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationIntSumFacetRequest.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/associations/AssociationIntSumFacetRequest.java (working copy) @@ -1,50 +0,0 @@ -package org.apache.lucene.facet.associations; - -import org.apache.lucene.facet.search.FacetArrays; -import org.apache.lucene.facet.search.FacetRequest; -import org.apache.lucene.facet.taxonomy.CategoryPath; - -/* - * 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. - */ - -/** - * A {@link FacetRequest} for weighting facets according to their integer - * association by summing the association values. - * - * @lucene.experimental - */ -public class AssociationIntSumFacetRequest extends FacetRequest { - - /** - * Create an integer association facet request for a given node in the - * taxonomy. - */ - public AssociationIntSumFacetRequest(CategoryPath path, int num) { - super(path, num); - } - - @Override - public FacetArraysSource getFacetArraysSource() { - return FacetArraysSource.INT; - } - - @Override - public double getValueOf(FacetArrays arrays, int ordinal) { - return arrays.getIntArray()[ordinal]; - } - -} Index: lucene/facet/src/java/org/apache/lucene/facet/associations/MultiAssociationsFacetsAggregator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/associations/MultiAssociationsFacetsAggregator.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/associations/MultiAssociationsFacetsAggregator.java (working copy) @@ -1,92 +0,0 @@ -package org.apache.lucene.facet.associations; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.lucene.facet.params.CategoryListParams; -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.search.FacetsCollector.MatchingDocs; -import org.apache.lucene.facet.taxonomy.CategoryPath; - -/* - * 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. - */ - -/** - * A {@link FacetsAggregator} which chains multiple aggregators for aggregating - * the association values of categories that belong to the same category list. - * While nothing prevents you from chaining general purpose aggregators, it is - * only useful for aggregating association values, as each association type is - * written in its own list. - * - * @lucene.experimental - */ -public class MultiAssociationsFacetsAggregator implements FacetsAggregator { - - private final Map categoryAggregators; - private final List 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. - */ - public MultiAssociationsFacetsAggregator(Map aggregators) { - this.categoryAggregators = aggregators; - - // make sure that each FacetsAggregator class is invoked only once, or - // otherwise categories may be aggregated multiple times. - Map, FacetsAggregator> aggsClasses = - new HashMap,FacetsAggregator>(); - for (FacetsAggregator fa : aggregators.values()) { - aggsClasses.put(fa.getClass(), fa); - } - this.aggregators = new ArrayList(aggsClasses.values()); - } - - @Override - public void aggregate(MatchingDocs matchingDocs, CategoryListParams clp, FacetArrays facetArrays) throws IOException { - for (FacetsAggregator fa : aggregators) { - fa.aggregate(matchingDocs, clp, facetArrays); - } - } - - @Override - public void rollupValues(FacetRequest fr, int ordinal, int[] children, int[] siblings, FacetArrays facetArrays) { - categoryAggregators.get(fr.categoryPath).rollupValues(fr, ordinal, children, siblings, facetArrays); - } - - @Override - public boolean requiresDocScores() { - for (FacetsAggregator fa : aggregators) { - if (fa.requiresDocScores()) { - return true; - } - } - return false; - } - -} Index: lucene/facet/src/java/org/apache/lucene/facet/associations/SumFloatAssociationFacetRequest.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/associations/SumFloatAssociationFacetRequest.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/associations/SumFloatAssociationFacetRequest.java (working copy) @@ -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 @@ * * @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]; } Index: lucene/facet/src/java/org/apache/lucene/facet/associations/SumIntAssociationFacetRequest.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/associations/SumIntAssociationFacetRequest.java (revision 1507930) +++ lucene/facet/src/java/org/apache/lucene/facet/associations/SumIntAssociationFacetRequest.java (working copy) @@ -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 @@ * * @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; } Index: lucene/facet/src/java/org/apache/lucene/facet/params/FacetSearchParams.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/params/FacetSearchParams.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/params/FacetSearchParams.java (working copy) @@ -23,13 +23,9 @@ */ /** - * Defines parameters that are needed for faceted search. The list of - * {@link FacetRequest facet requests} denotes the facets for which aggregated - * should be done. - *

- * 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 */ Index: lucene/facet/src/java/org/apache/lucene/facet/range/MultiFacetsAccumulator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/range/MultiFacetsAccumulator.java (revision 1507930) +++ lucene/facet/src/java/org/apache/lucene/facet/range/MultiFacetsAccumulator.java (working copy) @@ -20,98 +20,50 @@ 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 rangeRequests = new ArrayList(); - List nonRangeRequests = new ArrayList(); - 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; + private MultiFacetsAccumulator(FacetsAccumulator... accumulators) { + super((FacetSearchParams) null); + this.accumulators = accumulators; } @Override - public FacetsAggregator getAggregator() { - throw new UnsupportedOperationException(); + public boolean requiresDocScores() { + for (FacetsAccumulator fa : accumulators) { + if (fa.requiresDocScores()) { + return true; + } + } + return false; } @Override - protected FacetResultsHandler createFacetResultsHandler(FacetRequest fr) { - throw new UnsupportedOperationException(); - } - - @Override - protected Set getCategoryLists() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean requiresDocScores() { - return accumulator.requiresDocScores(); - } - public List accumulate(List matchingDocs) throws IOException { - List results = accumulator.accumulate(matchingDocs); - List rangeResults = rangeAccumulator.accumulate(matchingDocs); - - int aUpto = 0; - int raUpto = 0; List merged = new ArrayList(); - 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; } } Index: lucene/facet/src/java/org/apache/lucene/facet/range/RangeAccumulator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/range/RangeAccumulator.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/range/RangeAccumulator.java (working copy) @@ -19,6 +19,7 @@ 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.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,31 +50,27 @@ final List requests = new ArrayList(); - 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 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 accumulate(List matchingDocs) throws IOException { // TODO: test if this is faster (in the past it was @@ -82,7 +77,7 @@ // faster to do MachingDocs on the inside) ... see // patches on LUCENE-4965): List results = new ArrayList(); - for(int i=0;i rangeRequests = new ArrayList(); - List nonRangeRequests = new ArrayList(); - 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); - } 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); - } - } - - 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 getCategoryLists() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean requiresDocScores() { - return accumulator.requiresDocScores(); - } - - public List accumulate(List matchingDocs) throws IOException { - List results = accumulator.accumulate(matchingDocs); - List rangeResults = rangeAccumulator.accumulate(matchingDocs); - - int aUpto = 0; - int raUpto = 0; - List merged = new ArrayList(); - for(FacetRequest fr : searchParams.facetRequests) { - if (fr instanceof RangeFacetRequest) { - merged.add(rangeResults.get(raUpto++)); - } else { - merged.add(results.get(aUpto++)); - } - } - - return merged; - } -} Index: lucene/facet/src/java/org/apache/lucene/facet/sampling/Sampler.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/sampling/Sampler.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/sampling/Sampler.java (working copy) @@ -4,6 +4,7 @@ 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; @@ -10,6 +11,7 @@ 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 @@ } @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); Index: lucene/facet/src/java/org/apache/lucene/facet/search/CountFacetRequest.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/CountFacetRequest.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/search/CountFacetRequest.java (working copy) @@ -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 @@ 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 @@ } @Override + public FacetsAggregator createFacetsAggregator(FacetIndexingParams fip) { + return CountingFacetsAggregator.create(fip.getCategoryListParams(categoryPath)); + } + + @Override public double getValueOf(FacetArrays arrays, int ordinal) { return arrays.getIntArray()[ordinal]; } Index: lucene/facet/src/java/org/apache/lucene/facet/search/CountingFacetsAggregator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/CountingFacetsAggregator.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/search/CountingFacetsAggregator.java (working copy) @@ -2,6 +2,7 @@ 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 @@ */ 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 Index: lucene/facet/src/java/org/apache/lucene/facet/search/DrillSideways.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/DrillSideways.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/search/DrillSideways.java (working copy) @@ -25,7 +25,10 @@ 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,12 +73,27 @@ 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 * facet request into the baseQuery. This is unusual, @@ -440,13 +458,21 @@ /** 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 Index: lucene/facet/src/java/org/apache/lucene/facet/search/FacetRequest.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FacetRequest.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetRequest.java (working copy) @@ -3,6 +3,8 @@ 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 @@ "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) { Index: lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java (working copy) @@ -7,6 +7,7 @@ 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 @@ } 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"); } Index: lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java (working copy) @@ -2,20 +2,15 @@ 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.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.MultiFacetsAccumulator; +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,96 @@ */ /** - * 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. + *

+ * If both types of facet requests are used, it returns a + * {@link MultiFacetsAccumulator} such that the range facet requests are + * accumulated last. Therefore the result of {@link #accumulate(List)} may not + * be in the same order as the given facet requests. */ - 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 rangeRequests = new ArrayList(); + List nonRangeRequests = new ArrayList(); + 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. + *

+ * If both types of facet requests are used, it returns a + * {@link MultiFacetsAccumulator} such that the range facet requests are + * accumulated last. Therefore the result of {@link #accumulate(List)} may not + * be in the same order as the given facet requests. */ - 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 rangeRequests = new ArrayList(); + List nonRangeRequests = new ArrayList(); 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 +132,6 @@ } /** - * 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 getCategoryLists() { - if (searchParams.indexingParams.getAllCategoryListParams().size() == 1) { - return Collections.singleton(searchParams.indexingParams.getCategoryListParams(null)); - } - - HashSet clps = new HashSet(); - 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 +139,12 @@ * @param matchingDocs * the documents that matched the query, per-segment. */ - public List accumulate(List 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 res = new ArrayList(); - 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 accumulate(List 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(); + } Index: lucene/facet/src/java/org/apache/lucene/facet/search/FacetsCollector.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FacetsCollector.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetsCollector.java (working copy) @@ -167,7 +167,7 @@ * 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)); } /** Index: lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FastCountingFacetsAggregator.java (working copy) @@ -5,7 +5,6 @@ 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 @@ 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 { Index: lucene/facet/src/java/org/apache/lucene/facet/search/MultiFacetsAggregator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/MultiFacetsAggregator.java (revision 1507930) +++ lucene/facet/src/java/org/apache/lucene/facet/search/MultiFacetsAggregator.java (working copy) @@ -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,13 +39,13 @@ * * @lucene.experimental */ -public class MultiAssociationsFacetsAggregator implements FacetsAggregator { +public class MultiFacetsAggregator implements FacetsAggregator { private final Map categoryAggregators; private final List aggregators; /** - * Creates a new {@link MultiAssociationsFacetsAggregator} over the given + * Creates a new {@link MultiFacetsAggregator} 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 @@ -54,7 +54,7 @@ * categories, the aggregation is done on the unique set of aggregators, which * are identified by their class. */ - public MultiAssociationsFacetsAggregator(Map aggregators) { + public MultiFacetsAggregator(Map aggregators) { this.categoryAggregators = aggregators; // make sure that each FacetsAggregator class is invoked only once, or Index: lucene/facet/src/java/org/apache/lucene/facet/search/PerCategoryListAggregator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/PerCategoryListAggregator.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/search/PerCategoryListAggregator.java (working copy) @@ -27,6 +27,9 @@ /** * 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 { Index: lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java (working copy) @@ -43,7 +43,7 @@ */ /** - * Standard implementation for {@link FacetsAccumulator}, utilizing partitions to save on memory. + * Standard implementation for {@link TaxonomyFacetsAccumulator}, utilizing partitions to save on memory. *

* 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 @@ * * @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 @@ 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 @@ 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 { Index: lucene/facet/src/java/org/apache/lucene/facet/search/SumScoreFacetRequest.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/SumScoreFacetRequest.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/search/SumScoreFacetRequest.java (working copy) @@ -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 @@ } @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()); Index: lucene/facet/src/java/org/apache/lucene/facet/search/TaxonomyFacetsAccumulator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/TaxonomyFacetsAccumulator.java (revision 1507698) +++ lucene/facet/src/java/org/apache/lucene/facet/search/TaxonomyFacetsAccumulator.java (working copy) @@ -3,18 +3,19 @@ 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.CategoryListParams.OrdinalPolicy; 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.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,58 +38,31 @@ */ /** - * 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 @@ -95,26 +69,85 @@ * 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> groupRequests() { + if (searchParams.indexingParams.getAllCategoryListParams().size() == 1) { + return Collections.singletonMap(searchParams.indexingParams.getCategoryListParams(null), searchParams.facetRequests); + } + + HashMap> requestsPerCLP = new HashMap>(); + for (FacetRequest fr : searchParams.facetRequests) { + CategoryListParams clp = searchParams.indexingParams.getCategoryListParams(fr.categoryPath); + List requests = requestsPerCLP.get(clp); + if (requests == null) { + requests = new ArrayList(); + 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> 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 perCLPAggregator = new HashMap(); + for (Entry> e : requestsPerCLP.entrySet()) { + CategoryListParams clp = e.getKey(); + List requests = e.getValue(); + Map,FacetsAggregator> aggClasses = new HashMap,FacetsAggregator>(); + Map perCategoryAggregator = new HashMap(); + 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 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 @@ return new TopKFacetResultsHandler(taxonomyReader, fr, facetArrays); } - protected Set getCategoryLists() { - if (searchParams.indexingParams.getAllCategoryListParams().size() == 1) { - return Collections.singleton(searchParams.indexingParams.getCategoryListParams(null)); - } - - HashSet clps = new HashSet(); - 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 @@ * @param matchingDocs * the documents that matched the query, per-segment. */ + @Override public List accumulate(List 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 @@ return res; } + @Override public boolean requiresDocScores() { return getAggregator().requiresDocScores(); } Index: lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesAccumulator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesAccumulator.java (revision 1507979) +++ lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesAccumulator.java (working copy) @@ -32,19 +32,19 @@ 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; import org.apache.lucene.index.MultiDocValues.MultiSortedSetDocValues; -import org.apache.lucene.index.MultiDocValues; 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 @@ 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 @@ } } - @Override - public FacetsAggregator getAggregator() { + /** Keeps highest count results. */ + static class TopCountPQ extends PriorityQueue { + public TopCountPQ(int topN) { + super(topN, false); + } - return new FacetsAggregator() { + @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 void aggregate(MatchingDocs matchingDocs, CategoryListParams clp, FacetArrays facetArrays) throws IOException { + static class SortedSetAggregator { - AtomicReader reader = matchingDocs.context.reader(); + 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 { - // 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; - } + AtomicReader reader = matchingDocs.context.reader(); - final int[] counts = facetArrays.getIntArray(); - final int maxDoc = reader.maxDoc(); - assert maxDoc == matchingDocs.bits.length(); + // 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; + } - if (dv instanceof MultiSortedSetDocValues) { - MultiDocValues.OrdinalMap ordinalMap = ((MultiSortedSetDocValues) dv).mapping; - int segOrd = matchingDocs.context.ord; + final int[] counts = facetArrays.getIntArray(); + final int maxDoc = reader.maxDoc(); + assert maxDoc == matchingDocs.bits.length(); - int numSegOrds = (int) segValues.getValueCount(); + if (dv instanceof MultiSortedSetDocValues) { + MultiDocValues.OrdinalMap ordinalMap = ((MultiSortedSetDocValues) dv).mapping; + int segOrd = matchingDocs.context.ord; - 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 { + int numSegOrds = (int) segValues.getValueCount(); - // 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 (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(); } - - // Then, migrate to global ords: - for(int ord=0;ord { - 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 accumulate(List 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 @@ 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 @@ return results; } + + @Override + public boolean requiresDocScores() { + return false; + } + } Index: lucene/facet/src/test/org/apache/lucene/facet/associations/AssociationsFacetRequestTest.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/associations/AssociationsFacetRequestTest.java (revision 1507979) +++ lucene/facet/src/test/org/apache/lucene/facet/associations/AssociationsFacetRequestTest.java (working copy) @@ -1,8 +1,6 @@ package org.apache.lucene.facet.associations; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.analysis.MockTokenizer; @@ -10,9 +8,9 @@ import org.apache.lucene.facet.FacetTestCase; 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.search.TaxonomyFacetsAccumulator; import org.apache.lucene.facet.taxonomy.CategoryPath; import org.apache.lucene.facet.taxonomy.TaxonomyWriter; import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader; @@ -103,12 +101,12 @@ // facet requests for two facets FacetSearchParams fsp = new FacetSearchParams( - new AssociationIntSumFacetRequest(aint, 10), - new AssociationIntSumFacetRequest(bint, 10)); + new SumIntAssociationFacetRequest(aint, 10), + new SumIntAssociationFacetRequest(bint, 10)); Query q = new MatchAllDocsQuery(); - FacetsAccumulator fa = new FacetsAccumulator(fsp, reader, taxo) { + TaxonomyFacetsAccumulator fa = new TaxonomyFacetsAccumulator(fsp, reader, taxo) { @Override public FacetsAggregator getAggregator() { return new SumIntAssociationFacetsAggregator(); @@ -135,12 +133,12 @@ // facet requests for two facets FacetSearchParams fsp = new FacetSearchParams( - new AssociationFloatSumFacetRequest(afloat, 10), - new AssociationFloatSumFacetRequest(bfloat, 10)); + new SumFloatAssociationFacetRequest(afloat, 10), + new SumFloatAssociationFacetRequest(bfloat, 10)); Query q = new MatchAllDocsQuery(); - FacetsAccumulator fa = new FacetsAccumulator(fsp, reader, taxo) { + TaxonomyFacetsAccumulator fa = new TaxonomyFacetsAccumulator(fsp, reader, taxo) { @Override public FacetsAggregator getAggregator() { return new SumFloatAssociationFacetsAggregator(); @@ -167,27 +165,14 @@ // facet requests for two facets FacetSearchParams fsp = new FacetSearchParams( - new AssociationIntSumFacetRequest(aint, 10), - new AssociationIntSumFacetRequest(bint, 10), - new AssociationFloatSumFacetRequest(afloat, 10), - new AssociationFloatSumFacetRequest(bfloat, 10)); + new SumIntAssociationFacetRequest(aint, 10), + new SumIntAssociationFacetRequest(bint, 10), + new SumFloatAssociationFacetRequest(afloat, 10), + new SumFloatAssociationFacetRequest(bfloat, 10)); Query q = new MatchAllDocsQuery(); - final SumIntAssociationFacetsAggregator sumInt = new SumIntAssociationFacetsAggregator(); - final SumFloatAssociationFacetsAggregator sumFloat = new SumFloatAssociationFacetsAggregator(); - final Map aggregators = new HashMap(); - aggregators.put(aint, sumInt); - aggregators.put(bint, sumInt); - aggregators.put(afloat, sumFloat); - aggregators.put(bfloat, sumFloat); - FacetsAccumulator fa = new FacetsAccumulator(fsp, reader, taxo) { - @Override - public FacetsAggregator getAggregator() { - return new MultiAssociationsFacetsAggregator(aggregators); - } - }; - FacetsCollector fc = FacetsCollector.create(fa); + FacetsCollector fc = FacetsCollector.create(fsp, reader, taxo); IndexSearcher searcher = newSearcher(reader); searcher.search(q, fc); Index: lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeAccumulator.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeAccumulator.java (revision 1507979) +++ lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeAccumulator.java (working copy) @@ -17,6 +17,7 @@ * limitations under the License. */ +import java.io.IOException; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -37,13 +38,15 @@ import org.apache.lucene.facet.params.FacetSearchParams; import org.apache.lucene.facet.search.CountFacetRequest; import org.apache.lucene.facet.search.DrillDownQuery; +import org.apache.lucene.facet.search.DrillSideways; import org.apache.lucene.facet.search.DrillSideways.DrillSidewaysResult; -import org.apache.lucene.facet.search.DrillSideways; 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.FacetsAccumulator; import org.apache.lucene.facet.search.FacetsCollector; +import org.apache.lucene.facet.sortedset.SortedSetDocValuesFacetFields; +import org.apache.lucene.facet.sortedset.SortedSetDocValuesReaderState; import org.apache.lucene.facet.taxonomy.CategoryPath; import org.apache.lucene.facet.taxonomy.TaxonomyReader; import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader; @@ -74,15 +77,12 @@ IndexReader r = w.getReader(); w.close(); - FacetSearchParams fsp = new FacetSearchParams( - new RangeFacetRequest("field", - new LongRange("less than 10", 0L, true, 10L, false), - new LongRange("less than or equal to 10", 0L, true, 10L, true), - new LongRange("over 90", 90L, false, 100L, false), - new LongRange("90 or above", 90L, true, 100L, false), - new LongRange("over 1000", 1000L, false, Long.MAX_VALUE, false))); - - RangeAccumulator a = new RangeAccumulator(fsp, r); + RangeAccumulator a = new RangeAccumulator(new RangeFacetRequest("field", + new LongRange("less than 10", 0L, true, 10L, false), + new LongRange("less than or equal to 10", 0L, true, 10L, true), + new LongRange("over 90", 90L, false, 100L, false), + new LongRange("90 or above", 90L, true, 100L, false), + new LongRange("over 1000", 1000L, false, Long.MAX_VALUE, false))); FacetsCollector fc = FacetsCollector.create(a); @@ -97,8 +97,8 @@ } /** Tests single request that mixes Range and non-Range - * faceting, with DrillSideways. */ - public void testMixedRangeAndNonRange() throws Exception { + * faceting, with DrillSideways and taxonomy. */ + public void testMixedRangeAndNonRangeTaxonomy() throws Exception { Directory d = newDirectory(); RandomIndexWriter w = new RandomIndexWriter(random(), d); Directory td = newDirectory(); @@ -105,7 +105,7 @@ DirectoryTaxonomyWriter tw = new DirectoryTaxonomyWriter(td, IndexWriterConfig.OpenMode.CREATE); FacetFields ff = new FacetFields(tw); - for(long l=0;l<100;l++) { + for (long l = 0; l < 100; l++) { Document doc = new Document(); // For computing range facet counts: doc.add(new NumericDocValuesField("field", l)); @@ -122,7 +122,7 @@ w.addDocument(doc); } - IndexReader r = w.getReader(); + final IndexReader r = w.getReader(); w.close(); final TaxonomyReader tr = new DirectoryTaxonomyReader(tw); @@ -130,15 +130,15 @@ IndexSearcher s = newSearcher(r); - final FacetSearchParams fsp = new FacetSearchParams( - new CountFacetRequest(new CategoryPath("dim"), 2), - new RangeFacetRequest("field", - new LongRange("less than 10", 0L, true, 10L, false), - new LongRange("less than or equal to 10", 0L, true, 10L, true), - new LongRange("over 90", 90L, false, 100L, false), - new LongRange("90 or above", 90L, true, 100L, false), - new LongRange("over 1000", 1000L, false, Long.MAX_VALUE, false))); - + final CountFacetRequest countRequest = new CountFacetRequest(new CategoryPath("dim"), 2); + final RangeFacetRequest rangeRequest = new RangeFacetRequest("field", + new LongRange("less than 10", 0L, true, 10L, false), + new LongRange("less than or equal to 10", 0L, true, 10L, true), + new LongRange("over 90", 90L, false, 100L, false), + new LongRange("90 or above", 90L, true, 100L, false), + new LongRange("over 1000", 1000L, false, Long.MAX_VALUE, false)); + FacetSearchParams fsp = new FacetSearchParams(countRequest, rangeRequest); + final Set dimSeen = new HashSet(); DrillSideways ds = new DrillSideways(s, tr) { @@ -145,17 +145,17 @@ @Override protected FacetsAccumulator getDrillDownAccumulator(FacetSearchParams fsp) { checkSeen(fsp); - return RangeFacetsAccumulatorWrapper.create(fsp, searcher.getIndexReader(), tr); + return FacetsAccumulator.create(fsp, r, tr, null); } @Override protected FacetsAccumulator getDrillSidewaysAccumulator(String dim, FacetSearchParams fsp) { checkSeen(fsp); - return RangeFacetsAccumulatorWrapper.create(fsp, searcher.getIndexReader(), tr); + return FacetsAccumulator.create(fsp, r, tr, null); } private void checkSeen(FacetSearchParams fsp) { - // Each dim should should up only once, across + // Each dim should up only once, across // both drillDown and drillSideways requests: for(FacetRequest fr : fsp.facetRequests) { String dim = fr.categoryPath.components[0]; @@ -204,6 +204,111 @@ IOUtils.close(tr, td, r, d); } + /** Tests single request that mixes Range and non-Range + * faceting, with DrillSideways and SortedSet. */ + public void testMixedRangeAndNonRangeSortedSet() throws Exception { + assumeTrue("Test requires SortedSetDV support", defaultCodecSupportsSortedSet()); + Directory d = newDirectory(); + RandomIndexWriter w = new RandomIndexWriter(random(), d); + SortedSetDocValuesFacetFields ff = new SortedSetDocValuesFacetFields(); + + for (long l = 0; l < 100; l++) { + Document doc = new Document(); + // For computing range facet counts: + doc.add(new NumericDocValuesField("field", l)); + // For drill down by numeric range: + doc.add(new LongField("field", l, Field.Store.NO)); + + CategoryPath cp; + if ((l&3) == 0) { + cp = new CategoryPath("dim", "a"); + } else { + cp = new CategoryPath("dim", "b"); + } + ff.addFields(doc, Collections.singletonList(cp)); + w.addDocument(doc); + } + + final IndexReader r = w.getReader(); + w.close(); + + IndexSearcher s = newSearcher(r); + final SortedSetDocValuesReaderState state = new SortedSetDocValuesReaderState(s.getIndexReader()); + + final CountFacetRequest countRequest = new CountFacetRequest(new CategoryPath("dim"), 2); + final RangeFacetRequest rangeRequest = new RangeFacetRequest("field", + new LongRange("less than 10", 0L, true, 10L, false), + new LongRange("less than or equal to 10", 0L, true, 10L, true), + new LongRange("over 90", 90L, false, 100L, false), + new LongRange("90 or above", 90L, true, 100L, false), + new LongRange("over 1000", 1000L, false, Long.MAX_VALUE, false)); + FacetSearchParams fsp = new FacetSearchParams(countRequest, rangeRequest); + + final Set dimSeen = new HashSet(); + + DrillSideways ds = new DrillSideways(s, state) { + @Override + protected FacetsAccumulator getDrillDownAccumulator(FacetSearchParams fsp) throws IOException { + checkSeen(fsp); + return FacetsAccumulator.create(fsp, state, null); + } + + @Override + protected FacetsAccumulator getDrillSidewaysAccumulator(String dim, FacetSearchParams fsp) throws IOException { + checkSeen(fsp); + return FacetsAccumulator.create(fsp, state, null); + } + + private void checkSeen(FacetSearchParams fsp) { + // Each dim should up only once, across + // both drillDown and drillSideways requests: + for(FacetRequest fr : fsp.facetRequests) { + String dim = fr.categoryPath.components[0]; + assertFalse("dim " + dim + " already seen", dimSeen.contains(dim)); + dimSeen.add(dim); + } + } + + @Override + protected boolean scoreSubDocsAtOnce() { + return random().nextBoolean(); + } + }; + + // First search, no drill downs: + DrillDownQuery ddq = new DrillDownQuery(FacetIndexingParams.DEFAULT, new MatchAllDocsQuery()); + DrillSidewaysResult dsr = ds.search(null, ddq, 10, fsp); + + assertEquals(100, dsr.hits.totalHits); + assertEquals(2, dsr.facetResults.size()); + assertEquals("dim (0)\n b (75)\n a (25)\n", FacetTestUtils.toSimpleString(dsr.facetResults.get(0))); + assertEquals("field (0)\n less than 10 (10)\n less than or equal to 10 (11)\n over 90 (9)\n 90 or above (10)\n over 1000 (0)\n", FacetTestUtils.toSimpleString(dsr.facetResults.get(1))); + + // Second search, drill down on dim=b: + ddq = new DrillDownQuery(FacetIndexingParams.DEFAULT, new MatchAllDocsQuery()); + ddq.add(new CategoryPath("dim", "b")); + dimSeen.clear(); + dsr = ds.search(null, ddq, 10, fsp); + + assertEquals(75, dsr.hits.totalHits); + assertEquals(2, dsr.facetResults.size()); + assertEquals("dim (0)\n b (75)\n a (25)\n", FacetTestUtils.toSimpleString(dsr.facetResults.get(0))); + assertEquals("field (0)\n less than 10 (7)\n less than or equal to 10 (8)\n over 90 (7)\n 90 or above (8)\n over 1000 (0)\n", FacetTestUtils.toSimpleString(dsr.facetResults.get(1))); + + // Third search, drill down on "less than or equal to 10": + ddq = new DrillDownQuery(FacetIndexingParams.DEFAULT, new MatchAllDocsQuery()); + ddq.add("field", NumericRangeQuery.newLongRange("field", 0L, 10L, true, true)); + dimSeen.clear(); + dsr = ds.search(null, ddq, 10, fsp); + + assertEquals(11, dsr.hits.totalHits); + assertEquals(2, dsr.facetResults.size()); + assertEquals("dim (0)\n b (8)\n a (3)\n", FacetTestUtils.toSimpleString(dsr.facetResults.get(0))); + assertEquals("field (0)\n less than 10 (10)\n less than or equal to 10 (11)\n over 90 (9)\n 90 or above (10)\n over 1000 (0)\n", FacetTestUtils.toSimpleString(dsr.facetResults.get(1))); + + IOUtils.close(r, d); + } + public void testBasicDouble() throws Exception { Directory d = newDirectory(); RandomIndexWriter w = new RandomIndexWriter(random(), d); @@ -211,7 +316,7 @@ DoubleDocValuesField field = new DoubleDocValuesField("field", 0.0); doc.add(field); for(long l=0;l<100;l++) { - field.setDoubleValue((double) l); + field.setDoubleValue(l); w.addDocument(doc); } @@ -218,15 +323,12 @@ IndexReader r = w.getReader(); w.close(); - FacetSearchParams fsp = new FacetSearchParams( - new RangeFacetRequest("field", - new DoubleRange("less than 10", 0.0, true, 10.0, false), - new DoubleRange("less than or equal to 10", 0.0, true, 10.0, true), - new DoubleRange("over 90", 90.0, false, 100.0, false), - new DoubleRange("90 or above", 90.0, true, 100.0, false), - new DoubleRange("over 1000", 1000.0, false, Double.POSITIVE_INFINITY, false))); - - RangeAccumulator a = new RangeAccumulator(fsp, r); + RangeAccumulator a = new RangeAccumulator(new RangeFacetRequest("field", + new DoubleRange("less than 10", 0.0, true, 10.0, false), + new DoubleRange("less than or equal to 10", 0.0, true, 10.0, true), + new DoubleRange("over 90", 90.0, false, 100.0, false), + new DoubleRange("90 or above", 90.0, true, 100.0, false), + new DoubleRange("over 1000", 1000.0, false, Double.POSITIVE_INFINITY, false))); FacetsCollector fc = FacetsCollector.create(a); @@ -247,7 +349,7 @@ FloatDocValuesField field = new FloatDocValuesField("field", 0.0f); doc.add(field); for(long l=0;l<100;l++) { - field.setFloatValue((float) l); + field.setFloatValue(l); w.addDocument(doc); } @@ -254,15 +356,12 @@ IndexReader r = w.getReader(); w.close(); - FacetSearchParams fsp = new FacetSearchParams( - new RangeFacetRequest("field", - new FloatRange("less than 10", 0.0f, true, 10.0f, false), - new FloatRange("less than or equal to 10", 0.0f, true, 10.0f, true), - new FloatRange("over 90", 90.0f, false, 100.0f, false), - new FloatRange("90 or above", 90.0f, true, 100.0f, false), - new FloatRange("over 1000", 1000.0f, false, Float.POSITIVE_INFINITY, false))); - - RangeAccumulator a = new RangeAccumulator(fsp, r); + RangeAccumulator a = new RangeAccumulator(new RangeFacetRequest("field", + new FloatRange("less than 10", 0.0f, true, 10.0f, false), + new FloatRange("less than or equal to 10", 0.0f, true, 10.0f, true), + new FloatRange("over 90", 90.0f, false, 100.0f, false), + new FloatRange("90 or above", 90.0f, true, 100.0f, false), + new FloatRange("over 1000", 1000.0f, false, Float.POSITIVE_INFINITY, false))); FacetsCollector fc = FacetsCollector.create(a); @@ -335,8 +434,7 @@ } } - FacetSearchParams fsp = new FacetSearchParams(new RangeFacetRequest("field", ranges)); - FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(fsp, r)); + FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(new RangeFacetRequest("field", ranges))); s.search(new MatchAllDocsQuery(), fc); List results = fc.getFacetResults(); assertEquals(1, results.size()); @@ -350,7 +448,7 @@ assertEquals("field/r" + rangeID, subNode.label.toString('/')); assertEquals(expectedCounts[rangeID], (int) subNode.value); - LongRange range = (LongRange) ((RangeFacetRequest) results.get(0).getFacetRequest()).ranges[rangeID]; + LongRange range = (LongRange) ((RangeFacetRequest) results.get(0).getFacetRequest()).ranges[rangeID]; // Test drill-down: DrillDownQuery ddq = new DrillDownQuery(FacetIndexingParams.DEFAULT); @@ -422,8 +520,7 @@ } } - FacetSearchParams fsp = new FacetSearchParams(new RangeFacetRequest("field", ranges)); - FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(fsp, r)); + FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(new RangeFacetRequest("field", ranges))); s.search(new MatchAllDocsQuery(), fc); List results = fc.getFacetResults(); assertEquals(1, results.size()); @@ -437,7 +534,7 @@ assertEquals("field/r" + rangeID, subNode.label.toString('/')); assertEquals(expectedCounts[rangeID], (int) subNode.value); - FloatRange range = (FloatRange) ((RangeFacetRequest) results.get(0).getFacetRequest()).ranges[rangeID]; + FloatRange range = (FloatRange) ((RangeFacetRequest) results.get(0).getFacetRequest()).ranges[rangeID]; // Test drill-down: DrillDownQuery ddq = new DrillDownQuery(FacetIndexingParams.DEFAULT); @@ -509,8 +606,7 @@ } } - FacetSearchParams fsp = new FacetSearchParams(new RangeFacetRequest("field", ranges)); - FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(fsp, r)); + FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(new RangeFacetRequest("field", ranges))); s.search(new MatchAllDocsQuery(), fc); List results = fc.getFacetResults(); assertEquals(1, results.size()); @@ -524,7 +620,7 @@ assertEquals("field/r" + rangeID, subNode.label.toString('/')); assertEquals(expectedCounts[rangeID], (int) subNode.value); - DoubleRange range = (DoubleRange) ((RangeFacetRequest) results.get(0).getFacetRequest()).ranges[rangeID]; + DoubleRange range = (DoubleRange) ((RangeFacetRequest) results.get(0).getFacetRequest()).ranges[rangeID]; // Test drill-down: DrillDownQuery ddq = new DrillDownQuery(FacetIndexingParams.DEFAULT); Index: lucene/facet/src/test/org/apache/lucene/facet/search/CountingFacetsAggregatorTest.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/CountingFacetsAggregatorTest.java (revision 1507979) +++ lucene/facet/src/test/org/apache/lucene/facet/search/CountingFacetsAggregatorTest.java (working copy) @@ -269,7 +269,7 @@ IOUtils.close(indexWriter, taxoWriter); } - private FacetsAccumulator randomAccumulator(FacetSearchParams fsp, IndexReader indexReader, TaxonomyReader taxoReader) { + private TaxonomyFacetsAccumulator randomAccumulator(FacetSearchParams fsp, IndexReader indexReader, TaxonomyReader taxoReader) { final FacetsAggregator aggregator; double val = random().nextDouble(); if (val < 0.6) { @@ -279,7 +279,7 @@ } else { aggregator = new CachedOrdsCountingFacetsAggregator(); } - return new FacetsAccumulator(fsp, indexReader, taxoReader) { + return new TaxonomyFacetsAccumulator(fsp, indexReader, taxoReader) { @Override public FacetsAggregator getAggregator() { return aggregator; Index: lucene/facet/src/test/org/apache/lucene/facet/search/FacetResultTest.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/FacetResultTest.java (revision 1507979) +++ lucene/facet/src/test/org/apache/lucene/facet/search/FacetResultTest.java (working copy) @@ -116,7 +116,7 @@ @Override protected FacetsAccumulator getDrillSidewaysAccumulator(String dim, FacetSearchParams fsp) throws IOException { FacetsAccumulator fa = super.getDrillSidewaysAccumulator(dim, fsp); - dimArrays.put(dim, fa.facetArrays); + dimArrays.put(dim, ((TaxonomyFacetsAccumulator) fa).facetArrays); return fa; } }; Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestDrillSideways.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestDrillSideways.java (revision 1507979) +++ lucene/facet/src/test/org/apache/lucene/facet/search/TestDrillSideways.java (working copy) @@ -336,6 +336,8 @@ String id; String contentToken; + public Doc() {} + // -1 if the doc is missing this dim, else the index // -into the values for this dim: int[] dims; @@ -790,15 +792,15 @@ Sort sort = new Sort(new SortField("id", SortField.Type.STRING)); DrillSideways ds; if (doUseDV) { - ds = new DrillSideways(s, null) { + ds = new DrillSideways(s, sortedSetDVState) { @Override protected FacetsAccumulator getDrillDownAccumulator(FacetSearchParams fsp) throws IOException { - return new SortedSetDocValuesAccumulator(fsp, sortedSetDVState); + return new SortedSetDocValuesAccumulator(sortedSetDVState, fsp); } @Override protected FacetsAccumulator getDrillSidewaysAccumulator(String dim, FacetSearchParams fsp) throws IOException { - return new SortedSetDocValuesAccumulator(fsp, sortedSetDVState); + return new SortedSetDocValuesAccumulator(sortedSetDVState, fsp); } }; } else { @@ -881,6 +883,7 @@ List hits; int[][] counts; int[] uniqueCounts; + public SimpleFacetResult() {} } private int[] getTopNOrds(final int[] counts, final String[] values, int topN) { Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestFacetsCollector.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestFacetsCollector.java (revision 1507979) +++ lucene/facet/src/test/org/apache/lucene/facet/search/TestFacetsCollector.java (working copy) @@ -3,9 +3,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.document.Document; @@ -90,7 +88,7 @@ DirectoryTaxonomyReader taxo = new DirectoryTaxonomyReader(taxoDir); FacetSearchParams sParams = new FacetSearchParams(new SumScoreFacetRequest(new CategoryPath("a"), 10)); - FacetsAccumulator fa = new FacetsAccumulator(sParams, r, taxo) { + TaxonomyFacetsAccumulator fa = new TaxonomyFacetsAccumulator(sParams, r, taxo) { @Override public FacetsAggregator getAggregator() { return new SumScoreFacetsAggregator(); @@ -181,18 +179,7 @@ new CountFacetRequest(new CategoryPath("a"), 10), new SumScoreFacetRequest(new CategoryPath("b"), 10)); - Map aggregators = new HashMap(); - aggregators.put(fip.getCategoryListParams(new CategoryPath("a")), new FastCountingFacetsAggregator()); - aggregators.put(fip.getCategoryListParams(new CategoryPath("b")), new SumScoreFacetsAggregator()); - final FacetsAggregator aggregator = new PerCategoryListAggregator(aggregators, fip); - FacetsAccumulator fa = new FacetsAccumulator(sParams, r, taxo) { - @Override - public FacetsAggregator getAggregator() { - return aggregator; - } - }; - - FacetsCollector fc = FacetsCollector.create(fa); + FacetsCollector fc = FacetsCollector.create(sParams, r, taxo); TopScoreDocCollector topDocs = TopScoreDocCollector.create(10, false); newSearcher(r).search(new MatchAllDocsQuery(), MultiCollector.wrap(fc, topDocs)); @@ -231,7 +218,7 @@ FacetSearchParams fsp = new FacetSearchParams(new CountFacetRequest(CategoryPath.EMPTY, 10)); - final FacetsAccumulator fa = random().nextBoolean() ? new FacetsAccumulator(fsp, r, taxo) : new StandardFacetsAccumulator(fsp, r, taxo); + final TaxonomyFacetsAccumulator fa = random().nextBoolean() ? new TaxonomyFacetsAccumulator(fsp, r, taxo) : new StandardFacetsAccumulator(fsp, r, taxo); FacetsCollector fc = FacetsCollector.create(fa); newSearcher(r).search(new MatchAllDocsQuery(), fc); @@ -265,7 +252,7 @@ FacetSearchParams fsp = new FacetSearchParams( new CountFacetRequest(new CategoryPath("a"), 10), new CountFacetRequest(new CategoryPath("b"), 10)); - final FacetsAccumulator fa = random().nextBoolean() ? new FacetsAccumulator(fsp, r, taxo) : new StandardFacetsAccumulator(fsp, r, taxo); + final TaxonomyFacetsAccumulator fa = random().nextBoolean() ? new TaxonomyFacetsAccumulator(fsp, r, taxo) : new StandardFacetsAccumulator(fsp, r, taxo); final FacetsCollector fc = FacetsCollector.create(fa); newSearcher(r).search(new MatchAllDocsQuery(), fc); @@ -297,7 +284,7 @@ FacetSearchParams fsp = new FacetSearchParams( new CountFacetRequest(new CategoryPath("a"), 10), new CountFacetRequest(new CategoryPath("b"), 10)); - final FacetsAccumulator fa = random().nextBoolean() ? new FacetsAccumulator(fsp, r, taxo) : new StandardFacetsAccumulator(fsp, r, taxo); + final TaxonomyFacetsAccumulator fa = random().nextBoolean() ? new TaxonomyFacetsAccumulator(fsp, r, taxo) : new StandardFacetsAccumulator(fsp, r, taxo); final FacetsCollector fc = FacetsCollector.create(fa); // this should populate the cached results, but doing search should clear the cache fc.getFacetResults(); @@ -338,7 +325,7 @@ // assert IntFacetResultHandler FacetSearchParams fsp = new FacetSearchParams(new CountFacetRequest(new CategoryPath("a"), 10)); - FacetsAccumulator fa = random().nextBoolean() ? new FacetsAccumulator(fsp, r, taxo) : new StandardFacetsAccumulator(fsp, r, taxo); + TaxonomyFacetsAccumulator fa = random().nextBoolean() ? new TaxonomyFacetsAccumulator(fsp, r, taxo) : new StandardFacetsAccumulator(fsp, r, taxo); FacetsCollector fc = FacetsCollector.create(fa); newSearcher(r).search(new MatchAllDocsQuery(), fc); assertTrue("invalid ordinal for child node: 0", 0 != fc.getFacetResults().get(0).getFacetResultNode().subResults.get(0).ordinal); @@ -346,7 +333,7 @@ // assert IntFacetResultHandler fsp = new FacetSearchParams(new SumScoreFacetRequest(new CategoryPath("a"), 10)); if (random().nextBoolean()) { - fa = new FacetsAccumulator(fsp, r, taxo) { + fa = new TaxonomyFacetsAccumulator(fsp, r, taxo) { @Override public FacetsAggregator getAggregator() { return new SumScoreFacetsAggregator(); @@ -387,7 +374,7 @@ CountFacetRequest cfr = new CountFacetRequest(new CategoryPath("a"), 2); cfr.setResultMode(random().nextBoolean() ? ResultMode.GLOBAL_FLAT : ResultMode.PER_NODE_IN_TREE); FacetSearchParams fsp = new FacetSearchParams(cfr); - final FacetsAccumulator fa = random().nextBoolean() ? new FacetsAccumulator(fsp, r, taxo) : new StandardFacetsAccumulator(fsp, r, taxo); + final TaxonomyFacetsAccumulator fa = random().nextBoolean() ? new TaxonomyFacetsAccumulator(fsp, r, taxo) : new StandardFacetsAccumulator(fsp, r, taxo); FacetsCollector fc = FacetsCollector.create(fa); newSearcher(r).search(new MatchAllDocsQuery(), fc); @@ -426,8 +413,8 @@ } final Sampler sampler = new RandomSampler(sampleParams, random()); - FacetsAccumulator[] accumulators = new FacetsAccumulator[] { - new FacetsAccumulator(fsp, indexReader, taxoReader), + TaxonomyFacetsAccumulator[] accumulators = new TaxonomyFacetsAccumulator[] { + new TaxonomyFacetsAccumulator(fsp, indexReader, taxoReader), new StandardFacetsAccumulator(fsp, indexReader, taxoReader), new SamplingAccumulator(sampler, fsp, indexReader, taxoReader), new AdaptiveFacetsAccumulator(fsp, indexReader, taxoReader), @@ -434,7 +421,7 @@ new SamplingWrapper(new StandardFacetsAccumulator(fsp, indexReader, taxoReader), sampler) }; - for (FacetsAccumulator fa : accumulators) { + for (TaxonomyFacetsAccumulator fa : accumulators) { FacetsCollector fc = FacetsCollector.create(fa); searcher.search(new MatchAllDocsQuery(), fc); List facetResults = fc.getFacetResults(); @@ -444,7 +431,7 @@ try { // SortedSetDocValuesAccumulator cannot even be created in such state - assertNull(new SortedSetDocValuesAccumulator(fsp, new SortedSetDocValuesReaderState(indexReader))); + assertNull(new SortedSetDocValuesAccumulator(new SortedSetDocValuesReaderState(indexReader), fsp)); // if this ever changes, make sure FacetResultNode is labeled correctly fail("should not have succeeded to execute a request over a category which wasn't indexed as SortedSetDVField"); } catch (IllegalArgumentException e) { @@ -451,13 +438,12 @@ // expected } - fsp = new FacetSearchParams(new RangeFacetRequest("f", new LongRange("grr", 0, true, 1, true))); - RangeAccumulator ra = new RangeAccumulator(fsp, indexReader); + RangeAccumulator ra = new RangeAccumulator(new RangeFacetRequest("f", new LongRange("grr", 0, true, 1, true))); FacetsCollector fc = FacetsCollector.create(ra); searcher.search(new MatchAllDocsQuery(), fc); List facetResults = fc.getFacetResults(); assertNotNull(facetResults); - assertEquals("incorrect label returned for RangeAccumulator", fsp.facetRequests.get(0).categoryPath, facetResults.get(0).getFacetResultNode().label); + assertEquals("incorrect label returned for RangeAccumulator", new CategoryPath("f"), facetResults.get(0).getFacetResultNode().label); IOUtils.close(indexReader, taxoReader); Index: lucene/facet/src/test/org/apache/lucene/facet/sortedset/TestSortedSetDocValuesFacets.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/sortedset/TestSortedSetDocValuesFacets.java (revision 1507979) +++ lucene/facet/src/test/org/apache/lucene/facet/sortedset/TestSortedSetDocValuesFacets.java (working copy) @@ -112,7 +112,7 @@ //SortedSetDocValuesCollector c = new SortedSetDocValuesCollector(state); //SortedSetDocValuesCollectorMergeBySeg c = new SortedSetDocValuesCollectorMergeBySeg(state); - FacetsCollector c = FacetsCollector.create(new SortedSetDocValuesAccumulator(fsp, state)); + FacetsCollector c = FacetsCollector.create(new SortedSetDocValuesAccumulator(state, fsp)); searcher.search(new MatchAllDocsQuery(), c); @@ -177,7 +177,7 @@ FacetSearchParams fsp = new FacetSearchParams(requests); - FacetsCollector c = FacetsCollector.create(new SortedSetDocValuesAccumulator(fsp, state)); + FacetsCollector c = FacetsCollector.create(new SortedSetDocValuesAccumulator(state, fsp)); searcher.search(new MatchAllDocsQuery(), c);