Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestDrillSideways.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestDrillSideways.java (revision 1465298) +++ lucene/facet/src/test/org/apache/lucene/facet/search/TestDrillSideways.java (working copy) @@ -150,10 +150,13 @@ // Publish Date is only drill-down, and Lisa published // one in 2012 and one in 2010: assertEquals("Publish Date: 2012=1 2010=1", toString(r.facetResults.get(0))); + assertEquals(2, r.facetResults.get(0).getNumUniqueChildren()); + // Author is drill-sideways + drill-down: Lisa // (drill-down) published twice, and Frank/Susan/Bob // published once: assertEquals("Author: Lisa=2 Frank=1 Susan=1 Bob=1", toString(r.facetResults.get(1))); + assertEquals(4, r.facetResults.get(1).getNumUniqueChildren()); // Another simple case: drill-down on on single fields // but OR of two values @@ -735,6 +738,7 @@ ds = new DrillSideways(s, tr); } + // Retrieve all facets: DrillSidewaysResult actual = ds.search(ddq, filter, null, numDocs, sort, true, true, fsp); TopDocs hits = s.search(baseQuery, numDocs); @@ -742,9 +746,12 @@ for(ScoreDoc sd : hits.scoreDocs) { scores.put(s.doc(sd.doc).get("id"), sd.score); } + if (VERBOSE) { + System.out.println(" verify all facets"); + } verifyEquals(dimValues, s, expected, actual, scores, -1, doUseDV); - // Make sure topN works: + // Retrieve topN facets: int topN = _TestUtil.nextInt(random(), 1, 20); requests = new ArrayList(); @@ -753,6 +760,9 @@ } fsp = new FacetSearchParams(requests); actual = ds.search(ddq, filter, null, numDocs, sort, true, true, fsp); + if (VERBOSE) { + System.out.println(" verify topN=" + topN); + } verifyEquals(dimValues, s, expected, actual, scores, topN, doUseDV); // Make sure drill down doesn't change score: @@ -803,6 +813,7 @@ private static class SimpleFacetResult { List hits; int[][] counts; + int[] uniqueCounts; } private int[] getTopNOrds(final int[] counts, final String[] values, int topN) { @@ -953,12 +964,20 @@ SimpleFacetResult res = new SimpleFacetResult(); res.hits = hits; res.counts = new int[numDims][]; + res.uniqueCounts = new int[numDims]; for(int dim=0;dim 0) { + childCount++; + if (counts[ord] > bottomCount) { + dimCount += counts[ord]; + //System.out.println(" keep"); + if (reuse == null) { + reuse = new FacetResultNode(ord, counts[ord]); + } else { + reuse.ordinal = ord; + reuse.value = counts[ord]; + } + reuse = q.insertWithOverflow(reuse); + if (q.size() == request.numResults) { + bottomCount = (int) q.top().value; + //System.out.println(" new bottom=" + bottomCount); + } } - reuse = q.insertWithOverflow(reuse); - if (q.size() == request.numResults) { - bottomCount = (int) q.top().value; - //System.out.println(" new bottom=" + bottomCount); - } } } @@ -295,7 +299,7 @@ } rootNode.subResults = Arrays.asList(childNodes); - results.add(new FacetResult(request, rootNode, childNodes.length)); + results.add(new FacetResult(request, rootNode, childNodes.length, childCount)); } return results; Index: lucene/facet/src/java/org/apache/lucene/facet/search/TopKFacetResultsHandler.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/TopKFacetResultsHandler.java (revision 1465298) +++ lucene/facet/src/java/org/apache/lucene/facet/search/TopKFacetResultsHandler.java (working copy) @@ -63,7 +63,7 @@ Heap heap = ResultSortUtils.createSuitableHeap(facetRequest); int totalFacets = heapDescendants(ordinal, heap, parentResultNode, offset); - res = new TopKFacetResult(facetRequest, parentResultNode, totalFacets); + res = new TopKFacetResult(facetRequest, parentResultNode, totalFacets, -1); res.setHeap(heap); } return res; @@ -77,6 +77,7 @@ FacetResultNode resNode = new FacetResultNode(ordinal, 0); int totalFacets = 0; + int totalUniqueFacets = 0; Heap heap = null; // merge other results in queue @@ -84,6 +85,7 @@ // cast should succeed TopKFacetResult fres = (TopKFacetResult) tmpFres; totalFacets += fres.getNumValidDescendants(); + totalUniqueFacets += fres.getNumUniqueChildren(); // set the value for the result node representing the facet request resNode.value += fres.getFacetResultNode().value; Heap tmpHeap = fres.getHeap(); @@ -97,7 +99,7 @@ } } - TopKFacetResult res = new TopKFacetResult(facetRequest, resNode, totalFacets); + TopKFacetResult res = new TopKFacetResult(facetRequest, resNode, totalFacets, totalUniqueFacets); res.setHeap(heap); return res; } @@ -258,8 +260,8 @@ * @param facetResultNode top result node for this facet result. * @param totalFacets - number of children of the targetFacet, up till the requested depth. */ - TopKFacetResult(FacetRequest facetRequest, FacetResultNode facetResultNode, int totalFacets) { - super(facetRequest, facetResultNode, totalFacets); + TopKFacetResult(FacetRequest facetRequest, FacetResultNode facetResultNode, int totalFacets, int totalUniqueFacets) { + super(facetRequest, facetResultNode, totalFacets, totalUniqueFacets); } /** Index: lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java (revision 1465298) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java (working copy) @@ -27,11 +27,13 @@ private final FacetRequest facetRequest; private final FacetResultNode rootNode; private final int numValidDescendants; + private final int numUniqueChildren; - public FacetResult(FacetRequest facetRequest, FacetResultNode rootNode, int numValidDescendants) { + public FacetResult(FacetRequest facetRequest, FacetResultNode rootNode, int numValidDescendants, int numUniqueChildren) { this.facetRequest = facetRequest; this.rootNode = rootNode; this.numValidDescendants = numValidDescendants; + this.numUniqueChildren = numUniqueChildren; } /** @@ -61,6 +63,16 @@ public final FacetRequest getFacetRequest() { return this.facetRequest; } + + /** + * Returns the total count of all unique children, or -1 + * if this value is not available. Use + * this to know if there are more facets beyond the top K + * that were selected. + */ + public int getNumUniqueChildren() { + return numUniqueChildren; + } /** * String representation of this facet result. Index: lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java (revision 1465298) +++ lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java (working copy) @@ -202,7 +202,7 @@ root.ordinal = TaxonomyReader.INVALID_ORDINAL; root.label = fr.categoryPath; root.value = 0; - res.add(new FacetResult(fr, root, 0)); + res.add(new FacetResult(fr, root, 0, 0)); continue; } FacetResult facetRes = frHndlr.renderFacetResult(tmpResult); Index: lucene/facet/src/java/org/apache/lucene/facet/search/TopKInEachNodeHandler.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/TopKInEachNodeHandler.java (revision 1465298) +++ lucene/facet/src/java/org/apache/lucene/facet/search/TopKInEachNodeHandler.java (working copy) @@ -706,7 +706,7 @@ value = tmp.rootNodeValue; } FacetResultNode root = generateNode(ordinal, value, tmp.mapToAACOs); - return new FacetResult (tmp.facetRequest, root, tmp.totalNumOfFacetsConsidered); + return new FacetResult (tmp.facetRequest, root, tmp.totalNumOfFacetsConsidered, -1); } Index: lucene/facet/src/java/org/apache/lucene/facet/search/DepthOneFacetResultsHandler.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/DepthOneFacetResultsHandler.java (revision 1465298) +++ lucene/facet/src/java/org/apache/lucene/facet/search/DepthOneFacetResultsHandler.java (working copy) @@ -113,7 +113,7 @@ }); root.subResults = nodes; - return new FacetResult(facetRequest, root, nodes.size()); + return new FacetResult(facetRequest, root, nodes.size(), nodes.size()); } // since we use sentinel objects, we cannot reuse PQ. but that's ok because it's not big @@ -133,7 +133,7 @@ subResults[i] = node; } root.subResults = Arrays.asList(subResults); - return new FacetResult(facetRequest, root, size); + return new FacetResult(facetRequest, root, size, numResults); } } Index: lucene/facet/src/java/org/apache/lucene/facet/search/IntFacetResultsHandler.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/IntFacetResultsHandler.java (revision 1465298) +++ lucene/facet/src/java/org/apache/lucene/facet/search/IntFacetResultsHandler.java (working copy) @@ -49,11 +49,13 @@ int numResults = 0; while (ordinal != TaxonomyReader.INVALID_ORDINAL) { int value = values[ordinal]; - if (value > top.value) { - top.value = value; - top.ordinal = ordinal; - top = pq.updateTop(); + if (value > 0) { ++numResults; + if (value > top.value) { + top.value = value; + top.ordinal = ordinal; + top = pq.updateTop(); + } } ordinal = siblings[ordinal]; } Index: lucene/facet/src/java/org/apache/lucene/facet/search/FloatFacetResultsHandler.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FloatFacetResultsHandler.java (revision 1465298) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FloatFacetResultsHandler.java (working copy) @@ -50,11 +50,13 @@ int numResults = 0; while (ordinal != TaxonomyReader.INVALID_ORDINAL) { float value = values[ordinal]; - if (value > top.value) { - top.value = value; - top.ordinal = ordinal; - top = pq.updateTop(); + if (value > 0.0f) { ++numResults; + if (value > top.value) { + top.value = value; + top.ordinal = ordinal; + top = pq.updateTop(); + } } ordinal = siblings[ordinal]; } Index: lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java (revision 1465298) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java (working copy) @@ -86,7 +86,7 @@ root.ordinal = ordinal; root.label = fr.categoryPath; root.value = 0; - return new FacetResult(fr, root, 0); + return new FacetResult(fr, root, 0, 0); } /** Index: lucene/facet/src/java/org/apache/lucene/facet/sampling/Sampler.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/sampling/Sampler.java (revision 1465298) +++ lucene/facet/src/java/org/apache/lucene/facet/sampling/Sampler.java (working copy) @@ -170,7 +170,7 @@ FacetResultNode trimmedRootNode = facetResult.getFacetResultNode(); trimSubResults(trimmedRootNode, origFrq.numResults); - return new FacetResult(origFrq, trimmedRootNode, facetResult.getNumValidDescendants()); + return new FacetResult(origFrq, trimmedRootNode, facetResult.getNumValidDescendants(), -1); } /** Trim sub results to a given size. */