LUCENE-4985: Make it easier to mix different kinds of FacetRequests

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1508043 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Shai Erera 2013-07-29 13:07:30 +00:00
parent 503b2b29a6
commit bb1164b2fc
33 changed files with 879 additions and 607 deletions

View File

@ -70,6 +70,13 @@ New features
* LUCENE-5091: SpanNotQuery can now be configured with pre and post slop to act
as a hypothetical SpanNotNearQuery. (Tim Allison via David Smiley)
* LUCENE-4985: FacetsAccumulator.create() is now able to create a
MultiFacetsAccumulator over a mixed set of facet requests. MultiFacetsAccumulator
allows wrapping multiple FacetsAccumulators, allowing to easily mix
existing and custom ones. TaxonomyFacetsAccumulator supports any
FacetRequest which implements createFacetsAggregator and was indexed
using the taxonomy index. (Shai Erera)
Bug Fixes
* LUCENE-5116: IndexWriter.addIndexes(IndexReader...) should drop empty (or all

View File

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

View File

@ -27,7 +27,6 @@ import org.apache.lucene.document.Field;
import org.apache.lucene.document.LongField;
import org.apache.lucene.document.NumericDocValuesField;
import org.apache.lucene.facet.params.FacetIndexingParams;
import org.apache.lucene.facet.params.FacetSearchParams;
import org.apache.lucene.facet.range.LongRange;
import org.apache.lucene.facet.range.RangeAccumulator;
import org.apache.lucene.facet.range.RangeFacetRequest;
@ -80,13 +79,12 @@ public class RangeFacetsExample implements Closeable {
/** User runs a query and counts facets. */
public List<FacetResult> search() throws IOException {
FacetSearchParams fsp = new FacetSearchParams(
new RangeFacetRequest<LongRange>("timestamp",
new LongRange("Past hour", nowSec-3600, true, nowSec, true),
new LongRange("Past six hours", nowSec-6*3600, true, nowSec, true),
new LongRange("Past day", nowSec-24*3600, true, nowSec, true)));
RangeFacetRequest<LongRange> rangeFacetRequest = new RangeFacetRequest<LongRange>("timestamp",
new LongRange("Past hour", nowSec-3600, true, nowSec, true),
new LongRange("Past six hours", nowSec-6*3600, true, nowSec, true),
new LongRange("Past day", nowSec-24*3600, true, nowSec, true));
// Aggregatses the facet counts
FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(fsp, searcher.getIndexReader()));
FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(rangeFacetRequest));
// MatchAllDocsQuery is for "browsing" (counts facets
// for all non-deleted docs in the index); normally
@ -112,6 +110,7 @@ public class RangeFacetsExample implements Closeable {
return searcher.search(q, 10);
}
@Override
public void close() throws IOException {
searcher.getIndexReader().close();
indexDir.close();

View File

@ -91,7 +91,7 @@ public class SimpleSortedSetFacetsExample {
new CountFacetRequest(new CategoryPath("Author"), 10));
// Aggregatses the facet counts
FacetsCollector fc = FacetsCollector.create(new SortedSetDocValuesAccumulator(fsp, state));
FacetsCollector fc = FacetsCollector.create(new SortedSetDocValuesAccumulator(state, fsp));
// MatchAllDocsQuery is for "browsing" (counts facets
// for all non-deleted docs in the index); normally
@ -117,7 +117,7 @@ public class SimpleSortedSetFacetsExample {
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

View File

@ -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,16 +29,21 @@ import org.apache.lucene.facet.taxonomy.CategoryPath;
*
* @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];

View File

@ -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,16 +29,21 @@ import org.apache.lucene.facet.taxonomy.CategoryPath;
*
* @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;

View File

@ -23,13 +23,9 @@ import org.apache.lucene.facet.search.FacetRequest;
*/
/**
* Defines parameters that are needed for faceted search. The list of
* {@link FacetRequest facet requests} denotes the facets for which aggregated
* should be done.
* <p>
* One can pass {@link FacetIndexingParams} in order to tell the search code how
* to read the facets information. Note that you must use the same
* {@link FacetIndexingParams} that were used for indexing.
* Defines parameters that are needed for faceted search: the list of facet
* {@link FacetRequest facet requests} which should be aggregated as well as the
* {@link FacetIndexingParams indexing params} that were used to index them.
*
* @lucene.experimental
*/

View File

@ -19,6 +19,7 @@ package org.apache.lucene.facet.range;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.lucene.facet.params.FacetSearchParams;
@ -26,10 +27,8 @@ import org.apache.lucene.facet.search.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.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,30 +50,26 @@ public class RangeAccumulator extends FacetsAccumulator {
final List<RangeSet> requests = new ArrayList<RangeSet>();
public RangeAccumulator(FacetSearchParams fsp, IndexReader reader) {
super(fsp, reader, null, null);
for(FacetRequest fr : fsp.facetRequests) {
public RangeAccumulator(FacetRequest... facetRequests) {
this(Arrays.asList(facetRequests));
}
public RangeAccumulator(List<FacetRequest> facetRequests) {
super(new FacetSearchParams(facetRequests));
for (FacetRequest fr : facetRequests) {
if (!(fr instanceof RangeFacetRequest)) {
throw new IllegalArgumentException("only RangeFacetRequest is supported; got " + fsp.facetRequests.get(0).getClass());
throw new IllegalArgumentException("this accumulator only supports RangeFacetRequest; got " + fr);
}
if (fr.categoryPath.length != 1) {
throw new IllegalArgumentException("only flat (dimension only) CategoryPath is allowed");
}
RangeFacetRequest<?> rfr = (RangeFacetRequest<?>) fr;
requests.add(new RangeSet(rfr.ranges, rfr.categoryPath.components[0]));
requests.add(new RangeSet(rfr.ranges, fr.categoryPath.components[0]));
}
}
@Override
public FacetsAggregator getAggregator() {
throw new UnsupportedOperationException();
}
@Override
public List<FacetResult> accumulate(List<MatchingDocs> matchingDocs) throws IOException {
@ -82,7 +77,7 @@ public class RangeAccumulator extends FacetsAccumulator {
// faster to do MachingDocs on the inside) ... see
// patches on LUCENE-4965):
List<FacetResult> results = new ArrayList<FacetResult>();
for(int i=0;i<requests.size();i++) {
for (int i = 0; i < requests.size(); i++) {
RangeSet ranges = requests.get(i);
int[] counts = new int[ranges.ranges.length];
@ -100,7 +95,7 @@ public class RangeAccumulator extends FacetsAccumulator {
// (really, a specialized case of the interval
// tree)
// TODO: use interval tree instead of linear search:
for(int j=0;j<ranges.ranges.length;j++) {
for (int j = 0; j < ranges.ranges.length; j++) {
if (ranges.ranges[j].accept(v)) {
counts[j]++;
}

View File

@ -19,9 +19,11 @@ package org.apache.lucene.facet.range;
import java.util.List;
import org.apache.lucene.facet.params.FacetIndexingParams;
import org.apache.lucene.facet.search.Aggregator;
import org.apache.lucene.facet.search.FacetArrays;
import org.apache.lucene.facet.search.FacetRequest;
import org.apache.lucene.facet.search.FacetsAggregator;
import org.apache.lucene.facet.taxonomy.CategoryPath;
import org.apache.lucene.facet.taxonomy.TaxonomyReader;
@ -52,6 +54,11 @@ public class RangeFacetRequest<T extends Range> extends FacetRequest {
throw new UnsupportedOperationException();
}
@Override
public FacetsAggregator createFacetsAggregator(FacetIndexingParams fip) {
return null;
}
@Override
public double getValueOf(FacetArrays arrays, int ordinal) {
throw new UnsupportedOperationException();

View File

@ -1,117 +0,0 @@
package org.apache.lucene.facet.range;
/*
* 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.
*/
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.lucene.facet.params.CategoryListParams;
import org.apache.lucene.facet.params.FacetSearchParams;
import org.apache.lucene.facet.search.FacetArrays;
import org.apache.lucene.facet.search.FacetRequest;
import org.apache.lucene.facet.search.FacetResult;
import org.apache.lucene.facet.search.FacetResultsHandler;
import org.apache.lucene.facet.search.FacetsAccumulator;
import org.apache.lucene.facet.search.FacetsAggregator;
import org.apache.lucene.facet.search.FacetsCollector.MatchingDocs;
import org.apache.lucene.facet.taxonomy.TaxonomyReader;
import org.apache.lucene.index.IndexReader;
/** Takes multiple facet requests and if necessary splits
* them between the normal {@link FacetsAccumulator} and a
* {@link RangeAccumulator} */
public class RangeFacetsAccumulatorWrapper extends FacetsAccumulator {
// TODO: somehow handle SortedSetDVAccumulator as
// well... but it's tricky because SSDV just uses an
// "ordinary" flat CountFacetRequest so we can't switch
// based on that.
private final FacetsAccumulator accumulator;
private final RangeAccumulator rangeAccumulator;
public static FacetsAccumulator create(FacetSearchParams fsp, IndexReader indexReader, TaxonomyReader taxoReader) {
return create(fsp, indexReader, taxoReader, new FacetArrays(taxoReader.getSize()));
}
public static FacetsAccumulator create(FacetSearchParams fsp, IndexReader indexReader, TaxonomyReader taxoReader, FacetArrays arrays) {
List<FacetRequest> rangeRequests = new ArrayList<FacetRequest>();
List<FacetRequest> nonRangeRequests = new ArrayList<FacetRequest>();
for(FacetRequest fr : fsp.facetRequests) {
if (fr instanceof RangeFacetRequest) {
rangeRequests.add(fr);
} else {
nonRangeRequests.add(fr);
}
}
if (rangeRequests.isEmpty()) {
return new FacetsAccumulator(fsp, indexReader, taxoReader, arrays);
} else if (nonRangeRequests.isEmpty()) {
return new RangeAccumulator(fsp, indexReader);
} 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<CategoryListParams> getCategoryLists() {
throw new UnsupportedOperationException();
}
@Override
public boolean requiresDocScores() {
return accumulator.requiresDocScores();
}
public List<FacetResult> accumulate(List<MatchingDocs> matchingDocs) throws IOException {
List<FacetResult> results = accumulator.accumulate(matchingDocs);
List<FacetResult> rangeResults = rangeAccumulator.accumulate(matchingDocs);
int aUpto = 0;
int raUpto = 0;
List<FacetResult> merged = new ArrayList<FacetResult>();
for(FacetRequest fr : searchParams.facetRequests) {
if (fr instanceof RangeFacetRequest) {
merged.add(rangeResults.get(raUpto++));
} else {
merged.add(results.get(aUpto++));
}
}
return merged;
}
}

View File

@ -4,12 +4,14 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.facet.params.FacetIndexingParams;
import org.apache.lucene.facet.params.FacetSearchParams;
import org.apache.lucene.facet.search.Aggregator;
import org.apache.lucene.facet.search.FacetArrays;
import org.apache.lucene.facet.search.FacetRequest;
import org.apache.lucene.facet.search.FacetResult;
import org.apache.lucene.facet.search.FacetResultNode;
import org.apache.lucene.facet.search.FacetsAggregator;
import org.apache.lucene.facet.search.ScoredDocIDs;
import org.apache.lucene.facet.taxonomy.TaxonomyReader;
@ -214,6 +216,11 @@ public abstract class Sampler {
setSortOrder(orig.getSortOrder());
}
@Override
public FacetsAggregator createFacetsAggregator(FacetIndexingParams fip) {
return orig.createFacetsAggregator(fip);
}
@Override
public Aggregator createAggregator(boolean useComplements, FacetArrays arrays, TaxonomyReader taxonomy)
throws IOException {

View File

@ -1,6 +1,7 @@
package org.apache.lucene.facet.search;
import org.apache.lucene.facet.complements.ComplementCountingAggregator;
import org.apache.lucene.facet.params.FacetIndexingParams;
import org.apache.lucene.facet.taxonomy.CategoryPath;
import org.apache.lucene.facet.taxonomy.TaxonomyReader;
@ -32,6 +33,7 @@ public class CountFacetRequest extends FacetRequest {
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!
@ -42,6 +44,11 @@ public class CountFacetRequest extends FacetRequest {
return new CountingAggregator(a);
}
@Override
public FacetsAggregator createFacetsAggregator(FacetIndexingParams fip) {
return CountingFacetsAggregator.create(fip.getCategoryListParams(categoryPath));
}
@Override
public double getValueOf(FacetArrays arrays, int ordinal) {
return arrays.getIntArray()[ordinal];

View File

@ -2,6 +2,7 @@ package org.apache.lucene.facet.search;
import java.io.IOException;
import org.apache.lucene.facet.encoding.DGapVInt8IntDecoder;
import org.apache.lucene.facet.params.CategoryListParams;
import org.apache.lucene.facet.search.FacetsCollector.MatchingDocs;
import org.apache.lucene.util.IntsRef;
@ -34,6 +35,18 @@ import org.apache.lucene.util.IntsRef;
*/
public class CountingFacetsAggregator extends IntRollupFacetsAggregator {
/**
* Returns a {@link FacetsAggregator} suitable for counting categories given
* the {@link CategoryListParams}.
*/
public static FacetsAggregator create(CategoryListParams clp) {
if (clp.createEncoder().createMatchingDecoder().getClass() == DGapVInt8IntDecoder.class) {
return new FastCountingFacetsAggregator();
} else {
return new CountingFacetsAggregator();
}
}
private final IntsRef ordinals = new IntsRef(32);
@Override

View File

@ -25,7 +25,10 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.lucene.facet.index.FacetFields;
import org.apache.lucene.facet.params.FacetSearchParams;
import org.apache.lucene.facet.sortedset.SortedSetDocValuesFacetFields;
import org.apache.lucene.facet.sortedset.SortedSetDocValuesReaderState;
import org.apache.lucene.facet.taxonomy.TaxonomyReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
@ -70,11 +73,26 @@ public class DrillSideways {
protected final IndexSearcher searcher;
protected final TaxonomyReader taxoReader;
/** Create a new {@code DrillSideways} instance. */
protected final SortedSetDocValuesReaderState state;
/**
* Create a new {@code DrillSideways} instance, assuming the categories were
* indexed with {@link FacetFields}.
*/
public DrillSideways(IndexSearcher searcher, TaxonomyReader taxoReader) {
this.searcher = searcher;
this.taxoReader = taxoReader;
this.state = null;
}
/**
* Create a new {@code DrillSideways} instance, assuming the categories were
* indexed with {@link SortedSetDocValuesFacetFields}.
*/
public DrillSideways(IndexSearcher searcher, SortedSetDocValuesReaderState state) {
this.searcher = searcher;
this.taxoReader = null;
this.state = state;
}
/** Moves any drill-downs that don't have a corresponding
@ -440,13 +458,21 @@ public class DrillSideways {
/** Override this to use a custom drill-down {@link
* FacetsAccumulator}. */
protected FacetsAccumulator getDrillDownAccumulator(FacetSearchParams fsp) throws IOException {
return FacetsAccumulator.create(fsp, searcher.getIndexReader(), taxoReader);
if (taxoReader != null) {
return FacetsAccumulator.create(fsp, searcher.getIndexReader(), taxoReader, null);
} else {
return FacetsAccumulator.create(fsp, state, null);
}
}
/** Override this to use a custom drill-sideways {@link
* FacetsAccumulator}. */
protected FacetsAccumulator getDrillSidewaysAccumulator(String dim, FacetSearchParams fsp) throws IOException {
return FacetsAccumulator.create(fsp, searcher.getIndexReader(), taxoReader);
if (taxoReader != null) {
return FacetsAccumulator.create(fsp, searcher.getIndexReader(), taxoReader, null);
} else {
return FacetsAccumulator.create(fsp, state, null);
}
}
/** Override this and return true if your collector

View File

@ -3,6 +3,8 @@ package org.apache.lucene.facet.search;
import java.io.IOException;
import org.apache.lucene.facet.params.CategoryListParams.OrdinalPolicy;
import org.apache.lucene.facet.params.FacetIndexingParams;
import org.apache.lucene.facet.range.RangeFacetRequest;
import org.apache.lucene.facet.taxonomy.CategoryPath;
import org.apache.lucene.facet.taxonomy.TaxonomyReader;
@ -139,6 +141,15 @@ public abstract class FacetRequest {
"you should override FacetsAccumulator to return the proper FacetsAggregator");
}
/**
* Returns the {@link FacetsAggregator} which can aggregate the categories of
* this facet request. The aggregator is expected to aggregate category values
* into {@link FacetArrays}. If the facet request does not support that, e.g.
* {@link RangeFacetRequest}, it can return {@code null}. Note though that
* such requests require a dedicated {@link FacetsAccumulator}.
*/
public abstract FacetsAggregator createFacetsAggregator(FacetIndexingParams fip);
@Override
public boolean equals(Object o) {
if (o instanceof FacetRequest) {

View File

@ -7,6 +7,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.lucene.facet.params.FacetIndexingParams;
import org.apache.lucene.facet.taxonomy.CategoryPath;
import org.apache.lucene.facet.taxonomy.TaxonomyReader;
import org.apache.lucene.util.CollectionUtil;
@ -152,6 +153,11 @@ public class FacetResult {
}
}
FacetRequest dummy = new FacetRequest(min, frs.get(0).getFacetRequest().numResults) {
@Override
public FacetsAggregator createFacetsAggregator(FacetIndexingParams fip) {
throw new UnsupportedOperationException("not supported by this request");
}
@Override
public double getValueOf(FacetArrays arrays, int idx) {
throw new UnsupportedOperationException("not supported by this request");

View File

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

View File

@ -167,7 +167,7 @@ public abstract class FacetsCollector extends Collector {
* 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));
}
/**

View File

@ -5,7 +5,6 @@ import java.io.IOException;
import org.apache.lucene.facet.encoding.DGapVInt8IntDecoder;
import org.apache.lucene.facet.encoding.DGapVInt8IntEncoder;
import org.apache.lucene.facet.params.CategoryListParams;
import org.apache.lucene.facet.params.FacetSearchParams;
import org.apache.lucene.facet.search.FacetsCollector.MatchingDocs;
import org.apache.lucene.index.BinaryDocValues;
import org.apache.lucene.util.BytesRef;
@ -40,23 +39,6 @@ public final class FastCountingFacetsAggregator extends IntRollupFacetsAggregato
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 {

View File

@ -0,0 +1,69 @@
package org.apache.lucene.facet.search;
/*
* 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.
*/
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
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.FacetsCollector.MatchingDocs;
/**
* 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 {
return new MultiFacetsAccumulator(accumulators);
}
}
private MultiFacetsAccumulator(FacetsAccumulator... accumulators) {
super((FacetSearchParams) null);
this.accumulators = accumulators;
}
@Override
public boolean requiresDocScores() {
for (FacetsAccumulator fa : accumulators) {
if (fa.requiresDocScores()) {
return true;
}
}
return false;
}
@Override
public List<FacetResult> accumulate(List<MatchingDocs> matchingDocs) throws IOException {
List<FacetResult> merged = new ArrayList<FacetResult>();
for (FacetsAccumulator fa : accumulators) {
merged.addAll(fa.accumulate(matchingDocs));
}
return merged;
}
}

View File

@ -1,4 +1,4 @@
package org.apache.lucene.facet.associations;
package org.apache.lucene.facet.search;
import java.io.IOException;
import java.util.ArrayList;
@ -39,22 +39,21 @@ import org.apache.lucene.facet.taxonomy.CategoryPath;
*
* @lucene.experimental
*/
public class MultiAssociationsFacetsAggregator implements FacetsAggregator {
public class MultiFacetsAggregator implements FacetsAggregator {
private final Map<CategoryPath,FacetsAggregator> categoryAggregators;
private final List<FacetsAggregator> aggregators;
/**
* Creates a new {@link MultiAssociationsFacetsAggregator} over the given
* aggregators. The mapping is used by
* {@link #rollupValues(FacetRequest, int, int[], int[], FacetArrays)} to
* rollup the values of the specific category by the corresponding
* {@link FacetsAggregator}. However, since each {@link FacetsAggregator}
* handles the associations of a specific type, which could cover multiple
* categories, the aggregation is done on the unique set of aggregators, which
* are identified by their class.
* Constructor.
* <p>
* The mapping is used to rollup the values of the specific category by the
* corresponding {@link FacetsAggregator}. It is ok to pass differnet
* {@link FacetsAggregator} instances for each {@link CategoryPath} - the
* constructor ensures that each aggregator <u>type</u> (determined by its
* class) is invoked only once.
*/
public MultiAssociationsFacetsAggregator(Map<CategoryPath,FacetsAggregator> aggregators) {
public MultiFacetsAggregator(Map<CategoryPath,FacetsAggregator> aggregators) {
this.categoryAggregators = aggregators;
// make sure that each FacetsAggregator class is invoked only once, or

View File

@ -27,6 +27,9 @@ import org.apache.lucene.facet.search.FacetsCollector.MatchingDocs;
/**
* 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 {

View File

@ -43,7 +43,7 @@ import org.apache.lucene.util.IntsRef;
*/
/**
* Standard implementation for {@link FacetsAccumulator}, utilizing partitions to save on memory.
* Standard implementation for {@link TaxonomyFacetsAccumulator}, utilizing partitions to save on memory.
* <p>
* Why partitions? Because if there are say 100M categories out of which
* only top K are required, we must first compute value for all 100M categories
@ -64,7 +64,7 @@ import org.apache.lucene.util.IntsRef;
*
* @lucene.experimental
*/
public class StandardFacetsAccumulator extends FacetsAccumulator {
public class StandardFacetsAccumulator extends TaxonomyFacetsAccumulator {
private static final Logger logger = Logger.getLogger(StandardFacetsAccumulator.class.getName());
@ -96,15 +96,18 @@ public class StandardFacetsAccumulator extends FacetsAccumulator {
private double complementThreshold = DEFAULT_COMPLEMENT_THRESHOLD;
public StandardFacetsAccumulator(FacetSearchParams searchParams, IndexReader indexReader,
private static FacetArrays createFacetArrays(FacetSearchParams searchParams, TaxonomyReader taxoReader) {
return new FacetArrays(PartitionsUtils.partitionSize(searchParams.indexingParams, taxoReader));
}
public StandardFacetsAccumulator(FacetSearchParams searchParams, IndexReader indexReader,
TaxonomyReader taxonomyReader) {
this(searchParams, indexReader, taxonomyReader, new FacetArrays(
PartitionsUtils.partitionSize(searchParams.indexingParams, taxonomyReader)));
this(searchParams, indexReader, taxonomyReader, null);
}
public StandardFacetsAccumulator(FacetSearchParams searchParams, IndexReader indexReader,
TaxonomyReader taxonomyReader, FacetArrays facetArrays) {
super(searchParams, indexReader, taxonomyReader, facetArrays);
super(searchParams, indexReader, taxonomyReader, facetArrays == null ? createFacetArrays(searchParams, taxonomyReader) : facetArrays);
// can only be computed later when docids size is known
isUsingComplements = false;
@ -126,8 +129,7 @@ public class StandardFacetsAccumulator extends FacetsAccumulator {
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 {

View File

@ -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;
@ -33,6 +34,11 @@ public class SumScoreFacetRequest extends FacetRequest {
super(path, num);
}
@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";

View File

@ -0,0 +1,225 @@
package org.apache.lucene.facet.search;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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.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;
/*
* 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 FacetsAccumulator} suitable for accumulating categories that were
* indexed into a taxonomy index.
*
* @lucene.experimental
*/
public class TaxonomyFacetsAccumulator extends FacetsAccumulator {
public final TaxonomyReader taxonomyReader;
public final IndexReader indexReader;
public final FacetArrays facetArrays;
/**
* 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 #TaxonomyFacetsAccumulator(FacetSearchParams, IndexReader, TaxonomyReader)}
* constructor.
*/
public TaxonomyFacetsAccumulator(FacetSearchParams searchParams, IndexReader indexReader,
TaxonomyReader taxonomyReader) {
this(searchParams, indexReader, taxonomyReader, null);
}
/**
* 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 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;
}
/** Group all requests that belong to the same {@link CategoryListParams}. */
protected Map<CategoryListParams,List<FacetRequest>> groupRequests() {
if (searchParams.indexingParams.getAllCategoryListParams().size() == 1) {
return Collections.singletonMap(searchParams.indexingParams.getCategoryListParams(null), searchParams.facetRequests);
}
HashMap<CategoryListParams,List<FacetRequest>> requestsPerCLP = new HashMap<CategoryListParams,List<FacetRequest>>();
for (FacetRequest fr : searchParams.facetRequests) {
CategoryListParams clp = searchParams.indexingParams.getCategoryListParams(fr.categoryPath);
List<FacetRequest> requests = requestsPerCLP.get(clp);
if (requests == null) {
requests = new ArrayList<FacetRequest>();
requestsPerCLP.put(clp, requests);
}
requests.add(fr);
}
return requestsPerCLP;
}
/**
* Returns the {@link FacetsAggregator} to use for aggregating the categories
* found in the result documents.
*/
public FacetsAggregator getAggregator() {
Map<CategoryListParams,List<FacetRequest>> requestsPerCLP = groupRequests();
// optimize for all-CountFacetRequest and single category list (common case)
if (requestsPerCLP.size() == 1) {
boolean allCount = true;
for (FacetRequest fr : searchParams.facetRequests) {
if (!(fr instanceof CountFacetRequest)) {
allCount = false;
break;
}
}
if (allCount) {
return requestsPerCLP.values().iterator().next().get(0).createFacetsAggregator(searchParams.indexingParams);
}
}
// If we're here it means the facet requests are spread across multiple
// category lists, or there are multiple types of facet requests, or both.
// Therefore create a per-CategoryList mapping of FacetsAggregators.
Map<CategoryListParams,FacetsAggregator> perCLPAggregator = new HashMap<CategoryListParams,FacetsAggregator>();
for (Entry<CategoryListParams,List<FacetRequest>> e : requestsPerCLP.entrySet()) {
CategoryListParams clp = e.getKey();
List<FacetRequest> requests = e.getValue();
Map<Class<? extends FacetsAggregator>,FacetsAggregator> aggClasses = new HashMap<Class<? extends FacetsAggregator>,FacetsAggregator>();
Map<CategoryPath,FacetsAggregator> perCategoryAggregator = new HashMap<CategoryPath,FacetsAggregator>();
for (FacetRequest fr : requests) {
FacetsAggregator fa = fr.createFacetsAggregator(searchParams.indexingParams);
if (fa == null) {
throw new IllegalArgumentException("this accumulator only supports requests that create a FacetsAggregator: " + fr);
}
Class<? extends FacetsAggregator> faClass = fa.getClass();
if (!aggClasses.containsKey(faClass)) {
aggClasses.put(faClass, fa);
} else {
fa = aggClasses.get(faClass);
}
perCategoryAggregator.put(fr.categoryPath, fa);
}
if (aggClasses.size() == 1) { // only one type of facet request
perCLPAggregator.put(clp, aggClasses.values().iterator().next());
} else {
perCLPAggregator.put(clp, new MultiFacetsAggregator(perCategoryAggregator));
}
}
return new PerCategoryListAggregator(perCLPAggregator, searchParams.indexingParams);
}
/**
* 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);
}
/**
* 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.
*
* @param matchingDocs
* the documents that matched the query, per-segment.
*/
@Override
public List<FacetResult> accumulate(List<MatchingDocs> matchingDocs) throws IOException {
// aggregate facets per category list (usually onle one category list)
FacetsAggregator aggregator = getAggregator();
for (CategoryListParams clp : groupRequests().keySet()) {
for (MatchingDocs md : matchingDocs) {
aggregator.aggregate(md, clp, facetArrays);
}
}
ParallelTaxonomyArrays arrays = taxonomyReader.getParallelTaxonomyArrays();
// compute top-K
final int[] children = arrays.children();
final int[] siblings = arrays.siblings();
List<FacetResult> res = new ArrayList<FacetResult>();
for (FacetRequest fr : searchParams.facetRequests) {
int rootOrd = taxonomyReader.getOrdinal(fr.categoryPath);
if (rootOrd == TaxonomyReader.INVALID_ORDINAL) { // category does not exist
// Add empty FacetResult
res.add(emptyResult(rootOrd, fr));
continue;
}
CategoryListParams clp = searchParams.indexingParams.getCategoryListParams(fr.categoryPath);
if (fr.categoryPath.length > 0) { // someone might ask to aggregate the ROOT category
OrdinalPolicy ordinalPolicy = clp.getOrdinalPolicy(fr.categoryPath.components[0]);
if (ordinalPolicy == OrdinalPolicy.NO_PARENTS) {
// rollup values
aggregator.rollupValues(fr, rootOrd, children, siblings, facetArrays);
}
}
FacetResultsHandler frh = createFacetResultsHandler(fr);
res.add(frh.compute());
}
return res;
}
@Override
public boolean requiresDocScores() {
return getAggregator().requiresDocScores();
}
}

View File

@ -32,19 +32,19 @@ 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.FacetsAggregator;
import org.apache.lucene.facet.search.FacetsCollector.MatchingDocs;
import org.apache.lucene.facet.search.TaxonomyFacetsAccumulator;
import org.apache.lucene.facet.taxonomy.CategoryPath;
import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.MultiDocValues.MultiSortedSetDocValues;
import org.apache.lucene.index.MultiDocValues;
import org.apache.lucene.index.MultiDocValues.MultiSortedSetDocValues;
import org.apache.lucene.index.ReaderUtil;
import org.apache.lucene.index.SortedSetDocValues;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.PriorityQueue;
/** A {@link FacetsAccumulator} that uses previously
/** A {@link TaxonomyFacetsAccumulator} that uses previously
* indexed {@link SortedSetDocValuesFacetFields} to perform faceting,
* without require a separate taxonomy index. Faceting is
* a bit slower (~25%), and there is added cost on every
@ -57,25 +57,34 @@ public class SortedSetDocValuesAccumulator extends FacetsAccumulator {
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,105 +93,6 @@ public class SortedSetDocValuesAccumulator extends FacetsAccumulator {
}
}
@Override
public FacetsAggregator getAggregator() {
return new FacetsAggregator() {
@Override
public void aggregate(MatchingDocs matchingDocs, CategoryListParams clp, FacetArrays facetArrays) throws IOException {
AtomicReader reader = matchingDocs.context.reader();
// LUCENE-5090: make sure the provided reader context "matches"
// the top-level reader passed to the
// SortedSetDocValuesReaderState, else cryptic
// AIOOBE can happen:
if (ReaderUtil.getTopLevelContext(matchingDocs.context).reader() != state.origReader) {
throw new IllegalStateException("the SortedSetDocValuesReaderState provided to this class does not match the reader being searched; you must create a new SortedSetDocValuesReaderState every time you open a new IndexReader");
}
SortedSetDocValues segValues = reader.getSortedSetDocValues(field);
if (segValues == null) {
return;
}
final int[] counts = facetArrays.getIntArray();
final int maxDoc = reader.maxDoc();
assert maxDoc == matchingDocs.bits.length();
if (dv instanceof MultiSortedSetDocValues) {
MultiDocValues.OrdinalMap ordinalMap = ((MultiSortedSetDocValues) dv).mapping;
int segOrd = matchingDocs.context.ord;
int numSegOrds = (int) segValues.getValueCount();
if (matchingDocs.totalHits < numSegOrds/10) {
// Remap every ord to global ord as we iterate:
int doc = 0;
while (doc < maxDoc && (doc = matchingDocs.bits.nextSetBit(doc)) != -1) {
segValues.setDocument(doc);
int term = (int) segValues.nextOrd();
while (term != SortedSetDocValues.NO_MORE_ORDS) {
counts[(int) ordinalMap.getGlobalOrd(segOrd, term)]++;
term = (int) segValues.nextOrd();
}
++doc;
}
} else {
// 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;
}
// Then, migrate to global ords:
for(int ord=0;ord<numSegOrds;ord++) {
int count = segCounts[ord];
if (count != 0) {
counts[(int) ordinalMap.getGlobalOrd(segOrd, ord)] += count;
}
}
}
} else {
// No ord mapping (e.g., single segment index):
// just aggregate directly into counts:
int doc = 0;
while (doc < maxDoc && (doc = matchingDocs.bits.nextSetBit(doc)) != -1) {
segValues.setDocument(doc);
int term = (int) segValues.nextOrd();
while (term != SortedSetDocValues.NO_MORE_ORDS) {
counts[term]++;
term = (int) segValues.nextOrd();
}
++doc;
}
}
}
@Override
public void rollupValues(FacetRequest fr, int ordinal, int[] children, int[] siblings, FacetArrays facetArrays) {
// Nothing to do here: we only support flat (dim +
// label) facets, and in accumulate we sum up the
// count for the dimension.
}
@Override
public boolean requiresDocScores() {
return false;
}
};
}
/** Keeps highest count results. */
static class TopCountPQ extends PriorityQueue<FacetResultNode> {
public TopCountPQ(int topN) {
@ -201,14 +111,105 @@ public class SortedSetDocValuesAccumulator extends FacetsAccumulator {
}
}
static class SortedSetAggregator {
private final SortedSetDocValuesReaderState state;
private final String field;
private final SortedSetDocValues dv;
public SortedSetAggregator(String field, SortedSetDocValuesReaderState state, SortedSetDocValues dv) {
this.field = field;
this.state = state;
this.dv = dv;
}
public void aggregate(MatchingDocs matchingDocs, FacetArrays facetArrays) throws IOException {
AtomicReader reader = matchingDocs.context.reader();
// LUCENE-5090: make sure the provided reader context "matches"
// the top-level reader passed to the
// SortedSetDocValuesReaderState, else cryptic
// AIOOBE can happen:
if (ReaderUtil.getTopLevelContext(matchingDocs.context).reader() != state.origReader) {
throw new IllegalStateException("the SortedSetDocValuesReaderState provided to this class does not match the reader being searched; you must create a new SortedSetDocValuesReaderState every time you open a new IndexReader");
}
SortedSetDocValues segValues = reader.getSortedSetDocValues(field);
if (segValues == null) {
return;
}
final int[] counts = facetArrays.getIntArray();
final int maxDoc = reader.maxDoc();
assert maxDoc == matchingDocs.bits.length();
if (dv instanceof MultiSortedSetDocValues) {
MultiDocValues.OrdinalMap ordinalMap = ((MultiSortedSetDocValues) dv).mapping;
int segOrd = matchingDocs.context.ord;
int numSegOrds = (int) segValues.getValueCount();
if (matchingDocs.totalHits < numSegOrds/10) {
// Remap every ord to global ord as we iterate:
int doc = 0;
while (doc < maxDoc && (doc = matchingDocs.bits.nextSetBit(doc)) != -1) {
segValues.setDocument(doc);
int term = (int) segValues.nextOrd();
while (term != SortedSetDocValues.NO_MORE_ORDS) {
counts[(int) ordinalMap.getGlobalOrd(segOrd, term)]++;
term = (int) segValues.nextOrd();
}
++doc;
}
} else {
// 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;
}
// Then, migrate to global ords:
for(int ord=0;ord<numSegOrds;ord++) {
int count = segCounts[ord];
if (count != 0) {
counts[(int) ordinalMap.getGlobalOrd(segOrd, ord)] += count;
}
}
}
} else {
// No ord mapping (e.g., single segment index):
// just aggregate directly into counts:
int doc = 0;
while (doc < maxDoc && (doc = matchingDocs.bits.nextSetBit(doc)) != -1) {
segValues.setDocument(doc);
int term = (int) segValues.nextOrd();
while (term != SortedSetDocValues.NO_MORE_ORDS) {
counts[term]++;
term = (int) segValues.nextOrd();
}
++doc;
}
}
}
}
@Override
public List<FacetResult> accumulate(List<MatchingDocs> matchingDocs) throws IOException {
FacetsAggregator aggregator = getAggregator();
for (CategoryListParams clp : getCategoryLists()) {
for (MatchingDocs md : matchingDocs) {
aggregator.aggregate(md, clp, facetArrays);
}
SortedSetAggregator aggregator = new SortedSetAggregator(field, state, dv);
for (MatchingDocs md : matchingDocs) {
aggregator.aggregate(md, facetArrays);
}
// compute top-K
@ -218,7 +219,7 @@ public class SortedSetDocValuesAccumulator extends FacetsAccumulator {
BytesRef scratch = new BytesRef();
for(FacetRequest request : searchParams.facetRequests) {
for (FacetRequest request : searchParams.facetRequests) {
String dim = request.categoryPath.components[0];
SortedSetDocValuesReaderState.OrdRange ordRange = state.getOrdRange(dim);
// checked in ctor:
@ -315,4 +316,10 @@ public class SortedSetDocValuesAccumulator extends FacetsAccumulator {
return results;
}
@Override
public boolean requiresDocScores() {
return false;
}
}

View File

@ -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.document.Document;
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 @@ public class AssociationsFacetRequestTest extends FacetTestCase {
// 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 @@ public class AssociationsFacetRequestTest extends FacetTestCase {
// 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 @@ public class AssociationsFacetRequestTest extends FacetTestCase {
// 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<CategoryPath,FacetsAggregator> aggregators = new HashMap<CategoryPath,FacetsAggregator>();
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);

View File

@ -17,6 +17,7 @@ package org.apache.lucene.facet.range;
* 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.FacetIndexingParams;
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.DrillSidewaysResult;
import org.apache.lucene.facet.search.DrillSideways;
import org.apache.lucene.facet.search.DrillSideways.DrillSidewaysResult;
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 @@ public class TestRangeAccumulator extends FacetTestCase {
IndexReader r = w.getReader();
w.close();
FacetSearchParams fsp = new FacetSearchParams(
new RangeFacetRequest<LongRange>("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<LongRange>("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,15 +97,15 @@ public class TestRangeAccumulator extends FacetTestCase {
}
/** 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();
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 @@ public class TestRangeAccumulator extends FacetTestCase {
w.addDocument(doc);
}
IndexReader r = w.getReader();
final IndexReader r = w.getReader();
w.close();
final TaxonomyReader tr = new DirectoryTaxonomyReader(tw);
@ -130,32 +130,32 @@ public class TestRangeAccumulator extends FacetTestCase {
IndexSearcher s = newSearcher(r);
final FacetSearchParams fsp = new FacetSearchParams(
new CountFacetRequest(new CategoryPath("dim"), 2),
new RangeFacetRequest<LongRange>("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<LongRange> rangeRequest = new RangeFacetRequest<LongRange>("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<String> dimSeen = new HashSet<String>();
DrillSideways ds = new DrillSideways(s, tr) {
@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 @@ public class TestRangeAccumulator extends FacetTestCase {
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<LongRange> rangeRequest = new RangeFacetRequest<LongRange>("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<String> dimSeen = new HashSet<String>();
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,22 +316,19 @@ public class TestRangeAccumulator extends FacetTestCase {
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);
}
IndexReader r = w.getReader();
w.close();
FacetSearchParams fsp = new FacetSearchParams(
new RangeFacetRequest<DoubleRange>("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<DoubleRange>("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,22 +349,19 @@ public class TestRangeAccumulator extends FacetTestCase {
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);
}
IndexReader r = w.getReader();
w.close();
FacetSearchParams fsp = new FacetSearchParams(
new RangeFacetRequest<FloatRange>("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<FloatRange>("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 @@ public class TestRangeAccumulator extends FacetTestCase {
}
}
FacetSearchParams fsp = new FacetSearchParams(new RangeFacetRequest<LongRange>("field", ranges));
FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(fsp, r));
FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(new RangeFacetRequest<LongRange>("field", ranges)));
s.search(new MatchAllDocsQuery(), fc);
List<FacetResult> results = fc.getFacetResults();
assertEquals(1, results.size());
@ -350,7 +448,7 @@ public class TestRangeAccumulator extends FacetTestCase {
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 @@ public class TestRangeAccumulator extends FacetTestCase {
}
}
FacetSearchParams fsp = new FacetSearchParams(new RangeFacetRequest<FloatRange>("field", ranges));
FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(fsp, r));
FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(new RangeFacetRequest<FloatRange>("field", ranges)));
s.search(new MatchAllDocsQuery(), fc);
List<FacetResult> results = fc.getFacetResults();
assertEquals(1, results.size());
@ -437,7 +534,7 @@ public class TestRangeAccumulator extends FacetTestCase {
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 @@ public class TestRangeAccumulator extends FacetTestCase {
}
}
FacetSearchParams fsp = new FacetSearchParams(new RangeFacetRequest<DoubleRange>("field", ranges));
FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(fsp, r));
FacetsCollector fc = FacetsCollector.create(new RangeAccumulator(new RangeFacetRequest<DoubleRange>("field", ranges)));
s.search(new MatchAllDocsQuery(), fc);
List<FacetResult> results = fc.getFacetResults();
assertEquals(1, results.size());
@ -524,7 +620,7 @@ public class TestRangeAccumulator extends FacetTestCase {
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);

View File

@ -269,7 +269,7 @@ public class CountingFacetsAggregatorTest extends FacetTestCase {
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 @@ public class CountingFacetsAggregatorTest extends FacetTestCase {
} else {
aggregator = new CachedOrdsCountingFacetsAggregator();
}
return new FacetsAccumulator(fsp, indexReader, taxoReader) {
return new TaxonomyFacetsAccumulator(fsp, indexReader, taxoReader) {
@Override
public FacetsAggregator getAggregator() {
return aggregator;

View File

@ -116,7 +116,7 @@ public class FacetResultTest extends FacetTestCase {
@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;
}
};

View File

@ -41,7 +41,6 @@ import org.apache.lucene.facet.index.FacetFields;
import org.apache.lucene.facet.params.FacetIndexingParams;
import org.apache.lucene.facet.params.FacetSearchParams;
import org.apache.lucene.facet.search.DrillSideways.DrillSidewaysResult;
import org.apache.lucene.facet.sortedset.SortedSetDocValuesAccumulator;
import org.apache.lucene.facet.sortedset.SortedSetDocValuesFacetFields;
import org.apache.lucene.facet.sortedset.SortedSetDocValuesReaderState;
import org.apache.lucene.facet.taxonomy.CategoryPath;
@ -62,8 +61,8 @@ import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField.Type;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.SortField.Type;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
@ -336,6 +335,8 @@ public class TestDrillSideways extends FacetTestCase {
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,17 +791,7 @@ public class TestDrillSideways extends FacetTestCase {
Sort sort = new Sort(new SortField("id", SortField.Type.STRING));
DrillSideways ds;
if (doUseDV) {
ds = new DrillSideways(s, null) {
@Override
protected FacetsAccumulator getDrillDownAccumulator(FacetSearchParams fsp) throws IOException {
return new SortedSetDocValuesAccumulator(fsp, sortedSetDVState);
}
@Override
protected FacetsAccumulator getDrillSidewaysAccumulator(String dim, FacetSearchParams fsp) throws IOException {
return new SortedSetDocValuesAccumulator(fsp, sortedSetDVState);
}
};
ds = new DrillSideways(s, sortedSetDVState);
} else {
ds = new DrillSideways(s, tr);
}
@ -881,6 +872,7 @@ public class TestDrillSideways extends FacetTestCase {
List<Doc> hits;
int[][] counts;
int[] uniqueCounts;
public SimpleFacetResult() {}
}
private int[] getTopNOrds(final int[] counts, final String[] values, int topN) {

View File

@ -3,9 +3,7 @@ package org.apache.lucene.facet.search;
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 @@ public class TestFacetsCollector extends FacetTestCase {
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 @@ public class TestFacetsCollector extends FacetTestCase {
new CountFacetRequest(new CategoryPath("a"), 10),
new SumScoreFacetRequest(new CategoryPath("b"), 10));
Map<CategoryListParams,FacetsAggregator> aggregators = new HashMap<CategoryListParams,FacetsAggregator>();
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 @@ public class TestFacetsCollector extends FacetTestCase {
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 @@ public class TestFacetsCollector extends FacetTestCase {
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 @@ public class TestFacetsCollector extends FacetTestCase {
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 @@ public class TestFacetsCollector extends FacetTestCase {
// 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 @@ public class TestFacetsCollector extends FacetTestCase {
// 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 @@ public class TestFacetsCollector extends FacetTestCase {
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,15 +413,15 @@ public class TestFacetsCollector extends FacetTestCase {
}
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),
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<FacetResult> facetResults = fc.getFacetResults();
@ -444,20 +431,19 @@ public class TestFacetsCollector extends FacetTestCase {
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) {
// expected
}
fsp = new FacetSearchParams(new RangeFacetRequest<LongRange>("f", new LongRange("grr", 0, true, 1, true)));
RangeAccumulator ra = new RangeAccumulator(fsp, indexReader);
RangeAccumulator ra = new RangeAccumulator(new RangeFacetRequest<LongRange>("f", new LongRange("grr", 0, true, 1, true)));
FacetsCollector fc = FacetsCollector.create(ra);
searcher.search(new MatchAllDocsQuery(), fc);
List<FacetResult> 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);

View File

@ -112,7 +112,7 @@ public class TestSortedSetDocValuesFacets extends FacetTestCase {
//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 @@ public class TestSortedSetDocValuesFacets extends FacetTestCase {
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);