Index: lucene/facet/src/examples/org/apache/lucene/facet/example/multiCL/MultiCLSearcher.java =================================================================== --- lucene/facet/src/examples/org/apache/lucene/facet/example/multiCL/MultiCLSearcher.java (revision 1435786) +++ lucene/facet/src/examples/org/apache/lucene/facet/example/multiCL/MultiCLSearcher.java (working copy) @@ -109,8 +109,7 @@ // behavior - in those // situations other, more low-level interfaces are available, as // demonstrated in other search examples. - FacetsCollector facetsCollector = new FacetsCollector( - facetSearchParams, indexReader, taxo); + FacetsCollector facetsCollector = FacetsCollector.create(facetSearchParams, indexReader, taxo); // perform documents search and facets accumulation searcher.search(q, MultiCollector.wrap(topDocsCollector, facetsCollector)); Index: lucene/facet/src/examples/org/apache/lucene/facet/example/simple/SimpleSearcher.java =================================================================== --- lucene/facet/src/examples/org/apache/lucene/facet/example/simple/SimpleSearcher.java (revision 1435786) +++ lucene/facet/src/examples/org/apache/lucene/facet/example/simple/SimpleSearcher.java (working copy) @@ -104,9 +104,9 @@ } // Faceted search parameters indicate which facets are we interested in - FacetSearchParams facetSearchParams = new FacetSearchParams(Arrays.asList(facetRequests), indexingParams); + FacetSearchParams facetSearchParams = new FacetSearchParams(indexingParams, facetRequests); - FacetsCollector facetsCollector = new FacetsCollector(facetSearchParams, indexReader, taxoReader); + FacetsCollector facetsCollector = FacetsCollector.create(facetSearchParams, indexReader, taxoReader); // perform documents search and facets accumulation searcher.search(q, MultiCollector.wrap(topDocsCollector, facetsCollector)); Index: lucene/facet/src/java/org/apache/lucene/facet/search/CountingFacetsCollector.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/CountingFacetsCollector.java (revision 0) +++ lucene/facet/src/java/org/apache/lucene/facet/search/CountingFacetsCollector.java (working copy) @@ -0,0 +1,327 @@ +package org.apache.lucene.facet.search; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.lucene.facet.index.categorypolicy.OrdinalPolicy; +import org.apache.lucene.facet.index.params.CategoryListParams; +import org.apache.lucene.facet.index.params.FacetIndexingParams; +import org.apache.lucene.facet.search.params.CountFacetRequest; +import org.apache.lucene.facet.search.params.FacetRequest; +import org.apache.lucene.facet.search.params.FacetSearchParams; +import org.apache.lucene.facet.search.results.FacetResult; +import org.apache.lucene.facet.search.results.FacetResultNode; +import org.apache.lucene.facet.taxonomy.CategoryPath; +import org.apache.lucene.facet.taxonomy.TaxonomyReader; +import org.apache.lucene.facet.taxonomy.directory.ParallelTaxonomyArrays; +import org.apache.lucene.index.AtomicReaderContext; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.search.Collector; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.PriorityQueue; +import org.apache.lucene.util.encoding.DGapVInt8IntDecoder; + +/* + * 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 Collector} which counts facets associated with matching documents. + * This {@link Collector} can be used only in the following conditions: + * + */ +public class CountingFacetsCollector extends FacetsCollector { + + private final FacetSearchParams fsp; + private final TaxonomyReader taxoReader; + private final BytesRef buf = new BytesRef(32); + private final FacetArrays facetArrays; + private final int[] counts; + private final String facetsField; + + private DocValues.Source facetsSource; + + public CountingFacetsCollector(FacetSearchParams fsp, TaxonomyReader taxoReader) { + this(fsp, taxoReader, new FacetArrays(taxoReader.getSize())); + } + + public CountingFacetsCollector(FacetSearchParams fsp, TaxonomyReader taxoReader, FacetArrays facetArrays) { + assert facetArrays.arrayLength >= taxoReader.getSize() : "too small facet array"; + assert assertParams(fsp) == null : assertParams(fsp); + + this.fsp = fsp; + this.taxoReader = taxoReader; + this.facetArrays = facetArrays; + this.counts = facetArrays.getIntArray(); + this.facetsField = fsp.indexingParams.getCategoryListParams(null).field; + } + + /** + * Asserts that this {@link FacetsCollector} can handle the given + * {@link FacetSearchParams}. Returns {@code null} if true, otherwise an error + * message. + */ + static String assertParams(FacetSearchParams fsp) { + // verify that all facet requests are CountFacetRequest + for (FacetRequest fr : fsp.facetRequests) { + if (!(fr instanceof CountFacetRequest)) { + return "all FacetRequests must be CountFacetRequest"; + } + if (fr.getDepth() != 1) { + return "all requests must be of depth 1"; + } + if (fr.getNumLabel() < fr.getNumResults()) { + return "this Collector always labels all requested results"; + } + } + + // verify that there's only one CategoryListParams + List clps = fsp.indexingParams.getAllCategoryListParams(); + if (clps.size() != 1) { + return "this Collector supports only one CategoryListParams"; + } + + // verify DGapVInt decoder + CategoryListParams clp = clps.get(0); + if (clp.createEncoder().createMatchingDecoder().getClass() != DGapVInt8IntDecoder.class) { + return "this Collector supports only DGap + VInt encoding"; + } + + // verify that partitions are disabled + if (fsp.indexingParams.getPartitionSize() != Integer.MAX_VALUE) { + return "this Collector does not support partitions"; + } + + // verify that all parents are indexed + if (fsp.indexingParams.getOrdinalPolicy() != OrdinalPolicy.ALL_PARENTS) { + return "this Collector assumes that all of a category's parents are indexed"; + } + + return null; + } + + @Override + public void setNextReader(AtomicReaderContext context) throws IOException { + DocValues dv = context.reader().docValues(facetsField); + if (dv == null) { + facetsSource = null; + } else { + facetsSource = dv.getSource(); + } + } + + @Override + public void collect(int doc) throws IOException { + if (facetsSource == null) { + return; + } + + facetsSource.getBytes(doc, buf); + if (buf.length == 0) { + // this document has no facets + } + + int upto = buf.offset + buf.length; + int ord = 0; + int offset = buf.offset; + int prev = 0; + while (offset < upto) { + byte b = buf.bytes[offset++]; + if (b >= 0) { + prev = ord = ((ord << 7) | b) + prev; + counts[ord]++; + ord = 0; + } else { + ord = (ord << 7) | (b & 0x7F); + } + } + } + + @Override + public synchronized List getFacetResults() throws IOException { + try { + ParallelTaxonomyArrays arrays = taxoReader.getParallelTaxonomyArrays(); + final int[] children = arrays.children(); + final int[] siblings = arrays.siblings(); + List res = new ArrayList(); + for (FacetRequest fr : fsp.facetRequests) { + int rootOrd = taxoReader.getOrdinal(fr.categoryPath); + if (rootOrd == TaxonomyReader.INVALID_ORDINAL) { // category does not exist + continue; + } + // since we use sentinel objects, we cannot reuse PQ. but that's ok because it's not big + // nocommit specialize for Integer.MAX_VALUE? Or any value that's larger than taxoSize? + FacetResultNodeQueue pq = new FacetResultNodeQueue(fr.getNumResults(), true); + FacetResultNodeImpl top = pq.top(); + int child = children[rootOrd]; + int numResults = 0; // count the number of results + int residue = 0; + while (child != TaxonomyReader.INVALID_ORDINAL) { + int count = counts[child]; + if (count > top.value) { + residue += top.value; + top.value = count; + top.ordinal = child; + top = pq.updateTop(); + ++numResults; + } else { + residue += count; + } + child = siblings[child]; + } + + // since we populate the queue w/ sentinel objects, need to pop() the + // least ones (which are sentinel) + int pqsize = pq.size(); + int size = numResults < pqsize ? numResults : pqsize; + for (int i = pqsize - size; i > 0; i--) { pq.pop(); } + + // create the FacetResultNodes. first pop() the queue into an array, and later label + FacetResultNode[] subResults = new FacetResultNode[size]; + for (int i = size - 1; i >= 0; i--) { + FacetResultNodeImpl node = pq.pop(); + node.cp = taxoReader.getPath(node.ordinal); + subResults[i] = node; + } + FacetResultNodeImpl root = new FacetResultNodeImpl(); + root.ordinal = rootOrd; + root.cp = fr.categoryPath; + root.subResults = Arrays.asList(subResults); + root.value = counts[rootOrd]; + root.residue = residue; + FacetResult fres = new FacetResult(fr, root, size); + res.add(fres); + } + return res; + } finally { + facetArrays.free(); + } + } + + @Override + public boolean acceptsDocsOutOfOrder() { + // in-memory DocValues are random access + return true; + } + + @Override + public void setScorer(Scorer scorer) throws IOException { + } + + private static class FacetResultNodeQueue extends PriorityQueue { + + public FacetResultNodeQueue(int maxSize, boolean prepopulate) { + super(maxSize, prepopulate); + } + + @Override + protected FacetResultNodeImpl getSentinelObject() { + return new FacetResultNodeImpl(); + } + + @Override + protected boolean lessThan(FacetResultNodeImpl a, FacetResultNodeImpl b) { + if (a.value < b.value) return true; + if (a.value > b.value) return false; + // both have the same value, break tie by ordinal + return a.ordinal < b.ordinal; + } + + } + + private static class FacetResultNodeImpl implements FacetResultNode { + + int ordinal; + CategoryPath cp; + List subResults = Collections.emptyList(); + double residue = 0; + double value; + + public FacetResultNodeImpl() { + } + + @Override + public String toString(String prefix) { + StringBuilder sb = new StringBuilder(); + sb.append(prefix).append(cp.toString()).append(" (").append(Double.toString(value)).append(")"); + if (residue > 0) { + sb.append(" (residue=").append(residue).append(")"); + } + for (FacetResultNode sub : subResults) { + sb.append("\n").append(prefix).append(sub.toString(prefix)); + } + return sb.toString(); + } + + @Override + public int getOrdinal() { + return ordinal; + } + + @Override + public CategoryPath getLabel() { + return cp; + } + + @Override + public CategoryPath getLabel(TaxonomyReader taxonomyReader) throws IOException { + return cp; + } + + @Override + public double getValue() { + return value; + } + + @Override + public double getResidue() { + return residue; + } + + @Override + public Iterable getSubResults() { + return subResults; + } + + @Override + public int getNumSubResults() { + return subResults == null ? 0 : subResults.size(); + } + + @Override + public void setValue(double value) { + this.value = value; + } + + } + +} Property changes on: lucene/facet/src/java/org/apache/lucene/facet/search/CountingFacetsCollector.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lucene/facet/src/java/org/apache/lucene/facet/search/DecoderCountingFacetsCollector.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/DecoderCountingFacetsCollector.java (revision 0) +++ lucene/facet/src/java/org/apache/lucene/facet/search/DecoderCountingFacetsCollector.java (working copy) @@ -0,0 +1,333 @@ +package org.apache.lucene.facet.search; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.lucene.facet.index.categorypolicy.OrdinalPolicy; +import org.apache.lucene.facet.index.params.CategoryListParams; +import org.apache.lucene.facet.index.params.FacetIndexingParams; +import org.apache.lucene.facet.search.params.CountFacetRequest; +import org.apache.lucene.facet.search.params.FacetRequest; +import org.apache.lucene.facet.search.params.FacetSearchParams; +import org.apache.lucene.facet.search.results.FacetResult; +import org.apache.lucene.facet.search.results.FacetResultNode; +import org.apache.lucene.facet.taxonomy.CategoryPath; +import org.apache.lucene.facet.taxonomy.TaxonomyReader; +import org.apache.lucene.facet.taxonomy.directory.ParallelTaxonomyArrays; +import org.apache.lucene.index.AtomicReaderContext; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.search.Collector; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.IntsRef; +import org.apache.lucene.util.PriorityQueue; +import org.apache.lucene.util.encoding.DGapVInt8IntDecoder; +import org.apache.lucene.util.encoding.IntDecoder; + +/* + * 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 Collector} which counts facets associated with matching documents. + * This {@link Collector} can be used only in the following conditions: + *
    + *
  • All {@link FacetRequest requests} must be {@link CountFacetRequest}. + * Also, their {@link FacetRequest#getDepth()} must return 1, and + * {@link FacetRequest#getNumLabel()} must be ≥ than + * {@link FacetRequest#getNumResults()}. + *
  • Partitions should be disabled ( + * {@link FacetIndexingParams#getPartitionSize()} should return + * Integer.MAX_VALUE). + *
  • There can be only one {@link CategoryListParams} in the + * {@link FacetIndexingParams}, with {@link DGapVInt8IntDecoder}. + *
  • {@link FacetIndexingParams#getOrdinalPolicy()} must be + * {@link OrdinalPolicy#ALL_PARENTS}. + *
+ */ +public class DecoderCountingFacetsCollector extends FacetsCollector { + + private final FacetSearchParams fsp; + private final TaxonomyReader taxoReader; + private final BytesRef buf = new BytesRef(32); + private final FacetArrays facetArrays; + private final int[] counts; + private final String facetsField; + private final IntDecoder decoder; + private final IntsRef docOrdinals = new IntsRef(32); + + private DocValues.Source facetsSource; + + public DecoderCountingFacetsCollector(FacetSearchParams fsp, TaxonomyReader taxoReader) { + this(fsp, taxoReader, new FacetArrays(taxoReader.getSize())); + } + + public DecoderCountingFacetsCollector(FacetSearchParams fsp, TaxonomyReader taxoReader, FacetArrays facetArrays) { + assert facetArrays.arrayLength >= taxoReader.getSize() : "too small facet array"; + assert assertParams(fsp) == null : assertParams(fsp); + + this.fsp = fsp; + this.taxoReader = taxoReader; + this.facetArrays = facetArrays; + this.counts = facetArrays.getIntArray(); + CategoryListParams clp = fsp.indexingParams.getCategoryListParams(null); + this.facetsField = clp.field; + decoder = clp.createEncoder().createMatchingDecoder(); + } + + /** + * Asserts that this {@link FacetsCollector} can handle the given + * {@link FacetSearchParams}. Returns {@code null} if true, otherwise an error + * message. + */ + static String assertParams(FacetSearchParams fsp) { + // verify that all facet requests are CountFacetRequest + for (FacetRequest fr : fsp.facetRequests) { + if (!(fr instanceof CountFacetRequest)) { + return "all FacetRequests must be CountFacetRequest"; + } + if (fr.getDepth() != 1) { + return "all requests must be of depth 1"; + } + if (fr.getNumLabel() < fr.getNumResults()) { + return "this Collector always labels all requested results"; + } + } + + // verify that there's only one CategoryListParams + List clps = fsp.indexingParams.getAllCategoryListParams(); + if (clps.size() != 1) { + return "this Collector supports only one CategoryListParams"; + } + + // verify that partitions are disabled + if (fsp.indexingParams.getPartitionSize() != Integer.MAX_VALUE) { + return "this Collector does not support partitions"; + } + + // verify that all parents are indexed + if (fsp.indexingParams.getOrdinalPolicy() != OrdinalPolicy.ALL_PARENTS) { + return "this Collector assumes that all of a category's parents are indexed"; + } + + return null; + } + + @Override + public void setNextReader(AtomicReaderContext context) throws IOException { + DocValues dv = context.reader().docValues(facetsField); + if (dv == null) { + facetsSource = null; + } else { + facetsSource = dv.getSource(); + } + } + + @Override + public void collect(int doc) throws IOException { + if (facetsSource == null) { + return; + } + + facetsSource.getBytes(doc, buf); + if (buf.length == 0) { + // this document has no facets + } + + decoder.decode(buf, docOrdinals); + int upto = docOrdinals.offset + docOrdinals.length; + for (int i = docOrdinals.offset; i < upto; i++) { + counts[docOrdinals.ints[i]]++; + } + +// int upto = buf.offset + buf.length; +// int ord = 0; +// int offset = buf.offset; +// int prev = 0; +// while (offset < upto) { +// byte b = buf.bytes[offset++]; +// if (b >= 0) { +// prev = ord = ((ord << 7) | b) + prev; +// counts[ord]++; +// ord = 0; +// } else { +// ord = (ord << 7) | (b & 0x7F); +// } +// } + } + + @Override + public synchronized List getFacetResults() throws IOException { + try { + ParallelTaxonomyArrays arrays = taxoReader.getParallelTaxonomyArrays(); + final int[] children = arrays.children(); + final int[] siblings = arrays.siblings(); + List res = new ArrayList(); + for (FacetRequest fr : fsp.facetRequests) { + int rootOrd = taxoReader.getOrdinal(fr.categoryPath); + if (rootOrd == TaxonomyReader.INVALID_ORDINAL) { // category does not exist + continue; + } + // since we use sentinel objects, we cannot reuse PQ. but that's ok because it's not big + // nocommit specialize for Integer.MAX_VALUE? Or any value that's larger than taxoSize? + FacetResultNodeQueue pq = new FacetResultNodeQueue(fr.getNumResults(), true); + FacetResultNodeImpl top = pq.top(); + int child = children[rootOrd]; + int numResults = 0; // count the number of results + int residue = 0; + while (child != TaxonomyReader.INVALID_ORDINAL) { + int count = counts[child]; + if (count > top.value) { + residue += top.value; + top.value = count; + top.ordinal = child; + top = pq.updateTop(); + ++numResults; + } else { + residue += count; + } + child = siblings[child]; + } + + // since we populate the queue w/ sentinel objects, need to pop() the + // least ones (which are sentinel) + int pqsize = pq.size(); + int size = numResults < pqsize ? numResults : pqsize; + for (int i = pqsize - size; i > 0; i--) { pq.pop(); } + + // create the FacetResultNodes. first pop() the queue into an array, and later label + FacetResultNode[] subResults = new FacetResultNode[size]; + for (int i = size - 1; i >= 0; i--) { + FacetResultNodeImpl node = pq.pop(); + node.cp = taxoReader.getPath(node.ordinal); + subResults[i] = node; + } + FacetResultNodeImpl root = new FacetResultNodeImpl(); + root.ordinal = rootOrd; + root.cp = fr.categoryPath; + root.subResults = Arrays.asList(subResults); + root.value = counts[rootOrd]; + root.residue = residue; + FacetResult fres = new FacetResult(fr, root, size); + res.add(fres); + } + return res; + } finally { + facetArrays.free(); + } + } + + @Override + public boolean acceptsDocsOutOfOrder() { + // in-memory DocValues are random access + return true; + } + + @Override + public void setScorer(Scorer scorer) throws IOException { + } + + private static class FacetResultNodeQueue extends PriorityQueue { + + public FacetResultNodeQueue(int maxSize, boolean prepopulate) { + super(maxSize, prepopulate); + } + + @Override + protected FacetResultNodeImpl getSentinelObject() { + return new FacetResultNodeImpl(); + } + + @Override + protected boolean lessThan(FacetResultNodeImpl a, FacetResultNodeImpl b) { + if (a.value < b.value) return true; + if (a.value > b.value) return false; + // both have the same value, break tie by ordinal + return a.ordinal < b.ordinal; + } + + } + + private static class FacetResultNodeImpl implements FacetResultNode { + + int ordinal; + CategoryPath cp; + List subResults = Collections.emptyList(); + double residue = 0; + double value; + + public FacetResultNodeImpl() { + } + + @Override + public String toString(String prefix) { + StringBuilder sb = new StringBuilder(); + sb.append(prefix).append(cp.toString()).append(" (").append(Double.toString(value)).append(")"); + if (residue > 0) { + sb.append(" (residue=").append(residue).append(")"); + } + for (FacetResultNode sub : subResults) { + sb.append("\n").append(prefix).append(sub.toString(prefix)); + } + return sb.toString(); + } + + @Override + public int getOrdinal() { + return ordinal; + } + + @Override + public CategoryPath getLabel() { + return cp; + } + + @Override + public CategoryPath getLabel(TaxonomyReader taxonomyReader) throws IOException { + return cp; + } + + @Override + public double getValue() { + return value; + } + + @Override + public double getResidue() { + return residue; + } + + @Override + public Iterable getSubResults() { + return subResults; + } + + @Override + public int getNumSubResults() { + return subResults == null ? 0 : subResults.size(); + } + + @Override + public void setValue(double value) { + this.value = value; + } + + } + +} Property changes on: lucene/facet/src/java/org/apache/lucene/facet/search/DecoderCountingFacetsCollector.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lucene/facet/src/java/org/apache/lucene/facet/search/DrillDown.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/DrillDown.java (revision 1435786) +++ lucene/facet/src/java/org/apache/lucene/facet/search/DrillDown.java (working copy) @@ -47,7 +47,7 @@ * @see #term(FacetIndexingParams, CategoryPath) */ public static final Term term(FacetSearchParams sParams, CategoryPath path) { - return term(sParams.getFacetIndexingParams(), path); + return term(sParams.indexingParams, path); } /** Return a drill-down {@link Term} for a category. */ @@ -103,7 +103,7 @@ * @see #query(FacetIndexingParams, Query, CategoryPath...) */ public static final Query query(FacetSearchParams sParams, Query baseQuery, CategoryPath... paths) { - return query(sParams.getFacetIndexingParams(), baseQuery, paths); + return query(sParams.indexingParams, baseQuery, paths); } } Index: lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java (revision 1435786) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java (working copy) @@ -138,7 +138,7 @@ /** check if all requests are complementable */ protected boolean mayComplement() { - for (FacetRequest freq:searchParams.getFacetRequests()) { + for (FacetRequest freq:searchParams.facetRequests) { if (!freq.supportsComplements()) { return false; } Index: lucene/facet/src/java/org/apache/lucene/facet/search/FacetsCollector.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FacetsCollector.java (revision 1435786) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetsCollector.java (working copy) @@ -3,15 +3,13 @@ import java.io.IOException; import java.util.List; -import org.apache.lucene.index.AtomicReaderContext; -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.search.Collector; -import org.apache.lucene.search.Scorer; - import org.apache.lucene.facet.search.params.FacetRequest; import org.apache.lucene.facet.search.params.FacetSearchParams; import org.apache.lucene.facet.search.results.FacetResult; +import org.apache.lucene.facet.taxonomy.CategoryPath; import org.apache.lucene.facet.taxonomy.TaxonomyReader; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.search.Collector; /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -31,109 +29,40 @@ */ /** - * Collector for facet accumulation. * + * A {@link Collector} which executes faceted search and computes the weight of + * requested facets. To get the facet results you should call + * {@link #getFacetResults()}. + * {@link #create(FacetSearchParams, IndexReader, TaxonomyReader)} returns the + * most optimized {@link FacetsCollector} for the given parameters. * * @lucene.experimental */ -public class FacetsCollector extends Collector { - - protected final FacetsAccumulator facetsAccumulator; - private ScoredDocIdCollector scoreDocIdCollector; - private List results; - private Object resultsGuard; - - /** - * Create a collector for accumulating facets while collecting documents - * during search. - * - * @param facetSearchParams - * faceted search parameters defining which facets are required and - * how. - * @param indexReader - * searched index. - * @param taxonomyReader - * taxonomy containing the facets. - */ - public FacetsCollector(FacetSearchParams facetSearchParams, - IndexReader indexReader, TaxonomyReader taxonomyReader) { - facetsAccumulator = initFacetsAccumulator(facetSearchParams, indexReader, taxonomyReader); - scoreDocIdCollector = initScoredDocCollector(facetSearchParams, indexReader, taxonomyReader); - resultsGuard = new Object(); - } - - /** - * Create a {@link ScoredDocIdCollector} to be used as the first phase of - * the facet collection. If all facetRequests are do not require the - * document score, a ScoredDocIdCollector which does not store the document - * scores would be returned. Otherwise a SDIC which does store the documents - * will be returned, having an initial allocated space for 1000 such - * documents' scores. - */ - protected ScoredDocIdCollector initScoredDocCollector( - FacetSearchParams facetSearchParams, IndexReader indexReader, - TaxonomyReader taxonomyReader) { - boolean scoresNeeded = false; - for (FacetRequest frq : facetSearchParams.getFacetRequests()) { - if (frq.requireDocumentScore()) { - scoresNeeded = true; - break; - } +public abstract class FacetsCollector extends Collector { + + // nocommit javadocs + public static FacetsCollector create(FacetSearchParams fsp, IndexReader indexReader, TaxonomyReader taxoReader) { + // nocommit: which one to keep? + if (PostCollectionCountingFacetsCollector.assertParams(fsp) == null) { + return new PostCollectionCountingFacetsCollector(fsp, taxoReader); } - return ScoredDocIdCollector.create(indexReader.maxDoc(), scoresNeeded); + + if (DecoderCountingFacetsCollector.assertParams(fsp) == null) { + return new DecoderCountingFacetsCollector(fsp, taxoReader); + } + + if (CountingFacetsCollector.assertParams(fsp) == null) { + return new CountingFacetsCollector(fsp, taxoReader); + } + + return new StandardFacetsCollector(fsp, indexReader, taxoReader); } - + /** - * Create the {@link FacetsAccumulator} to be used. Default is - * {@link StandardFacetsAccumulator}. Called once at the constructor of the collector. - * - * @param facetSearchParams - * The search params. - * @param indexReader - * A reader to the index to search in. - * @param taxonomyReader - * A reader to the active taxonomy. - * @return The {@link FacetsAccumulator} to use. + * Returns a {@link FacetResult} per {@link FacetRequest} set in + * {@link FacetSearchParams}. Note that if one of the {@link FacetRequest + * requests} is for a {@link CategoryPath} that does not exist in the taxonomy, + * no matching {@link FacetResult} will be returned. */ - protected FacetsAccumulator initFacetsAccumulator(FacetSearchParams facetSearchParams, - IndexReader indexReader, - TaxonomyReader taxonomyReader) { - return new StandardFacetsAccumulator(facetSearchParams, indexReader, taxonomyReader); - } + public abstract List getFacetResults() throws IOException; - /** - * Return accumulated facets results (according to faceted search parameters) - * for collected documents. - * @throws IOException on error - */ - public List getFacetResults() throws IOException { - synchronized (resultsGuard) { // over protection - if (results == null) { - // lazy creation but just once - results = facetsAccumulator.accumulate(scoreDocIdCollector.getScoredDocIDs()); - scoreDocIdCollector = null; - } - return results; - } - } - - @Override - public boolean acceptsDocsOutOfOrder() { - return false; - } - - @Override - public void collect(int doc) throws IOException { - scoreDocIdCollector.collect(doc); - } - - @Override - public void setNextReader(AtomicReaderContext context) throws IOException { - scoreDocIdCollector.setNextReader(context); - } - - @Override - public void setScorer(Scorer scorer) throws IOException { - scoreDocIdCollector.setScorer(scorer); - } - } Index: lucene/facet/src/java/org/apache/lucene/facet/search/PostCollectionCountingFacetsCollector.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/PostCollectionCountingFacetsCollector.java (revision 0) +++ lucene/facet/src/java/org/apache/lucene/facet/search/PostCollectionCountingFacetsCollector.java (working copy) @@ -0,0 +1,345 @@ +package org.apache.lucene.facet.search; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +import org.apache.lucene.facet.index.categorypolicy.OrdinalPolicy; +import org.apache.lucene.facet.index.params.CategoryListParams; +import org.apache.lucene.facet.index.params.FacetIndexingParams; +import org.apache.lucene.facet.search.params.CountFacetRequest; +import org.apache.lucene.facet.search.params.FacetRequest; +import org.apache.lucene.facet.search.params.FacetSearchParams; +import org.apache.lucene.facet.search.results.FacetResult; +import org.apache.lucene.facet.search.results.FacetResultNode; +import org.apache.lucene.facet.taxonomy.CategoryPath; +import org.apache.lucene.facet.taxonomy.TaxonomyReader; +import org.apache.lucene.facet.taxonomy.directory.ParallelTaxonomyArrays; +import org.apache.lucene.index.AtomicReaderContext; +import org.apache.lucene.index.DocValues; +import org.apache.lucene.index.DocValues.Source; +import org.apache.lucene.search.Collector; +import org.apache.lucene.search.DocIdSetIterator; +import org.apache.lucene.search.Scorer; +import org.apache.lucene.util.BytesRef; +import org.apache.lucene.util.FixedBitSet; +import org.apache.lucene.util.PriorityQueue; +import org.apache.lucene.util.encoding.DGapVInt8IntDecoder; + +/* + * 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 Collector} which counts facets associated with matching documents. + * This {@link Collector} can be used only in the following conditions: + *
    + *
  • All {@link FacetRequest requests} must be {@link CountFacetRequest}. + * Also, their {@link FacetRequest#getDepth()} must return 1, and + * {@link FacetRequest#getNumLabel()} must be ≥ than + * {@link FacetRequest#getNumResults()}. + *
  • Partitions should be disabled ( + * {@link FacetIndexingParams#getPartitionSize()} should return + * Integer.MAX_VALUE). + *
  • There can be only one {@link CategoryListParams} in the + * {@link FacetIndexingParams}, with {@link DGapVInt8IntDecoder}. + *
  • {@link FacetIndexingParams#getOrdinalPolicy()} must be + * {@link OrdinalPolicy#ALL_PARENTS}. + *
+ */ +public class PostCollectionCountingFacetsCollector extends FacetsCollector { + + private final FacetSearchParams fsp; + private final TaxonomyReader taxoReader; + private final BytesRef buf = new BytesRef(32); + private final FacetArrays facetArrays; + private final int[] counts; + private final String facetsField; + private final HashMap matchingDocs = new HashMap(); + + private DocValues facetsValues; + private FixedBitSet bits; + + public PostCollectionCountingFacetsCollector(FacetSearchParams fsp, TaxonomyReader taxoReader) { + this(fsp, taxoReader, new FacetArrays(taxoReader.getSize())); + } + + public PostCollectionCountingFacetsCollector(FacetSearchParams fsp, TaxonomyReader taxoReader, FacetArrays facetArrays) { + assert facetArrays.arrayLength >= taxoReader.getSize() : "too small facet array"; + assert assertParams(fsp) == null : assertParams(fsp); + + this.fsp = fsp; + this.taxoReader = taxoReader; + this.facetArrays = facetArrays; + this.counts = facetArrays.getIntArray(); + this.facetsField = fsp.indexingParams.getCategoryListParams(null).field; + } + + /** + * Asserts that this {@link FacetsCollector} can handle the given + * {@link FacetSearchParams}. Returns {@code null} if true, otherwise an error + * message. + */ + static String assertParams(FacetSearchParams fsp) { + // verify that all facet requests are CountFacetRequest + for (FacetRequest fr : fsp.facetRequests) { + if (!(fr instanceof CountFacetRequest)) { + return "all FacetRequests must be CountFacetRequest"; + } + if (fr.getDepth() != 1) { + return "all requests must be of depth 1"; + } + if (fr.getNumLabel() < fr.getNumResults()) { + return "this Collector always labels all requested results"; + } + } + + // verify that there's only one CategoryListParams + List clps = fsp.indexingParams.getAllCategoryListParams(); + if (clps.size() != 1) { + return "this Collector supports only one CategoryListParams"; + } + + // verify DGapVInt decoder + CategoryListParams clp = clps.get(0); + if (clp.createEncoder().createMatchingDecoder().getClass() != DGapVInt8IntDecoder.class) { + return "this Collector supports only DGap + VInt encoding"; + } + + // verify that partitions are disabled + if (fsp.indexingParams.getPartitionSize() != Integer.MAX_VALUE) { + return "this Collector does not support partitions"; + } + + // verify that all parents are indexed + if (fsp.indexingParams.getOrdinalPolicy() != OrdinalPolicy.ALL_PARENTS) { + return "this Collector assumes that all of a category's parents are indexed"; + } + + return null; + } + + @Override + public void setNextReader(AtomicReaderContext context) throws IOException { + facetsValues = context.reader().docValues(facetsField); + if (facetsValues != null) { + Source facetSource = facetsValues.getSource(); + bits = new FixedBitSet(context.reader().maxDoc()); + matchingDocs.put(facetSource, bits); + } + } + + @Override + public void collect(int doc) throws IOException { + if (facetsValues == null) { + return; + } + + bits.set(doc); + } + + @Override + public synchronized List getFacetResults() throws IOException { + try { + // first, count matching documents' facets + for (Entry entry : matchingDocs.entrySet()) { + Source facetsSource = entry.getKey(); + FixedBitSet bits = entry.getValue(); + DocIdSetIterator iter = bits.iterator(); + int doc; + while ((doc = iter.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) { + facetsSource .getBytes(doc, buf); + if (buf.length == 0) { + // this document has no facets + } + + int upto = buf.offset + buf.length; + int ord = 0; + int offset = buf.offset; + int prev = 0; + while (offset < upto) { + byte b = buf.bytes[offset++]; + if (b >= 0) { + prev = ord = ((ord << 7) | b) + prev; + counts[ord]++; + ord = 0; + } else { + ord = (ord << 7) | (b & 0x7F); + } + } + } + } + + ParallelTaxonomyArrays arrays = taxoReader.getParallelTaxonomyArrays(); + final int[] children = arrays.children(); + final int[] siblings = arrays.siblings(); + List res = new ArrayList(); + for (FacetRequest fr : fsp.facetRequests) { + int rootOrd = taxoReader.getOrdinal(fr.categoryPath); + if (rootOrd == TaxonomyReader.INVALID_ORDINAL) { // category does not exist + continue; + } + // since we use sentinel objects, we cannot reuse PQ. but that's ok because it's not big + // nocommit specialize for Integer.MAX_VALUE? Or any value that's larger than taxoSize? + FacetResultNodeQueue pq = new FacetResultNodeQueue(fr.getNumResults(), true); + FacetResultNodeImpl top = pq.top(); + int child = children[rootOrd]; + int numResults = 0; // count the number of results + int residue = 0; + while (child != TaxonomyReader.INVALID_ORDINAL) { + int count = counts[child]; + if (count > top.value) { + residue += top.value; + top.value = count; + top.ordinal = child; + top = pq.updateTop(); + ++numResults; + } else { + residue += count; + } + child = siblings[child]; + } + + // since we populate the queue w/ sentinel objects, need to pop() the + // least ones (which are sentinel) + int pqsize = pq.size(); + int size = numResults < pqsize ? numResults : pqsize; + for (int i = pqsize - size; i > 0; i--) { pq.pop(); } + + // create the FacetResultNodes. first pop() the queue into an array, and later label + FacetResultNode[] subResults = new FacetResultNode[size]; + for (int i = size - 1; i >= 0; i--) { + FacetResultNodeImpl node = pq.pop(); + node.cp = taxoReader.getPath(node.ordinal); + subResults[i] = node; + } + FacetResultNodeImpl root = new FacetResultNodeImpl(); + root.ordinal = rootOrd; + root.cp = fr.categoryPath; + root.subResults = Arrays.asList(subResults); + root.value = counts[rootOrd]; + root.residue = residue; + FacetResult fres = new FacetResult(fr, root, size); + res.add(fres); + } + return res; + } finally { + facetArrays.free(); + } + } + + @Override + public boolean acceptsDocsOutOfOrder() { + // in-memory DocValues are random access + return true; + } + + @Override + public void setScorer(Scorer scorer) throws IOException { + } + + private static class FacetResultNodeQueue extends PriorityQueue { + + public FacetResultNodeQueue(int maxSize, boolean prepopulate) { + super(maxSize, prepopulate); + } + + @Override + protected FacetResultNodeImpl getSentinelObject() { + return new FacetResultNodeImpl(); + } + + @Override + protected boolean lessThan(FacetResultNodeImpl a, FacetResultNodeImpl b) { + if (a.value < b.value) return true; + if (a.value > b.value) return false; + // both have the same value, break tie by ordinal + return a.ordinal < b.ordinal; + } + + } + + private static class FacetResultNodeImpl implements FacetResultNode { + + int ordinal; + CategoryPath cp; + List subResults = Collections.emptyList(); + double residue = 0; + double value; + + public FacetResultNodeImpl() { + } + + @Override + public String toString(String prefix) { + StringBuilder sb = new StringBuilder(); + sb.append(prefix).append(cp.toString()).append(" (").append(Double.toString(value)).append(")"); + if (residue > 0) { + sb.append(" (residue=").append(residue).append(")"); + } + for (FacetResultNode sub : subResults) { + sb.append("\n").append(prefix).append(sub.toString(prefix)); + } + return sb.toString(); + } + + @Override + public int getOrdinal() { + return ordinal; + } + + @Override + public CategoryPath getLabel() { + return cp; + } + + @Override + public CategoryPath getLabel(TaxonomyReader taxonomyReader) throws IOException { + return cp; + } + + @Override + public double getValue() { + return value; + } + + @Override + public double getResidue() { + return residue; + } + + @Override + public Iterable getSubResults() { + return subResults; + } + + @Override + public int getNumSubResults() { + return subResults == null ? 0 : subResults.size(); + } + + @Override + public void setValue(double value) { + this.value = value; + } + + } + +} Property changes on: lucene/facet/src/java/org/apache/lucene/facet/search/PostCollectionCountingFacetsCollector.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java (revision 1435786) +++ lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java (working copy) @@ -87,7 +87,7 @@ this.facetArrays = facetArrays; // can only be computed later when docids size is known isUsingComplements = false; - partitionSize = PartitionsUtils.partitionSize(searchParams.getFacetIndexingParams(), taxonomyReader); + partitionSize = PartitionsUtils.partitionSize(searchParams.indexingParams, taxonomyReader); maxPartitions = (int) Math.ceil(this.taxonomyReader.getSize() / (double) partitionSize); accumulateGuard = new Object(); } @@ -95,7 +95,7 @@ public StandardFacetsAccumulator(FacetSearchParams searchParams, IndexReader indexReader, TaxonomyReader taxonomyReader) { this(searchParams, indexReader, taxonomyReader, new FacetArrays( - PartitionsUtils.partitionSize(searchParams.getFacetIndexingParams(), taxonomyReader))); + PartitionsUtils.partitionSize(searchParams.indexingParams, taxonomyReader))); } @Override @@ -112,7 +112,7 @@ if (isUsingComplements) { try { totalFacetCounts = TotalFacetCountsCache.getSingleton().getTotalCounts(indexReader, taxonomyReader, - searchParams.getFacetIndexingParams()); + searchParams.indexingParams); if (totalFacetCounts != null) { docids = ScoredDocIdsUtils.getComplementSet(docids, indexReader); } else { @@ -159,7 +159,7 @@ // In this implementation merges happen after each partition, // but other impl could merge only at the end. final HashSet handledRequests = new HashSet(); - for (FacetRequest fr : searchParams.getFacetRequests()) { + for (FacetRequest fr : searchParams.facetRequests) { // Handle and merge only facet requests which were not already handled. if (handledRequests.add(fr)) { FacetResultsHandler frHndlr = fr.createFacetResultsHandler(taxonomyReader); @@ -178,7 +178,7 @@ // gather results from all requests into a list for returning them List res = new ArrayList(); - for (FacetRequest fr : searchParams.getFacetRequests()) { + for (FacetRequest fr : searchParams.facetRequests) { FacetResultsHandler frHndlr = fr.createFacetResultsHandler(taxonomyReader); IntermediateFacetResult tmpResult = fr2tmpRes.get(fr); if (tmpResult == null) { @@ -321,8 +321,8 @@ HashMap categoryLists = new HashMap(); - FacetIndexingParams indexingParams = searchParams.getFacetIndexingParams(); - for (FacetRequest facetRequest : searchParams.getFacetRequests()) { + FacetIndexingParams indexingParams = searchParams.indexingParams; + for (FacetRequest facetRequest : searchParams.facetRequests) { Aggregator categoryAggregator = facetRequest.createAggregator(isUsingComplements, facetArrays, taxonomyReader); CategoryListIterator cli = indexingParams.getCategoryListParams(facetRequest.categoryPath).createCategoryListIterator(partition); Index: lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsCollector.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsCollector.java (revision 0) +++ lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsCollector.java (working copy) @@ -0,0 +1,132 @@ +package org.apache.lucene.facet.search; + +import java.io.IOException; +import java.util.List; + +import org.apache.lucene.facet.search.params.FacetRequest; +import org.apache.lucene.facet.search.params.FacetSearchParams; +import org.apache.lucene.facet.search.results.FacetResult; +import org.apache.lucene.facet.taxonomy.TaxonomyReader; +import org.apache.lucene.index.AtomicReaderContext; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.search.Scorer; + +/* + * 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 FacetsCollector} which allows initilizing e.g. + * {@link FacetsAccumulator}. + * + * @lucene.experimental + */ +public class StandardFacetsCollector extends FacetsCollector { + + protected final FacetsAccumulator facetsAccumulator; + private ScoredDocIdCollector scoreDocIdCollector; + private List results; + private Object resultsGuard; + + /** + * Create a collector for accumulating facets while collecting documents + * during search. + * + * @param facetSearchParams + * faceted search parameters defining which facets are required and + * how. + * @param indexReader + * searched index. + * @param taxonomyReader + * taxonomy containing the facets. + */ + public StandardFacetsCollector(FacetSearchParams facetSearchParams, IndexReader indexReader, TaxonomyReader taxonomyReader) { + facetsAccumulator = initFacetsAccumulator(facetSearchParams, indexReader, taxonomyReader); + scoreDocIdCollector = initScoredDocCollector(facetSearchParams, indexReader, taxonomyReader); + resultsGuard = new Object(); + } + + /** + * Create a {@link ScoredDocIdCollector} to be used as the first phase of + * the facet collection. If all facetRequests are do not require the + * document score, a ScoredDocIdCollector which does not store the document + * scores would be returned. Otherwise a SDIC which does store the documents + * will be returned, having an initial allocated space for 1000 such + * documents' scores. + */ + protected ScoredDocIdCollector initScoredDocCollector(FacetSearchParams facetSearchParams, IndexReader indexReader, + TaxonomyReader taxonomyReader) { + boolean scoresNeeded = false; + for (FacetRequest frq : facetSearchParams.facetRequests) { + if (frq.requireDocumentScore()) { + scoresNeeded = true; + break; + } + } + return ScoredDocIdCollector.create(indexReader.maxDoc(), scoresNeeded); + } + + /** + * Create the {@link FacetsAccumulator} to be used. Default is + * {@link StandardFacetsAccumulator}. Called once at the constructor of the collector. + * + * @param facetSearchParams + * The search params. + * @param indexReader + * A reader to the index to search in. + * @param taxonomyReader + * A reader to the active taxonomy. + * @return The {@link FacetsAccumulator} to use. + */ + protected FacetsAccumulator initFacetsAccumulator(FacetSearchParams facetSearchParams, + IndexReader indexReader, + TaxonomyReader taxonomyReader) { + return new StandardFacetsAccumulator(facetSearchParams, indexReader, taxonomyReader); + } + + @Override + public List getFacetResults() throws IOException { + synchronized (resultsGuard) { // over protection + if (results == null) { + // lazy creation but just once + results = facetsAccumulator.accumulate(scoreDocIdCollector.getScoredDocIDs()); + scoreDocIdCollector = null; + } + return results; + } + } + + @Override + public boolean acceptsDocsOutOfOrder() { + return false; + } + + @Override + public void collect(int doc) throws IOException { + scoreDocIdCollector.collect(doc); + } + + @Override + public void setNextReader(AtomicReaderContext context) throws IOException { + scoreDocIdCollector.setNextReader(context); + } + + @Override + public void setScorer(Scorer scorer) throws IOException { + scoreDocIdCollector.setScorer(scorer); + } + +} Property changes on: lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsCollector.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lucene/facet/src/java/org/apache/lucene/facet/search/TotalFacetCounts.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/TotalFacetCounts.java (revision 1435786) +++ lucene/facet/src/java/org/apache/lucene/facet/search/TotalFacetCounts.java (working copy) @@ -8,9 +8,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.lucene.facet.index.params.CategoryListParams; @@ -150,14 +148,13 @@ } // needed because FacetSearchParams do not allow empty FacetRequests - private static final List DUMMY_REQ = Arrays.asList( - new FacetRequest[] { new CountFacetRequest(CategoryPath.EMPTY, 1) }); + private static final FacetRequest DUMMY_REQ = new CountFacetRequest(CategoryPath.EMPTY, 1); static TotalFacetCounts compute(final IndexReader indexReader, final TaxonomyReader taxonomy, final FacetIndexingParams facetIndexingParams) throws IOException { int partitionSize = PartitionsUtils.partitionSize(facetIndexingParams, taxonomy); final int[][] counts = new int[(int) Math.ceil(taxonomy.getSize() /(float) partitionSize)][partitionSize]; - FacetSearchParams newSearchParams = new FacetSearchParams(DUMMY_REQ, facetIndexingParams); + FacetSearchParams newSearchParams = new FacetSearchParams(facetIndexingParams, DUMMY_REQ); //createAllListsSearchParams(facetIndexingParams, this.totalCounts); FacetsAccumulator fe = new StandardFacetsAccumulator(newSearchParams, indexReader, taxonomy) { @Override Index: lucene/facet/src/java/org/apache/lucene/facet/search/params/FacetSearchParams.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/params/FacetSearchParams.java (revision 1435786) +++ lucene/facet/src/java/org/apache/lucene/facet/search/params/FacetSearchParams.java (working copy) @@ -35,8 +35,8 @@ */ public class FacetSearchParams { - protected final FacetIndexingParams indexingParams; - protected final List facetRequests; + public final FacetIndexingParams indexingParams; + public final List facetRequests; /** * Initializes with the given {@link FacetRequest requests} and default @@ -57,6 +57,14 @@ public FacetSearchParams(List facetRequests) { this(facetRequests, FacetIndexingParams.ALL_PARENTS); } + + /** + * Initilizes with the given {@link FacetRequest requests} and + * {@link FacetIndexingParams}. + */ + public FacetSearchParams(FacetIndexingParams indexingParams, FacetRequest... facetRequests) { + this(Arrays.asList(facetRequests), indexingParams); + } /** * Initilizes with the given {@link FacetRequest requests} and @@ -66,36 +74,20 @@ if (facetRequests == null || facetRequests.size() == 0) { throw new IllegalArgumentException("at least one FacetRequest must be defined"); } + this.facetRequests = facetRequests; this.indexingParams = indexingParams; - this.facetRequests = facetRequests; } - /** - * Returns the {@link FacetIndexingParams} that were passed to the - * constructor. - */ - public FacetIndexingParams getFacetIndexingParams() { - return indexingParams; - } - - /** - * Returns the list of {@link FacetRequest facet requests} that were passed to - * the constructor. - */ - public List getFacetRequests() { - return facetRequests; - } - @Override public String toString() { final char TAB = '\t'; final char NEWLINE = '\n'; StringBuilder sb = new StringBuilder("IndexingParams: "); - sb.append(NEWLINE).append(TAB).append(getFacetIndexingParams()); + sb.append(NEWLINE).append(TAB).append(indexingParams); sb.append(NEWLINE).append("FacetRequests:"); - for (FacetRequest facetRequest : getFacetRequests()) { + for (FacetRequest facetRequest : facetRequests) { sb.append(NEWLINE).append(TAB).append(facetRequest); } Index: lucene/facet/src/java/org/apache/lucene/facet/search/results/MutableFacetResultNode.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/results/MutableFacetResultNode.java (revision 1435786) +++ lucene/facet/src/java/org/apache/lucene/facet/search/results/MutableFacetResultNode.java (working copy) @@ -196,13 +196,6 @@ return value; } - /** - * Set the value of this result. - * - * @param value - * the value to set - * @see #getValue() - */ @Override public void setValue(double value) { this.value = value; @@ -222,11 +215,6 @@ return residue; } - /** - * Set the residue. - * @param residue the residue to set - * @see #getResidue() - */ public void setResidue(double residue) { this.residue = residue; } @@ -308,27 +296,14 @@ subResults.add(0, subRes); } - /* - * (non-Javadoc) - * - * @see - * org.apache.lucene.facet.search.results.FacetResultNode#getLabel(org.apache.lucene - * .facet.taxonomy.TaxonomyReader) - */ @Override - public final CategoryPath getLabel(TaxonomyReader taxonomyReader) - throws IOException { + public final CategoryPath getLabel(TaxonomyReader taxonomyReader) throws IOException { if (label == null) { label = taxonomyReader.getPath(ordinal); } return label; } - /* - * (non-Javadoc) - * - * @see org.apache.lucene.facet.search.results.FacetResultNode#getNumSubResults() - */ @Override public final int getNumSubResults() { return subResults == null ? 0 : subResults.size(); Index: lucene/facet/src/java/org/apache/lucene/facet/search/sampling/Sampler.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/sampling/Sampler.java (revision 1435786) +++ lucene/facet/src/java/org/apache/lucene/facet/search/sampling/Sampler.java (working copy) @@ -184,11 +184,11 @@ double overSampleFactor = getSamplingParams().getOversampleFactor(); if (overSampleFactor > 1) { // any factoring to do? List facetRequests = new ArrayList(); - for (FacetRequest frq : original.getFacetRequests()) { + for (FacetRequest frq : original.facetRequests) { int overSampledNumResults = (int) Math.ceil(frq.getNumResults() * overSampleFactor); facetRequests.add(new OverSampledFacetRequest(frq, overSampledNumResults)); } - res = new FacetSearchParams(facetRequests, original.getFacetIndexingParams()); + res = new FacetSearchParams(facetRequests, original.indexingParams); } return res; } Index: lucene/facet/src/test/org/apache/lucene/facet/FacetTestBase.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/FacetTestBase.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/FacetTestBase.java (working copy) @@ -196,7 +196,7 @@ * test with different faceted search params. */ protected FacetSearchParams getFacetSearchParams(FacetIndexingParams iParams, FacetRequest... facetRequests) { - return new FacetSearchParams(Arrays.asList(facetRequests), iParams); + return new FacetSearchParams(iParams, facetRequests); } /** @@ -315,33 +315,18 @@ assertCountsAndCardinality(facetCountsTruth, subRes, reqNumResults); // recurse into child results } } - + /** Validate results equality */ protected static void assertSameResults(List expected, List actual) { - String expectedResults = resStringValueOnly(expected); - String actualResults = resStringValueOnly(actual); - if (!expectedResults.equals(actualResults)) { - System.err.println("Results are not the same!"); - System.err.println("Expected:\n" + expectedResults); - System.err.println("Actual:\n" + actualResults); - throw new NotSameResultError(); + assertEquals("wrong number of facet results", expected.size(), actual.size()); + int size = expected.size(); + for (int i = 0; i < size; i++) { + FacetResult expectedResult = expected.get(i); + FacetResult actualResult = actual.get(i); + String expectedStr = FacetTestUtils.toSimpleString(expectedResult); + String actualStr = FacetTestUtils.toSimpleString(actualResult); + assertEquals("Results not the same!\nExpected:" + expectedStr + "\nActual:\n" + actualStr, expectedStr, actualStr); } } - /** exclude the residue and numDecendants because it is incorrect in sampling */ - private static final String resStringValueOnly(List results) { - StringBuilder sb = new StringBuilder(); - for (FacetResult facetRes : results) { - sb.append(facetRes.toString()).append('\n'); - } - return sb.toString().replaceAll("Residue:.*.0", "").replaceAll("Num valid Descendants.*", ""); - } - - /** Special Error class for ability to ignore only this error and retry... */ - public static class NotSameResultError extends Error { - public NotSameResultError() { - super("Results are not the same!"); - } - } - } Index: lucene/facet/src/test/org/apache/lucene/facet/FacetTestUtils.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/FacetTestUtils.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/FacetTestUtils.java (working copy) @@ -10,6 +10,8 @@ import org.apache.lucene.facet.search.params.CountFacetRequest; import org.apache.lucene.facet.search.params.FacetRequest; import org.apache.lucene.facet.search.params.FacetSearchParams; +import org.apache.lucene.facet.search.results.FacetResult; +import org.apache.lucene.facet.search.results.FacetResultNode; import org.apache.lucene.facet.taxonomy.CategoryPath; import org.apache.lucene.facet.taxonomy.TaxonomyReader; import org.apache.lucene.facet.taxonomy.TaxonomyWriter; @@ -45,6 +47,33 @@ public class FacetTestUtils { + public static class IndexTaxonomyReaderPair { + public DirectoryReader indexReader; + public DirectoryTaxonomyReader taxReader; + public IndexSearcher indexSearcher; + + public void close() throws IOException { + indexReader.close(); + taxReader.close(); + } + + } + + public static class IndexTaxonomyWriterPair { + public IndexWriter indexWriter; + public TaxonomyWriter taxWriter; + + public void close() throws IOException { + indexWriter.close(); + taxWriter.close(); + } + + public void commit() throws IOException { + indexWriter.commit(); + taxWriter.commit(); + } + } + public static Directory[][] createIndexTaxonomyDirs(int number) { Directory[][] dirs = new Directory[number][2]; for (int i = 0; i < number; i++) { @@ -54,8 +83,7 @@ return dirs; } - public static IndexTaxonomyReaderPair[] createIndexTaxonomyReaderPair( - Directory[][] dirs) throws IOException { + public static IndexTaxonomyReaderPair[] createIndexTaxonomyReaderPair(Directory[][] dirs) throws IOException { IndexTaxonomyReaderPair[] pairs = new IndexTaxonomyReaderPair[dirs.length]; for (int i = 0; i < dirs.length; i++) { IndexTaxonomyReaderPair pair = new IndexTaxonomyReaderPair(); @@ -66,15 +94,13 @@ } return pairs; } - - public static IndexTaxonomyWriterPair[] createIndexTaxonomyWriterPair( - Directory[][] dirs) throws IOException { + + public static IndexTaxonomyWriterPair[] createIndexTaxonomyWriterPair(Directory[][] dirs) throws IOException { IndexTaxonomyWriterPair[] pairs = new IndexTaxonomyWriterPair[dirs.length]; for (int i = 0; i < dirs.length; i++) { IndexTaxonomyWriterPair pair = new IndexTaxonomyWriterPair(); pair.indexWriter = new IndexWriter(dirs[i][0], new IndexWriterConfig( - LuceneTestCase.TEST_VERSION_CURRENT, new StandardAnalyzer( - LuceneTestCase.TEST_VERSION_CURRENT))); + LuceneTestCase.TEST_VERSION_CURRENT, new StandardAnalyzer(LuceneTestCase.TEST_VERSION_CURRENT))); pair.taxWriter = new DirectoryTaxonomyWriter(dirs[i][1]); pair.indexWriter.commit(); pair.taxWriter.commit(); @@ -83,9 +109,8 @@ return pairs; } - public static Collector[] search(IndexSearcher searcher, - TaxonomyReader taxonomyReader, FacetIndexingParams iParams, int k, - String... facetNames) throws IOException { + public static Collector[] search(IndexSearcher searcher, TaxonomyReader taxonomyReader, FacetIndexingParams iParams, + int k, String... facetNames) throws IOException { Collector[] collectors = new Collector[2]; @@ -97,10 +122,8 @@ } FacetSearchParams facetSearchParams = new FacetSearchParams(fRequests, iParams); - TopScoreDocCollector topDocsCollector = TopScoreDocCollector.create( - searcher.getIndexReader().maxDoc(), true); - FacetsCollector facetsCollector = new FacetsCollector( - facetSearchParams, searcher.getIndexReader(), taxonomyReader); + TopScoreDocCollector topDocsCollector = TopScoreDocCollector.create(searcher.getIndexReader().maxDoc(), true); + FacetsCollector facetsCollector = FacetsCollector.create(facetSearchParams, searcher.getIndexReader(), taxonomyReader); Collector mColl = MultiCollector.wrap(topDocsCollector, facetsCollector); collectors[0] = topDocsCollector; @@ -109,32 +132,18 @@ searcher.search(new MatchAllDocsQuery(), mColl); return collectors; } - - public static class IndexTaxonomyReaderPair { - public DirectoryReader indexReader; - public DirectoryTaxonomyReader taxReader; - public IndexSearcher indexSearcher; - public void close() throws IOException { - indexReader.close(); - taxReader.close(); - } - + public static String toSimpleString(FacetResult fr) { + StringBuilder sb = new StringBuilder(); + toSimpleString(0, sb, fr.getFacetResultNode(), ""); + return sb.toString(); } - public static class IndexTaxonomyWriterPair { - public IndexWriter indexWriter; - public TaxonomyWriter taxWriter; - - public void close() throws IOException { - indexWriter.close(); - taxWriter.close(); + private static void toSimpleString(int depth, StringBuilder sb, FacetResultNode node, String indent) { + sb.append(indent + node.getLabel().components[depth] + " (" + (int) node.getValue() + ")\n"); + for(FacetResultNode childNode : node.getSubResults()) { + toSimpleString(depth + 1, sb, childNode, indent + " "); } - - public void commit() throws IOException { - indexWriter.commit(); - taxWriter.commit(); - } } } Index: lucene/facet/src/test/org/apache/lucene/facet/index/OrdinalMappingReaderTest.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/index/OrdinalMappingReaderTest.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/index/OrdinalMappingReaderTest.java (working copy) @@ -71,7 +71,7 @@ DirectoryTaxonomyReader taxReader = new DirectoryTaxonomyReader(taxDir); IndexSearcher searcher = newSearcher(reader1); FacetSearchParams fsp = new FacetSearchParams(new CountFacetRequest(new CategoryPath("tag"), NUM_DOCS)); - FacetsCollector collector = new FacetsCollector(fsp, reader1, taxReader); + FacetsCollector collector = FacetsCollector.create(fsp, reader1, taxReader); searcher.search(new MatchAllDocsQuery(), collector); FacetResult result = collector.getFacetResults().get(0); FacetResultNode node = result.getFacetResultNode(); Index: lucene/facet/src/test/org/apache/lucene/facet/index/TestFacetsPayloadMigrationReader.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/index/TestFacetsPayloadMigrationReader.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/index/TestFacetsPayloadMigrationReader.java (working copy) @@ -266,7 +266,7 @@ requests.add(new CountFacetRequest(new CategoryPath(dim), 5)); } FacetSearchParams fsp = new FacetSearchParams(requests, fip); - FacetsCollector fc = new FacetsCollector(fsp, indexReader, taxoReader); + FacetsCollector fc = FacetsCollector.create(fsp, indexReader, taxoReader); MatchAllDocsQuery base = new MatchAllDocsQuery(); searcher.search(base, fc); List facetResults = fc.getFacetResults(); @@ -283,12 +283,10 @@ // verify drill-down for (String dim : expectedCounts.keySet()) { CategoryPath drillDownCP = new CategoryPath(dim); - ArrayList request = new ArrayList(1); - request.add(new CountFacetRequest(drillDownCP, 10)); - FacetSearchParams fsp = new FacetSearchParams(request, fip); + FacetSearchParams fsp = new FacetSearchParams(fip, new CountFacetRequest(drillDownCP, 10)); Query drillDown = DrillDown.query(fsp, new MatchAllDocsQuery(), drillDownCP); TotalHitCountCollector total = new TotalHitCountCollector(); - FacetsCollector fc = new FacetsCollector(fsp, indexReader, taxoReader); + FacetsCollector fc = FacetsCollector.create(fsp, indexReader, taxoReader); searcher.search(drillDown, MultiCollector.wrap(fc, total)); assertTrue("no results for drill-down query " + drillDown, total.getTotalHits() > 0); List facetResults = fc.getFacetResults(); Index: lucene/facet/src/test/org/apache/lucene/facet/search/CountingFacetsCollectorTest.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/CountingFacetsCollectorTest.java (revision 0) +++ lucene/facet/src/test/org/apache/lucene/facet/search/CountingFacetsCollectorTest.java (working copy) @@ -0,0 +1,393 @@ +package org.apache.lucene.facet.search; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import org.apache.lucene.analysis.MockAnalyzer; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field.Store; +import org.apache.lucene.document.StringField; +import org.apache.lucene.facet.index.FacetFields; +import org.apache.lucene.facet.index.categorypolicy.OrdinalPolicy; +import org.apache.lucene.facet.index.params.CategoryListParams; +import org.apache.lucene.facet.index.params.FacetIndexingParams; +import org.apache.lucene.facet.search.params.CountFacetRequest; +import org.apache.lucene.facet.search.params.FacetRequest; +import org.apache.lucene.facet.search.params.FacetSearchParams; +import org.apache.lucene.facet.search.params.ScoreFacetRequest; +import org.apache.lucene.facet.search.results.FacetResult; +import org.apache.lucene.facet.search.results.FacetResultNode; +import org.apache.lucene.facet.taxonomy.CategoryPath; +import org.apache.lucene.facet.taxonomy.TaxonomyReader; +import org.apache.lucene.facet.taxonomy.TaxonomyWriter; +import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader; +import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyWriter; +import org.apache.lucene.index.DirectoryReader; +import org.apache.lucene.index.IndexWriter; +import org.apache.lucene.index.IndexWriterConfig; +import org.apache.lucene.index.NoMergePolicy; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.apache.lucene.search.TermQuery; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.IOUtils; +import org.apache.lucene.util.LuceneTestCase; +import org.apache.lucene.util.collections.ObjectToIntMap; +import org.apache.lucene.util.encoding.IntEncoder; +import org.apache.lucene.util.encoding.VInt8IntEncoder; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +/* + * 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. + */ + +public class CountingFacetsCollectorTest extends LuceneTestCase { + + private static final Term A = new Term("f", "a"); + private static final CategoryPath CP_A = new CategoryPath("A"), CP_B = new CategoryPath("B"); + private static final int NUM_CHILDREN_CP_A = 5, NUM_CHILDREN_CP_B = 3; + private static final CategoryPath[] CATEGORIES_A, CATEGORIES_B; + static { + CATEGORIES_A = new CategoryPath[NUM_CHILDREN_CP_A]; + for (int i = 0; i < NUM_CHILDREN_CP_A; i++) { + CATEGORIES_A[i] = new CategoryPath(CP_A.components[0], Integer.toString(i)); + } + CATEGORIES_B = new CategoryPath[NUM_CHILDREN_CP_B]; + for (int i = 0; i < NUM_CHILDREN_CP_B; i++) { + CATEGORIES_B[i] = new CategoryPath(CP_B.components[0], Integer.toString(i)); + } + } + + private static Directory indexDir, taxoDir; + private static ObjectToIntMap allExpectedCounts, termExpectedCounts; + private static int numChildrenIndexedA, numChildrenIndexedB; + + @AfterClass + public static void afterClassCountingFacetsCollectorTest() throws Exception { + IOUtils.close(indexDir, taxoDir); + } + + private static List randomCategories(Random random) { + // add random categories from the two dimensions, ensuring that the same + // category is not added twice. + int numFacetsA = random.nextInt(3) + 1; // 1-3 + int numFacetsB = random.nextInt(2) + 1; // 1-2 + ArrayList categories_a = new ArrayList(); + categories_a.addAll(Arrays.asList(CATEGORIES_A)); + ArrayList categories_b = new ArrayList(); + categories_b.addAll(Arrays.asList(CATEGORIES_B)); + Collections.shuffle(categories_a, random); + Collections.shuffle(categories_b, random); + + ArrayList categories = new ArrayList(); + categories.addAll(categories_a.subList(0, numFacetsA)); + categories.addAll(categories_b.subList(0, numFacetsB)); + return categories; + } + + private static void addField(Document doc) { + doc.add(new StringField(A.field(), A.text(), Store.NO)); + } + + private static void addFacets(Document doc, FacetFields facetFields, boolean updateTermExpectedCounts) + throws IOException { + List docCategories = randomCategories(random()); + for (CategoryPath cp : docCategories) { + allExpectedCounts.put(cp, allExpectedCounts.get(cp) + 1); + if (updateTermExpectedCounts) { + termExpectedCounts.put(cp, termExpectedCounts.get(cp) + 1); + } + } + // add 1 to each dimension + allExpectedCounts.put(CP_A, allExpectedCounts.get(CP_A) + 1); + allExpectedCounts.put(CP_B, allExpectedCounts.get(CP_B) + 1); + if (updateTermExpectedCounts) { + termExpectedCounts.put(CP_A, termExpectedCounts.get(CP_A) + 1); + termExpectedCounts.put(CP_B, termExpectedCounts.get(CP_B) + 1); + } + + facetFields.addFields(doc, docCategories); + } + + private static void indexDocsNoFacets(IndexWriter indexWriter) throws IOException { + int numDocs = atLeast(2); + for (int i = 0; i < numDocs; i++) { + Document doc = new Document(); + addField(doc); + indexWriter.addDocument(doc); + } + indexWriter.commit(); // flush a segment + } + + private static void indexDocsWithFacetsNoTerms(IndexWriter indexWriter, TaxonomyWriter taxoWriter, + ObjectToIntMap expectedCounts) throws IOException { + Random random = random(); + int numDocs = atLeast(random, 2); + FacetFields facetFields = new FacetFields(taxoWriter); + for (int i = 0; i < numDocs; i++) { + Document doc = new Document(); + addFacets(doc, facetFields, false); + indexWriter.addDocument(doc); + } + indexWriter.commit(); // flush a segment + } + + private static void indexDocsWithFacetsAndTerms(IndexWriter indexWriter, TaxonomyWriter taxoWriter, + ObjectToIntMap expectedCounts) throws IOException { + Random random = random(); + int numDocs = atLeast(random, 2); + FacetFields facetFields = new FacetFields(taxoWriter); + for (int i = 0; i < numDocs; i++) { + Document doc = new Document(); + addFacets(doc, facetFields, true); + addField(doc); + indexWriter.addDocument(doc); + } + indexWriter.commit(); // flush a segment + } + + private static void indexDocsWithFacetsAndSomeTerms(IndexWriter indexWriter, TaxonomyWriter taxoWriter, + ObjectToIntMap expectedCounts) throws IOException { + Random random = random(); + int numDocs = atLeast(random, 2); + FacetFields facetFields = new FacetFields(taxoWriter); + for (int i = 0; i < numDocs; i++) { + Document doc = new Document(); + boolean hasContent = random.nextBoolean(); + if (hasContent) { + addField(doc); + } + addFacets(doc, facetFields, hasContent); + indexWriter.addDocument(doc); + } + indexWriter.commit(); // flush a segment + } + + // initialize expectedCounts w/ 0 for all categories + private static ObjectToIntMap newCounts() { + ObjectToIntMap counts = new ObjectToIntMap(); + counts.put(CP_A, 0); + counts.put(CP_B, 0); + for (CategoryPath cp : CATEGORIES_A) { + counts.put(cp, 0); + } + for (CategoryPath cp : CATEGORIES_B) { + counts.put(cp, 0); + } + return counts; + } + + @BeforeClass + public static void beforeClassCountingFacetsCollectorTest() throws Exception { + indexDir = newDirectory(); + taxoDir = newDirectory(); + + // create an index which has: + // 1. Segment with no categories, but matching results + // 2. Segment w/ categories, but no results + // 3. Segment w/ categories and results + // 4. Segment w/ categories, but only some results + + IndexWriterConfig conf = newIndexWriterConfig(TEST_VERSION_CURRENT, new MockAnalyzer(random())); + conf.setMergePolicy(NoMergePolicy.COMPOUND_FILES); // prevent merges, so we can control the index segments + IndexWriter indexWriter = new IndexWriter(indexDir, conf); + TaxonomyWriter taxoWriter = new DirectoryTaxonomyWriter(taxoDir); + + allExpectedCounts = newCounts(); + termExpectedCounts = newCounts(); + + // segment w/ no categories + indexDocsNoFacets(indexWriter); + + // segment w/ categories, no content + indexDocsWithFacetsNoTerms(indexWriter, taxoWriter, allExpectedCounts); + + // segment w/ categories and content + indexDocsWithFacetsAndTerms(indexWriter, taxoWriter, allExpectedCounts); + + // segment w/ categories and some content + indexDocsWithFacetsAndSomeTerms(indexWriter, taxoWriter, allExpectedCounts); + + // set num children indexed from each dimension + for (CategoryPath cp : CATEGORIES_A) { + if (termExpectedCounts.get(cp) > 0) { + ++numChildrenIndexedA; + } + } + for (CategoryPath cp : CATEGORIES_B) { + if (termExpectedCounts.get(cp) > 0) { + ++numChildrenIndexedB; + } + } + + IOUtils.close(indexWriter, taxoWriter); + } + + @Test + public void testInvalidValidParams() throws Exception { + final CategoryPath dummyCP = new CategoryPath("a"); + final FacetRequest dummyFR = new CountFacetRequest(dummyCP, 10); + + // only CountFacetRequests are allowed + assertNotNull("only CountFacetRequests should be allowed", + CountingFacetsCollector.assertParams(new FacetSearchParams(new ScoreFacetRequest(dummyCP, 10)))); + + // only depth=1 + FacetRequest cfr = new CountFacetRequest(dummyCP, 10); + cfr.setDepth(2); + assertNotNull("only depth 1 should be allowed", CountingFacetsCollector.assertParams(new FacetSearchParams(cfr))); + + // no numToLabel + cfr = new CountFacetRequest(dummyCP, 10); + cfr.setNumLabel(2); + assertNotNull("numToLabel should not be allowed", CountingFacetsCollector.assertParams(new FacetSearchParams(cfr))); + + FacetIndexingParams fip = new FacetIndexingParams(new CategoryListParams("moo")) { + @Override + public List getAllCategoryListParams() { + return Arrays.asList(new CategoryListParams[] { clParams, clParams }); + } + }; + assertNotNull("only one CLP should be allowed", CountingFacetsCollector.assertParams(new FacetSearchParams(fip, dummyFR))); + + fip = new FacetIndexingParams(new CategoryListParams("moo")) { + final CategoryListParams clp = new CategoryListParams() { + @Override + public IntEncoder createEncoder() { + return new VInt8IntEncoder(); + } + }; + @Override + public List getAllCategoryListParams() { + return Collections.singletonList(clp); + } + + @Override + public CategoryListParams getCategoryListParams(CategoryPath category) { + return clp; + } + }; + assertNotNull("only DGapVIntEncoder should be allowed", CountingFacetsCollector.assertParams(new FacetSearchParams(fip, dummyFR))); + + fip = new FacetIndexingParams(new CategoryListParams("moo")) { + @Override + public int getPartitionSize() { + return 2; + } + }; + assertNotNull("partitions should be allowed", CountingFacetsCollector.assertParams(new FacetSearchParams(fip, dummyFR))); + + fip = new FacetIndexingParams(new CategoryListParams("moo")) { + @Override + public OrdinalPolicy getOrdinalPolicy() { + return OrdinalPolicy.NO_PARENTS; + } + }; + assertNotNull("only ALL_PARENTS policy should be allowed", CountingFacetsCollector.assertParams(new FacetSearchParams(fip, dummyFR))); + } + + @Test + public void testDifferentNumResults() throws Exception { + // test the collector w/ FacetRequests and different numResults + DirectoryReader indexReader = DirectoryReader.open(indexDir); + TaxonomyReader taxoReader = new DirectoryTaxonomyReader(taxoDir); + IndexSearcher searcher = new IndexSearcher(indexReader); + + FacetSearchParams fsp = new FacetSearchParams(new CountFacetRequest(CP_A, NUM_CHILDREN_CP_A), + new CountFacetRequest(CP_B, NUM_CHILDREN_CP_B)); + FacetsCollector fc = new CountingFacetsCollector(fsp , taxoReader); + TermQuery q = new TermQuery(A); + searcher.search(q, fc); + + List facetResults = fc.getFacetResults(); + assertEquals("invalid number of facet results", 2, facetResults.size()); + for (FacetResult res : facetResults) { + FacetResultNode root = res.getFacetResultNode(); + assertEquals("wrong count for " + root.getLabel(), termExpectedCounts.get(root.getLabel()), (int) root.getValue()); + assertEquals("invalid residue", 0, (int) root.getResidue()); + for (FacetResultNode child : root.getSubResults()) { + assertEquals("wrong count for " + child.getLabel(), termExpectedCounts.get(child.getLabel()), (int) child.getValue()); + } + } + + IOUtils.close(indexReader, taxoReader); + } + + @Test + public void testResidue() throws Exception { + // test the collector's handling of residue + DirectoryReader indexReader = DirectoryReader.open(indexDir); + TaxonomyReader taxoReader = new DirectoryTaxonomyReader(taxoDir); + IndexSearcher searcher = new IndexSearcher(indexReader); + + // asking for top 1 is the only way to guarantee there will be a residue + // provided that enough children were indexed (see below) + FacetSearchParams fsp = new FacetSearchParams(new CountFacetRequest(CP_A, 1), new CountFacetRequest(CP_B, 1)); + FacetsCollector fc = new CountingFacetsCollector(fsp , taxoReader); + TermQuery q = new TermQuery(A); + searcher.search(q, fc); + + List facetResults = fc.getFacetResults(); + assertEquals("invalid number of facet results", 2, facetResults.size()); + for (FacetResult res : facetResults) { + FacetResultNode root = res.getFacetResultNode(); + assertEquals("wrong count for " + root.getLabel(), termExpectedCounts.get(root.getLabel()), (int) root.getValue()); + // make sure randomness didn't pick only one child of root (otherwise there's no residue) + int numChildrenIndexed = res.getFacetRequest().categoryPath == CP_A ? numChildrenIndexedA : numChildrenIndexedB; + if (numChildrenIndexed > 1) { + assertTrue("expected residue", root.getResidue() > 0); + } + for (FacetResultNode child : root.getSubResults()) { + assertEquals("wrong count for " + child.getLabel(), termExpectedCounts.get(child.getLabel()), (int) child.getValue()); + } + } + + IOUtils.close(indexReader, taxoReader); + } + + @Test + public void testAllCounts() throws Exception { + DirectoryReader indexReader = DirectoryReader.open(indexDir); + TaxonomyReader taxoReader = new DirectoryTaxonomyReader(taxoDir); + IndexSearcher searcher = new IndexSearcher(indexReader); + + FacetSearchParams fsp = new FacetSearchParams(new CountFacetRequest(CP_A, NUM_CHILDREN_CP_A), + new CountFacetRequest(CP_B, NUM_CHILDREN_CP_B)); + FacetsCollector fc = new CountingFacetsCollector(fsp , taxoReader); + searcher.search(new MatchAllDocsQuery(), fc); + + List facetResults = fc.getFacetResults(); + assertEquals("invalid number of facet results", 2, facetResults.size()); + for (FacetResult res : facetResults) { + FacetResultNode root = res.getFacetResultNode(); + assertEquals("wrong count for " + root.getLabel(), allExpectedCounts.get(root.getLabel()), (int) root.getValue()); + assertEquals("invalid residue", 0, (int) root.getResidue()); + for (FacetResultNode child : root.getSubResults()) { + assertEquals("wrong count for " + child.getLabel(), allExpectedCounts.get(child.getLabel()), (int) child.getValue()); + } + } + + IOUtils.close(indexReader, taxoReader); + } + +} Property changes on: lucene/facet/src/test/org/apache/lucene/facet/search/CountingFacetsCollectorTest.java ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lucene/facet/src/test/org/apache/lucene/facet/search/SamplingWrapperTest.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/SamplingWrapperTest.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/search/SamplingWrapperTest.java (working copy) @@ -29,11 +29,10 @@ public class SamplingWrapperTest extends BaseSampleTestTopK { @Override - protected FacetsAccumulator getSamplingAccumulator(Sampler sampler, - TaxonomyReader taxoReader, IndexReader indexReader, - FacetSearchParams searchParams) { - FacetsAccumulator fExtrctr = new StandardFacetsAccumulator(searchParams, - indexReader, taxoReader); - return new SamplingWrapper(fExtrctr, sampler); + protected FacetsAccumulator getSamplingAccumulator(Sampler sampler, TaxonomyReader taxoReader, + IndexReader indexReader, FacetSearchParams searchParams) { + FacetsAccumulator fa = new StandardFacetsAccumulator(searchParams, indexReader, taxoReader); + return new SamplingWrapper(fa, sampler); } + } Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestDemoFacets.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestDemoFacets.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/search/TestDemoFacets.java (working copy) @@ -22,11 +22,11 @@ import java.util.List; import org.apache.lucene.document.Document; +import org.apache.lucene.facet.FacetTestUtils; import org.apache.lucene.facet.index.FacetFields; import org.apache.lucene.facet.search.params.CountFacetRequest; import org.apache.lucene.facet.search.params.FacetSearchParams; import org.apache.lucene.facet.search.results.FacetResult; -import org.apache.lucene.facet.search.results.FacetResultNode; import org.apache.lucene.facet.taxonomy.CategoryPath; import org.apache.lucene.facet.taxonomy.TaxonomyReader; import org.apache.lucene.facet.taxonomy.directory.DirectoryTaxonomyReader; @@ -89,7 +89,7 @@ new CountFacetRequest(new CategoryPath("Author"), 10)); // Aggregatses the facet counts: - FacetsCollector c = new FacetsCollector(fsp, searcher.getIndexReader(), taxoReader); + FacetsCollector c = FacetsCollector.create(fsp, searcher.getIndexReader(), taxoReader); // MatchAllDocsQuery is for "browsing" (counts facets // for all non-deleted docs in the index); normally @@ -101,20 +101,20 @@ List results = c.getFacetResults(); assertEquals(2, results.size()); assertEquals("Publish Date (5)\n 2012 (2)\n 2010 (2)\n 1999 (1)\n", - toSimpleString(results.get(0))); + FacetTestUtils.toSimpleString(results.get(0))); assertEquals("Author (5)\n Lisa (2)\n Frank (1)\n Susan (1)\n Bob (1)\n", - toSimpleString(results.get(1))); + FacetTestUtils.toSimpleString(results.get(1))); // Now user drills down on Publish Date/2010: fsp = new FacetSearchParams(new CountFacetRequest(new CategoryPath("Author"), 10)); Query q2 = DrillDown.query(fsp, new MatchAllDocsQuery(), new CategoryPath("Publish Date/2010", '/')); - c = new FacetsCollector(fsp, searcher.getIndexReader(), taxoReader); + c = FacetsCollector.create(fsp, searcher.getIndexReader(), taxoReader); searcher.search(q2, c); results = c.getFacetResults(); assertEquals(1, results.size()); assertEquals("Author (2)\n Lisa (1)\n Bob (1)\n", - toSimpleString(results.get(0))); + FacetTestUtils.toSimpleString(results.get(0))); taxoReader.close(); searcher.getIndexReader().close(); @@ -122,16 +122,4 @@ taxoDir.close(); } - private String toSimpleString(FacetResult fr) { - StringBuilder sb = new StringBuilder(); - toSimpleString(0, sb, fr.getFacetResultNode(), ""); - return sb.toString(); - } - - private void toSimpleString(int depth, StringBuilder sb, FacetResultNode node, String indent) { - sb.append(indent + node.getLabel().components[depth] + " (" + (int) node.getValue() + ")\n"); - for(FacetResultNode childNode : node.getSubResults()) { - toSimpleString(depth+1, sb, childNode, indent + " "); - } - } } Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestFacetsCollector.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestFacetsCollector.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/search/TestFacetsCollector.java (working copy) @@ -71,7 +71,7 @@ DirectoryReader r = DirectoryReader.open(indexDir); DirectoryTaxonomyReader taxo = new DirectoryTaxonomyReader(taxoDir); - FacetsCollector fc = new FacetsCollector(sParams, r, taxo); + FacetsCollector fc = FacetsCollector.create(sParams, r, taxo); TopScoreDocCollector topDocs = TopScoreDocCollector.create(10, false); new IndexSearcher(r).search(new MatchAllDocsQuery(), MultiCollector.wrap(fc, topDocs)); Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestMultipleCategoryLists.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestMultipleCategoryLists.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/search/TestMultipleCategoryLists.java (working copy) @@ -334,7 +334,7 @@ FacetSearchParams facetSearchParams = new FacetSearchParams(facetRequests, iParams); // perform documents search and facets accumulation - FacetsCollector facetsCollector = new FacetsCollector(facetSearchParams, ir, tr); + FacetsCollector facetsCollector = FacetsCollector.create(facetSearchParams, ir, tr); searcher.search(q, MultiCollector.wrap(topDocsCollector, facetsCollector)); return facetsCollector; } Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestSameRequestAccumulation.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestSameRequestAccumulation.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/search/TestSameRequestAccumulation.java (working copy) @@ -44,7 +44,7 @@ final CountFacetRequest facetRequest = new CountFacetRequest(new CategoryPath("root"), 10); FacetSearchParams fsp = new FacetSearchParams(facetRequest); - FacetsCollector fc = new FacetsCollector(fsp, indexReader, taxoReader); + FacetsCollector fc = FacetsCollector.create(fsp, indexReader, taxoReader); searcher.search(new MatchAllDocsQuery(), fc); final String expected = fc.getFacetResults().get(0).toString(); @@ -53,9 +53,9 @@ fsp = new FacetSearchParams(facetRequest, facetRequest, new CountFacetRequest(new CategoryPath("root"), 10)); // make sure the search params holds 3 requests now - assertEquals(3, fsp.getFacetRequests().size()); + assertEquals(3, fsp.facetRequests.size()); - fc = new FacetsCollector(fsp, indexReader, taxoReader); + fc = FacetsCollector.create(fsp, indexReader, taxoReader); searcher.search(new MatchAllDocsQuery(), fc); List actual = fc.getFacetResults(); Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestStandardFacetsAccumulator.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestStandardFacetsAccumulator.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/search/TestStandardFacetsAccumulator.java (working copy) @@ -1,7 +1,6 @@ package org.apache.lucene.facet.search; import java.io.IOException; -import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -102,16 +101,14 @@ // search for "f:a", only segments 1 and 3 should match results Query q = new TermQuery(new Term("f", "a")); - ArrayList requests = new ArrayList(1); - CountFacetRequest countNoComplements = new CountFacetRequest(new CategoryPath("A"), 10) { + FacetRequest countNoComplements = new CountFacetRequest(new CategoryPath("A"), 10) { @Override public boolean supportsComplements() { return false; // disable complements } }; - requests.add(countNoComplements); - FacetSearchParams fsp = new FacetSearchParams(requests, fip); - FacetsCollector fc = new FacetsCollector(fsp , indexReader, taxoReader); + FacetSearchParams fsp = new FacetSearchParams(fip, countNoComplements); + FacetsCollector fc = new StandardFacetsCollector(fsp , indexReader, taxoReader); indexSearcher.search(q, fc); List results = fc.getFacetResults(); assertEquals("received too many facet results", 1, results.size()); Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestTopKInEachNodeResultHandler.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestTopKInEachNodeResultHandler.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/search/TestTopKInEachNodeResultHandler.java (working copy) @@ -165,7 +165,7 @@ facetRequests.add(cfrb20); FacetSearchParams facetSearchParams = new FacetSearchParams(facetRequests, iParams); - FacetArrays facetArrays = new FacetArrays(PartitionsUtils.partitionSize(facetSearchParams.getFacetIndexingParams(), tr)); + FacetArrays facetArrays = new FacetArrays(PartitionsUtils.partitionSize(facetSearchParams.indexingParams, tr)); FacetsAccumulator fctExtrctr = new StandardFacetsAccumulator(facetSearchParams, is.getIndexReader(), tr, facetArrays); fctExtrctr.setComplementThreshold(FacetsAccumulator.DISABLE_COMPLEMENT); long start = System.currentTimeMillis(); Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestTopKResultsHandler.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestTopKResultsHandler.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/search/TestTopKResultsHandler.java (working copy) @@ -89,7 +89,7 @@ // do different facet counts and compare to control FacetSearchParams sParams = getFacetSearchParams(facetRequests, getFacetIndexingParams(partitionSize)); - FacetsCollector fc = new FacetsCollector(sParams, indexReader, taxoReader) { + FacetsCollector fc = new StandardFacetsCollector(sParams, indexReader, taxoReader) { @Override protected FacetsAccumulator initFacetsAccumulator(FacetSearchParams facetSearchParams, IndexReader indexReader, TaxonomyReader taxonomyReader) { FacetsAccumulator fa = new StandardFacetsAccumulator(facetSearchParams, indexReader, taxonomyReader); @@ -159,10 +159,10 @@ // do different facet counts and compare to control CategoryPath path = new CategoryPath("a", "b"); - FacetSearchParams sParams = getFacetSearchParams( - getFacetIndexingParams(partitionSize), new CountFacetRequest(path, Integer.MAX_VALUE)); + FacetSearchParams sParams = getFacetSearchParams(getFacetIndexingParams(partitionSize), + new CountFacetRequest(path, Integer.MAX_VALUE)); - FacetsCollector fc = new FacetsCollector(sParams, indexReader, taxoReader) { + FacetsCollector fc = new StandardFacetsCollector(sParams, indexReader, taxoReader) { @Override protected FacetsAccumulator initFacetsAccumulator(FacetSearchParams facetSearchParams, IndexReader indexReader, TaxonomyReader taxonomyReader) { FacetsAccumulator fa = new StandardFacetsAccumulator(facetSearchParams, indexReader, taxonomyReader); @@ -188,7 +188,7 @@ FacetSearchParams sParams2 = getFacetSearchParams( getFacetIndexingParams(partitionSize), new CountFacetRequest(path, Integer.MAX_VALUE)); - FacetsCollector fc2 = new FacetsCollector(sParams2, indexReader, taxoReader) { + FacetsCollector fc2 = new StandardFacetsCollector(sParams2, indexReader, taxoReader) { @Override protected FacetsAccumulator initFacetsAccumulator(FacetSearchParams facetSearchParams, IndexReader indexReader, TaxonomyReader taxonomyReader) { FacetsAccumulator fa = new StandardFacetsAccumulator(facetSearchParams, indexReader, taxonomyReader); @@ -226,7 +226,7 @@ getFacetIndexingParams(partitionSize), new CountFacetRequest(path, 10)); - FacetsCollector fc = new FacetsCollector(sParams, indexReader, taxoReader); + FacetsCollector fc = FacetsCollector.create(sParams, indexReader, taxoReader); searcher.search(new MatchAllDocsQuery(), fc); Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestTopKResultsHandlerRandom.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestTopKResultsHandlerRandom.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/search/TestTopKResultsHandlerRandom.java (working copy) @@ -4,16 +4,15 @@ import java.util.HashMap; import java.util.List; +import org.apache.lucene.facet.search.params.FacetSearchParams; +import org.apache.lucene.facet.search.results.FacetResult; +import org.apache.lucene.facet.search.results.FacetResultNode; +import org.apache.lucene.facet.taxonomy.TaxonomyReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; import org.junit.Test; -import org.apache.lucene.facet.search.params.FacetSearchParams; -import org.apache.lucene.facet.search.results.FacetResult; -import org.apache.lucene.facet.search.results.FacetResultNode; -import org.apache.lucene.facet.taxonomy.TaxonomyReader; - /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -37,7 +36,7 @@ throws IOException { Query q = new MatchAllDocsQuery(); FacetSearchParams facetSearchParams = searchParamsWithRequests(numResults, partitionSize); - FacetsCollector fc = new FacetsCollector(facetSearchParams, indexReader, taxoReader) { + FacetsCollector fc = new StandardFacetsCollector(facetSearchParams, indexReader, taxoReader) { @Override protected FacetsAccumulator initFacetsAccumulator( FacetSearchParams facetSearchParams, IndexReader indexReader, Index: lucene/facet/src/test/org/apache/lucene/facet/search/associations/AssociationsFacetRequestTest.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/associations/AssociationsFacetRequestTest.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/search/associations/AssociationsFacetRequestTest.java (working copy) @@ -110,7 +110,7 @@ Query q = new MatchAllDocsQuery(); - FacetsCollector fc = new FacetsCollector(fsp, reader, taxo); + FacetsCollector fc = FacetsCollector.create(fsp, reader, taxo); IndexSearcher searcher = newSearcher(reader); searcher.search(q, fc); @@ -135,7 +135,7 @@ Query q = new MatchAllDocsQuery(); - FacetsCollector fc = new FacetsCollector(fsp, reader, taxo); + FacetsCollector fc = FacetsCollector.create(fsp, reader, taxo); IndexSearcher searcher = newSearcher(reader); searcher.search(q, fc); @@ -165,7 +165,7 @@ Query q = new MatchAllDocsQuery(); - FacetsCollector fc = new FacetsCollector(fsp, reader, taxo); + FacetsCollector fc = FacetsCollector.create(fsp, reader, taxo); IndexSearcher searcher = newSearcher(reader); searcher.search(q, fc); Index: lucene/facet/src/test/org/apache/lucene/facet/search/sampling/BaseSampleTestTopK.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/sampling/BaseSampleTestTopK.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/search/sampling/BaseSampleTestTopK.java (working copy) @@ -3,22 +3,22 @@ import java.util.List; import java.util.Random; -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.Term; -import org.apache.lucene.search.Query; -import org.apache.lucene.search.TermQuery; - -import org.apache.lucene.search.MultiCollector; import org.apache.lucene.facet.search.BaseTestTopK; import org.apache.lucene.facet.search.FacetsAccumulator; import org.apache.lucene.facet.search.FacetsCollector; import org.apache.lucene.facet.search.ScoredDocIDs; import org.apache.lucene.facet.search.ScoredDocIdCollector; +import org.apache.lucene.facet.search.StandardFacetsCollector; import org.apache.lucene.facet.search.params.FacetRequest; import org.apache.lucene.facet.search.params.FacetRequest.ResultMode; import org.apache.lucene.facet.search.params.FacetSearchParams; import org.apache.lucene.facet.search.results.FacetResult; import org.apache.lucene.facet.taxonomy.TaxonomyReader; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.MultiCollector; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.TermQuery; /* * Licensed to the Apache Software Foundation (ASF) under one or more @@ -48,7 +48,7 @@ @Override protected FacetSearchParams searchParamsWithRequests(int numResults, int partitionSize) { FacetSearchParams res = super.searchParamsWithRequests(numResults, partitionSize); - for (FacetRequest req : res.getFacetRequests()) { + for (FacetRequest req : res.facetRequests) { // randomize the way we aggregate results if (random().nextBoolean()) { req.setResultMode(ResultMode.GLOBAL_FLAT); @@ -78,7 +78,7 @@ ScoredDocIdCollector docCollector = ScoredDocIdCollector.create(indexReader.maxDoc(), false); FacetSearchParams expectedSearchParams = searchParamsWithRequests(K, partitionSize); - FacetsCollector fc = new FacetsCollector(expectedSearchParams, indexReader, taxoReader); + FacetsCollector fc = FacetsCollector.create(expectedSearchParams, indexReader, taxoReader); searcher.search(q, MultiCollector.wrap(docCollector, fc)); @@ -97,7 +97,7 @@ assertSampling(expectedResults, q, sampler, samplingSearchParams, true); break; // succeeded - } catch (NotSameResultError e) { + } catch (AssertionError e) { if (nTrial >= RETRIES - 1) { throw e; // no more retries allowed, must fail } @@ -120,7 +120,7 @@ private FacetsCollector samplingCollector(final boolean complement, final Sampler sampler, FacetSearchParams samplingSearchParams) { - FacetsCollector samplingFC = new FacetsCollector(samplingSearchParams, indexReader, taxoReader) { + FacetsCollector samplingFC = new StandardFacetsCollector(samplingSearchParams, indexReader, taxoReader) { @Override protected FacetsAccumulator initFacetsAccumulator(FacetSearchParams facetSearchParams, IndexReader indexReader, TaxonomyReader taxonomyReader) { Index: lucene/facet/src/test/org/apache/lucene/facet/search/sampling/OversampleWithDepthTest.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/sampling/OversampleWithDepthTest.java (revision 1435786) +++ lucene/facet/src/test/org/apache/lucene/facet/search/sampling/OversampleWithDepthTest.java (working copy) @@ -8,6 +8,7 @@ import org.apache.lucene.facet.index.FacetFields; import org.apache.lucene.facet.search.FacetsAccumulator; import org.apache.lucene.facet.search.FacetsCollector; +import org.apache.lucene.facet.search.StandardFacetsCollector; import org.apache.lucene.facet.search.params.CountFacetRequest; import org.apache.lucene.facet.search.params.FacetRequest; import org.apache.lucene.facet.search.params.FacetRequest.ResultMode; @@ -111,11 +112,10 @@ } /** search reader r*/ - private FacetResult searchWithFacets(IndexReader r, - TaxonomyReader tr, FacetSearchParams fsp, final SamplingParams params) - throws IOException { + private FacetResult searchWithFacets(IndexReader r, TaxonomyReader tr, FacetSearchParams fsp, + final SamplingParams params) throws IOException { // a FacetsCollector with a sampling accumulator - FacetsCollector fcWithSampling = new FacetsCollector(fsp, r, tr) { + FacetsCollector fcWithSampling = new StandardFacetsCollector(fsp, r, tr) { @Override protected FacetsAccumulator initFacetsAccumulator(FacetSearchParams facetSearchParams, IndexReader indexReader, TaxonomyReader taxonomyReader) {