Index: lucene/facet/src/java/org/apache/lucene/facet/search/IntFacetResultsHandler.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/IntFacetResultsHandler.java (revision 1465372) +++ 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]; } @@ -65,7 +67,7 @@ while (ordinal != TaxonomyReader.INVALID_ORDINAL) { int value = values[ordinal]; if (value > 0) { - FacetResultNode node = new FacetResultNode(ordinal, value); + FacetResultNode node = new FacetResultNode(ordinal, value, -1); node.label = taxonomyReader.getPath(ordinal); nodes.add(node); } Index: lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java (revision 1465372) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetResult.java (working copy) @@ -61,7 +61,7 @@ public final FacetRequest getFacetRequest() { return this.facetRequest; } - + /** * String representation of this facet result. * Use with caution: might return a very long string. Index: lucene/facet/src/java/org/apache/lucene/facet/search/DepthOneFacetResultsHandler.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/DepthOneFacetResultsHandler.java (revision 1465372) +++ lucene/facet/src/java/org/apache/lucene/facet/search/DepthOneFacetResultsHandler.java (working copy) @@ -80,7 +80,8 @@ * Add the siblings of {@code ordinal} to the given {@link PriorityQueue}. The * given {@link PriorityQueue} is already filled with sentinel objects, so * implementations are encouraged to use {@link PriorityQueue#top()} and - * {@link PriorityQueue#updateTop()} for best performance. + * {@link PriorityQueue#updateTop()} for best performance. Returns the total + * number of siblings. */ protected abstract int addSiblings(int ordinal, int[] siblings, PriorityQueue pq); @@ -113,16 +114,17 @@ }); root.subResults = nodes; + root.numChildren = nodes.size(); return new FacetResult(facetRequest, root, nodes.size()); } // since we use sentinel objects, we cannot reuse PQ. but that's ok because it's not big PriorityQueue pq = new FacetResultNodeQueue(facetRequest.numResults, true); - int numResults = addSiblings(children[rootOrd], siblings, pq); + int numSiblings = addSiblings(children[rootOrd], siblings, pq); // pop() the least (sentinel) elements int pqsize = pq.size(); - int size = numResults < pqsize ? numResults : pqsize; + int size = numSiblings < pqsize ? numSiblings : pqsize; for (int i = pqsize - size; i > 0; i--) { pq.pop(); } // create the FacetResultNodes. @@ -133,6 +135,7 @@ subResults[i] = node; } root.subResults = Arrays.asList(subResults); + root.numChildren = numSiblings; return new FacetResult(facetRequest, root, size); } Index: lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java (revision 1465372) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetsAccumulator.java (working copy) @@ -82,10 +82,8 @@ } private static FacetResult emptyResult(int ordinal, FacetRequest fr) { - FacetResultNode root = new FacetResultNode(); - root.ordinal = ordinal; + FacetResultNode root = new FacetResultNode(ordinal, 0, 0); root.label = fr.categoryPath; - root.value = 0; return new FacetResult(fr, root, 0); } Index: lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java (revision 1465372) +++ lucene/facet/src/java/org/apache/lucene/facet/search/StandardFacetsAccumulator.java (working copy) @@ -198,10 +198,8 @@ IntermediateFacetResult tmpResult = fr2tmpRes.get(fr); if (tmpResult == null) { // Add empty FacetResult: - FacetResultNode root = new FacetResultNode(); - root.ordinal = TaxonomyReader.INVALID_ORDINAL; + FacetResultNode root = new FacetResultNode(TaxonomyReader.INVALID_ORDINAL, 0, 0); root.label = fr.categoryPath; - root.value = 0; res.add(new FacetResult(fr, root, 0)); continue; } Index: lucene/facet/src/java/org/apache/lucene/facet/search/FacetResultNode.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FacetResultNode.java (revision 1465372) +++ lucene/facet/src/java/org/apache/lucene/facet/search/FacetResultNode.java (working copy) @@ -57,6 +57,15 @@ public double value; /** + * Number of children that had values, or -1 if this value + * was not computed. Note that this + * can be higher than {@code subResults.size}, unless all + * children were collected in which case the two are + * equal. + */ + public int numChildren; + + /** * The sub-results of this result. If {@link FacetRequest#getResultMode()} is * {@link ResultMode#PER_NODE_IN_TREE}, every sub result denotes an immediate * child of this node. Otherwise, it is a descendant of any level. @@ -71,9 +80,10 @@ // empty constructor } - public FacetResultNode(int ordinal, double value) { + public FacetResultNode(int ordinal, double value, int numChildren) { this.ordinal = ordinal; this.value = value; + this.numChildren = numChildren; } @Override Index: lucene/facet/src/java/org/apache/lucene/facet/search/TopKInEachNodeHandler.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/TopKInEachNodeHandler.java (revision 1465372) +++ lucene/facet/src/java/org/apache/lucene/facet/search/TopKInEachNodeHandler.java (working copy) @@ -706,12 +706,12 @@ 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); } private FacetResultNode generateNode(int ordinal, double val, IntToObjectMap mapToAACOs) { - FacetResultNode node = new FacetResultNode(ordinal, val); + FacetResultNode node = new FacetResultNode(ordinal, val, -1); AACO aaco = mapToAACOs.get(ordinal); if (null == aaco) { return node; Index: lucene/facet/src/java/org/apache/lucene/facet/search/FloatFacetResultsHandler.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/FloatFacetResultsHandler.java (revision 1465372) +++ 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]; } @@ -66,7 +68,7 @@ while (ordinal != TaxonomyReader.INVALID_ORDINAL) { float value = values[ordinal]; if (value > 0) { - FacetResultNode node = new FacetResultNode(ordinal, value); + FacetResultNode node = new FacetResultNode(ordinal, value, -1); node.label = taxonomyReader.getPath(ordinal); nodes.add(node); } Index: lucene/facet/src/java/org/apache/lucene/facet/search/TopKFacetResultsHandler.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/search/TopKFacetResultsHandler.java (revision 1465372) +++ lucene/facet/src/java/org/apache/lucene/facet/search/TopKFacetResultsHandler.java (working copy) @@ -59,7 +59,7 @@ value = facetRequest.getValueOf(facetArrays, ordinal % partitionSize); } - FacetResultNode parentResultNode = new FacetResultNode(ordinal, value); + FacetResultNode parentResultNode = new FacetResultNode(ordinal, value, -1); Heap heap = ResultSortUtils.createSuitableHeap(facetRequest); int totalFacets = heapDescendants(ordinal, heap, parentResultNode, offset); @@ -74,7 +74,7 @@ public IntermediateFacetResult mergeResults(IntermediateFacetResult... tmpResults) throws IOException { int ordinal = taxonomyReader.getOrdinal(facetRequest.categoryPath); - FacetResultNode resNode = new FacetResultNode(ordinal, 0); + FacetResultNode resNode = new FacetResultNode(ordinal, 0, -1); int totalFacets = 0; Heap heap = null; @@ -162,7 +162,7 @@ if (value != 0 && !Double.isNaN(value)) { // Count current ordinal -- the TOS if (reusable == null) { - reusable = new FacetResultNode(tosOrdinal, value); + reusable = new FacetResultNode(tosOrdinal, value, -1); } else { // it is safe to cast since reusable was created here. reusable.ordinal = tosOrdinal; @@ -256,7 +256,6 @@ * Create a Facet Result. * @param facetRequest Request for which this result was obtained. * @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); Index: lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesAccumulator.java =================================================================== --- lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesAccumulator.java (revision 1465372) +++ lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesAccumulator.java (working copy) @@ -221,7 +221,7 @@ //System.out.println(" ord=" + ord + " count= "+ counts[ord] + " bottomCount=" + bottomCount); if (counts[ord] != 0) { dimCount += counts[ord]; - FacetResultNode node = new FacetResultNode(ord, counts[ord]); + FacetResultNode node = new FacetResultNode(ord, counts[ord], -1); dv.lookupOrd(ord, scratch); node.label = new CategoryPath(scratch.utf8ToString().split(state.separatorRegex, 2)); nodes.add(node); @@ -246,7 +246,7 @@ dimCount = 0; } - FacetResultNode rootNode = new FacetResultNode(-1, dimCount); + FacetResultNode rootNode = new FacetResultNode(-1, dimCount, nodes.size()); rootNode.label = new CategoryPath(new String[] {dim}); rootNode.subResults = nodes; results.add(new FacetResult(request, rootNode, nodes.size())); @@ -259,23 +259,27 @@ //System.out.println("collect"); int dimCount = 0; + int childCount = 0; FacetResultNode reuse = null; for(int ord=ordRange.start; ord<=ordRange.end; ord++) { //System.out.println(" ord=" + ord + " count= "+ counts[ord] + " bottomCount=" + bottomCount); - 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]; + if (counts[ord] > 0) { + childCount++; + if (counts[ord] > bottomCount) { + dimCount += counts[ord]; + //System.out.println(" keep"); + if (reuse == null) { + reuse = new FacetResultNode(ord, counts[ord], -1); + } 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); - } } } @@ -284,7 +288,7 @@ dimCount = 0; } - FacetResultNode rootNode = new FacetResultNode(-1, dimCount); + FacetResultNode rootNode = new FacetResultNode(-1, dimCount, childCount); rootNode.label = new CategoryPath(new String[] {dim}); FacetResultNode[] childNodes = new FacetResultNode[q.size()]; Index: lucene/facet/src/test/org/apache/lucene/facet/search/TestDrillSideways.java =================================================================== --- lucene/facet/src/test/org/apache/lucene/facet/search/TestDrillSideways.java (revision 1465372) +++ 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).getFacetResultNode().numChildren); + // 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).getFacetResultNode().numChildren); // 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