You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by gs...@apache.org on 2022/07/11 16:04:51 UTC

[lucene] branch main updated: LUCENE-10614: Properly support getTopChildren in RangeFacetCounts (#974)

This is an automated email from the ASF dual-hosted git repository.

gsmiller pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/lucene.git


The following commit(s) were added to refs/heads/main by this push:
     new 5ef7e5025de LUCENE-10614: Properly support getTopChildren in RangeFacetCounts (#974)
5ef7e5025de is described below

commit 5ef7e5025def61cf20442806486c8f6102ebcdc4
Author: Yuting Gan <44...@users.noreply.github.com>
AuthorDate: Mon Jul 11 09:04:46 2022 -0700

    LUCENE-10614: Properly support getTopChildren in RangeFacetCounts (#974)
---
 lucene/CHANGES.txt                                 |   2 +
 .../lucene/demo/facet/DistanceFacetsExample.java   |   2 +-
 .../lucene/demo/facet/RangeFacetsExample.java      |   2 +-
 .../lucene/facet/range/RangeFacetCounts.java       |  52 ++++++--
 .../lucene/facet/range/TestRangeFacetCounts.java   | 136 +++++++++++++++++++--
 5 files changed, 170 insertions(+), 24 deletions(-)

diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index b86d3bf1c3c..e91231c0e53 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -109,6 +109,8 @@ Improvements
 
 * GITHUB#983: AbstractSortedSetDocValueFacetCounts internal code cleanup/refactoring. (Greg Miller)
 
+* LUCENE-10614: Properly support getTopChildren in RangeFacetCounts. (Yuting Gan)
+
 Optimizations
 ---------------------
 * LUCENE-8519: MultiDocValues.getNormValues should not call getMergedFieldInfos (Rushabh Shah)
diff --git a/lucene/demo/src/java/org/apache/lucene/demo/facet/DistanceFacetsExample.java b/lucene/demo/src/java/org/apache/lucene/demo/facet/DistanceFacetsExample.java
index 1d6754f0fe1..d01866b0f17 100644
--- a/lucene/demo/src/java/org/apache/lucene/demo/facet/DistanceFacetsExample.java
+++ b/lucene/demo/src/java/org/apache/lucene/demo/facet/DistanceFacetsExample.java
@@ -227,7 +227,7 @@ public class DistanceFacetsExample implements Closeable {
             FIVE_KM,
             TEN_KM);
 
-    return facets.getTopChildren(10, "field");
+    return facets.getAllChildren("field");
   }
 
   /** User drills down on the specified range. */
diff --git a/lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java b/lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java
index bc02bd8bccf..9c47b563aee 100644
--- a/lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java
+++ b/lucene/demo/src/java/org/apache/lucene/demo/facet/RangeFacetsExample.java
@@ -94,7 +94,7 @@ public class RangeFacetsExample implements Closeable {
     FacetsCollector.search(searcher, new MatchAllDocsQuery(), 10, fc);
 
     Facets facets = new LongRangeFacetCounts("timestamp", fc, PAST_HOUR, PAST_SIX_HOURS, PAST_DAY);
-    return facets.getTopChildren(10, "timestamp");
+    return facets.getAllChildren("timestamp");
   }
 
   /** User drills down on the specified range. */
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetCounts.java b/lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetCounts.java
index 1e93ad802b0..5e5fa143a97 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetCounts.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/range/RangeFacetCounts.java
@@ -36,6 +36,7 @@ import org.apache.lucene.search.Query;
 import org.apache.lucene.search.ScoreMode;
 import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.Weight;
+import org.apache.lucene.util.PriorityQueue;
 
 /**
  * Base class for range faceting.
@@ -232,20 +233,43 @@ abstract class RangeFacetCounts extends Facets {
     return new FacetResult(dim, path, totCount, labelValues, labelValues.length);
   }
 
-  // The current getTopChildren method is not returning "top" ranges. Instead, it returns all
-  // user-provided ranges in
-  // the order the user specified them when instantiating. This concept is being introduced and
-  // supported in the
-  // getAllChildren functionality in LUCENE-10550. getTopChildren is temporarily calling
-  // getAllChildren to maintain its
-  // current behavior, and the current implementation will be replaced by an actual "top children"
-  // implementation
-  // in LUCENE-10614
-  // TODO: fix getTopChildren in LUCENE-10614
   @Override
   public FacetResult getTopChildren(int topN, String dim, String... path) throws IOException {
     validateTopN(topN);
-    return getAllChildren(dim, path);
+    validateDimAndPathForGetChildren(dim, path);
+
+    PriorityQueue<Entry> pq =
+        new PriorityQueue<>(Math.min(topN, counts.length)) {
+          @Override
+          protected boolean lessThan(Entry a, Entry b) {
+            int cmp = Integer.compare(a.count, b.count);
+            if (cmp == 0) {
+              cmp = b.label.compareTo(a.label);
+            }
+            return cmp < 0;
+          }
+        };
+
+    int childCount = 0;
+    Entry e = null;
+    for (int i = 0; i < counts.length; i++) {
+      if (counts[i] != 0) {
+        childCount++;
+        if (e == null) {
+          e = new Entry();
+        }
+        e.label = ranges[i].label;
+        e.count = counts[i];
+        e = pq.insertWithOverflow(e);
+      }
+    }
+
+    LabelAndValue[] results = new LabelAndValue[pq.size()];
+    while (pq.size() != 0) {
+      Entry entry = pq.pop();
+      results[pq.size()] = new LabelAndValue(entry.label, entry.count);
+    }
+    return new FacetResult(dim, path, totCount, results, childCount);
   }
 
   @Override
@@ -285,4 +309,10 @@ abstract class RangeFacetCounts extends Facets {
       throw new IllegalArgumentException("path.length should be 0");
     }
   }
+
+  /** Reusable entry to hold range label and int count. */
+  private static final class Entry {
+    int count;
+    String label;
+  }
 }
diff --git a/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java b/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java
index 1ce7642cfd1..9e83cba98c5 100644
--- a/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java
+++ b/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java
@@ -18,6 +18,7 @@ package org.apache.lucene.facet.range;
 
 import com.carrotsearch.randomizedtesting.generators.RandomNumbers;
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -104,6 +105,11 @@ public class TestRangeFacetCounts extends FacetTestCase {
         "dim=field path=[] value=22 childCount=5\n  less than 10 (10)\n  less than or equal to 10 (11)\n  over 90 (9)\n  90 or above (10)\n  over 1000 (1)\n",
         result.toString());
 
+    result = facets.getTopChildren(4, "field");
+    assertEquals(
+        "dim=field path=[] value=22 childCount=5\n  less than or equal to 10 (11)\n  90 or above (10)\n  less than 10 (10)\n  over 90 (9)\n",
+        result.toString());
+
     // test getTopChildren(0, dim)
     expectThrows(
         IllegalArgumentException.class,
@@ -156,6 +162,18 @@ public class TestRangeFacetCounts extends FacetTestCase {
         "dim=field path=[] value=22 childCount=5\n  less than 10 (10)\n  less than or equal to 10 (11)\n  over 90 (9)\n  90 or above (10)\n  over 1000 (1)\n",
         result.toString());
 
+    result = facets.getTopChildren(4, "field");
+    assertEquals(
+        "dim=field path=[] value=22 childCount=5\n  less than or equal to 10 (11)\n  90 or above (10)\n  less than 10 (10)\n  over 90 (9)\n",
+        result.toString());
+
+    // test getTopChildren(0, dim)
+    expectThrows(
+        IllegalArgumentException.class,
+        () -> {
+          facets.getTopChildren(0, "field");
+        });
+
     r.close();
     d.close();
   }
@@ -206,6 +224,11 @@ public class TestRangeFacetCounts extends FacetTestCase {
         "dim=field path=[] value=21 childCount=5\n  less than 10 (10)\n  less than or equal to 10 (11)\n  over 90 (9)\n  90 or above (10)\n  over 1000 (0)\n",
         result.toString());
 
+    result = facets.getTopChildren(4, "field");
+    assertEquals(
+        "dim=field path=[] value=21 childCount=4\n  less than or equal to 10 (11)\n  90 or above (10)\n  less than 10 (10)\n  over 90 (9)\n",
+        result.toString());
+
     r.close();
     d.close();
   }
@@ -244,23 +267,23 @@ public class TestRangeFacetCounts extends FacetTestCase {
     List<FacetResult> result = facets.getAllDims(10);
     assertEquals(1, result.size());
     assertEquals(
-        "dim=field path=[] value=22 childCount=5\n  less than 10 (10)\n  less than or equal to 10 (11)\n  over 90 (9)\n  90 or above (10)\n  over 1000 (1)\n",
+        "dim=field path=[] value=22 childCount=5\n  less than or equal to 10 (11)\n  90 or above (10)\n  less than 10 (10)\n  over 90 (9)\n  over 1000 (1)\n",
         result.get(0).toString());
 
     // test getAllDims(1)
-    List<FacetResult> test1Child = facets.getAllDims(1);
-    assertEquals(1, test1Child.size());
+    result = facets.getAllDims(1);
+    assertEquals(1, result.size());
     assertEquals(
-        "dim=field path=[] value=22 childCount=5\n  less than 10 (10)\n  less than or equal to 10 (11)\n  over 90 (9)\n  90 or above (10)\n  over 1000 (1)\n",
-        test1Child.get(0).toString());
+        "dim=field path=[] value=22 childCount=5\n  less than or equal to 10 (11)\n",
+        result.get(0).toString());
 
     // test default implementation of getTopDims
     List<FacetResult> topNDimsResult = facets.getTopDims(1, 1);
-    assertEquals(test1Child, topNDimsResult);
+    assertEquals(result, topNDimsResult);
 
     // test getTopDims(0, 1)
-    List<FacetResult> topDimsResults2 = facets.getTopDims(0, 1);
-    assertEquals(0, topDimsResults2.size());
+    topNDimsResult = facets.getTopDims(0, 1);
+    assertEquals(0, topNDimsResult.size());
 
     // test getAllDims(0)
     expectThrows(
@@ -332,6 +355,11 @@ public class TestRangeFacetCounts extends FacetTestCase {
         "dim=field path=[] value=3 childCount=6\n  min (1)\n  max (1)\n  all0 (3)\n  all1 (2)\n  all2 (2)\n  all3 (1)\n",
         result.toString());
 
+    result = facets.getTopChildren(5, "field");
+    assertEquals(
+        "dim=field path=[] value=3 childCount=6\n  all0 (3)\n  all1 (2)\n  all2 (2)\n  all3 (1)\n  max (1)\n",
+        result.toString());
+
     r.close();
     d.close();
   }
@@ -368,6 +396,11 @@ public class TestRangeFacetCounts extends FacetTestCase {
     assertEquals(
         "dim=field path=[] value=41 childCount=4\n  0-10 (11)\n  10-20 (11)\n  20-30 (11)\n  30-40 (11)\n",
         result.toString());
+
+    result = facets.getTopChildren(3, "field");
+    assertEquals(
+        "dim=field path=[] value=41 childCount=4\n  0-10 (11)\n  10-20 (11)\n  20-30 (11)\n",
+        result.toString());
     r.close();
     d.close();
   }
@@ -393,6 +426,8 @@ public class TestRangeFacetCounts extends FacetTestCase {
 
     FacetResult result = facets.getAllChildren("field");
     assertEquals("dim=field path=[] value=0 childCount=0\n", result.toString());
+    result = facets.getTopChildren(1, "field");
+    assertEquals("dim=field path=[] value=0 childCount=0\n", result.toString());
 
     r.close();
     d.close();
@@ -422,6 +457,8 @@ public class TestRangeFacetCounts extends FacetTestCase {
 
     FacetResult result = facets.getAllChildren("field");
     assertEquals("dim=field path=[] value=0 childCount=0\n", result.toString());
+    result = facets.getTopChildren(1, "field");
+    assertEquals("dim=field path=[] value=0 childCount=0\n", result.toString());
 
     r.close();
     d.close();
@@ -518,7 +555,7 @@ public class TestRangeFacetCounts extends FacetTestCase {
         "dim=dim path=[] value=100 childCount=2\n  b (75)\n  a (25)\n",
         dsr.facets.getTopChildren(10, "dim").toString());
     assertEquals(
-        "dim=field path=[] value=21 childCount=5\n  less than 10 (10)\n  less than or equal to 10 (11)\n  over 90 (9)\n  90 or above (10)\n  over 1000 (0)\n",
+        "dim=field path=[] value=21 childCount=4\n  less than or equal to 10 (11)\n  90 or above (10)\n  less than 10 (10)\n  over 90 (9)\n",
         dsr.facets.getTopChildren(10, "field").toString());
 
     // Second search, drill down on dim=b:
@@ -531,7 +568,7 @@ public class TestRangeFacetCounts extends FacetTestCase {
         "dim=dim path=[] value=100 childCount=2\n  b (75)\n  a (25)\n",
         dsr.facets.getTopChildren(10, "dim").toString());
     assertEquals(
-        "dim=field path=[] value=16 childCount=5\n  less than 10 (7)\n  less than or equal to 10 (8)\n  over 90 (7)\n  90 or above (8)\n  over 1000 (0)\n",
+        "dim=field path=[] value=16 childCount=4\n  90 or above (8)\n  less than or equal to 10 (8)\n  less than 10 (7)\n  over 90 (7)\n",
         dsr.facets.getTopChildren(10, "field").toString());
 
     // Third search, drill down on "less than or equal to 10":
@@ -544,7 +581,7 @@ public class TestRangeFacetCounts extends FacetTestCase {
         "dim=dim path=[] value=11 childCount=2\n  b (8)\n  a (3)\n",
         dsr.facets.getTopChildren(10, "dim").toString());
     assertEquals(
-        "dim=field path=[] value=21 childCount=5\n  less than 10 (10)\n  less than or equal to 10 (11)\n  over 90 (9)\n  90 or above (10)\n  over 1000 (0)\n",
+        "dim=field path=[] value=21 childCount=4\n  less than or equal to 10 (11)\n  90 or above (10)\n  less than 10 (10)\n  over 90 (9)\n",
         dsr.facets.getTopChildren(10, "field").toString());
     w.close();
     IOUtils.close(tw, tr, td, r, d);
@@ -578,6 +615,9 @@ public class TestRangeFacetCounts extends FacetTestCase {
     assertEquals(
         "dim=field path=[] value=21 childCount=5\n  less than 10 (10)\n  less than or equal to 10 (11)\n  over 90 (9)\n  90 or above (10)\n  over 1000 (0)\n",
         facets.getAllChildren("field").toString());
+    assertEquals(
+        "dim=field path=[] value=21 childCount=4\n  less than or equal to 10 (11)\n  90 or above (10)\n  less than 10 (10)\n  over 90 (9)\n",
+        facets.getTopChildren(4, "field").toString());
     w.close();
     IOUtils.close(r, d);
   }
@@ -611,9 +651,14 @@ public class TestRangeFacetCounts extends FacetTestCase {
             new DoubleRange("90 or above", 90.0, true, 100.0, false),
             new DoubleRange("over 1000", 1000.0, false, Double.POSITIVE_INFINITY, false));
 
+    facets.getTopChildren(4, "field");
     assertEquals(
         "dim=field path=[] value=21 childCount=5\n  less than 10 (10)\n  less than or equal to 10 (11)\n  over 90 (9)\n  90 or above (10)\n  over 1000 (0)\n",
         facets.getAllChildren("field").toString());
+    assertEquals(
+        "dim=field path=[] value=21 childCount=4\n  less than or equal to 10 (11)\n  90 or above (10)\n  less than 10 (10)\n  over 90 (9)\n",
+        facets.getTopChildren(4, "field").toString());
+    facets.getTopChildren(4, "field");
     w.close();
     IOUtils.close(r, d);
   }
@@ -663,6 +708,10 @@ public class TestRangeFacetCounts extends FacetTestCase {
     assertEquals(
         "dim=field path=[] value=21 childCount=5\n  less than 10 (10)\n  less than or equal to 10 (11)\n  over 90 (9)\n  90 or above (10)\n  over 1000 (0)\n",
         result.toString());
+    result = facets.getTopChildren(4, "field");
+    assertEquals(
+        "dim=field path=[] value=21 childCount=4\n  less than or equal to 10 (11)\n  90 or above (10)\n  less than 10 (10)\n  over 90 (9)\n",
+        result.toString());
 
     r.close();
     d.close();
@@ -702,6 +751,7 @@ public class TestRangeFacetCounts extends FacetTestCase {
       int numRange = TestUtil.nextInt(random(), 1, 100);
       LongRange[] ranges = new LongRange[numRange];
       int[] expectedCounts = new int[numRange];
+      int[] expectedTopNChildrenCounts = new int[numRange];
       long minAcceptedValue = Long.MAX_VALUE;
       long maxAcceptedValue = Long.MIN_VALUE;
       for (int rangeID = 0; rangeID < numRange; rangeID++) {
@@ -769,6 +819,7 @@ public class TestRangeFacetCounts extends FacetTestCase {
           }
           if (accept) {
             expectedCounts[rangeID]++;
+            expectedTopNChildrenCounts[rangeID]++;
             minAcceptedValue = Math.min(minAcceptedValue, values[i]);
             maxAcceptedValue = Math.max(maxAcceptedValue, values[i]);
           }
@@ -802,6 +853,9 @@ public class TestRangeFacetCounts extends FacetTestCase {
         }
         FacetResult result = facets.getAllChildren("field");
         assertEquals(numRange, result.labelValues.length);
+        FacetResult topNResult = facets.getTopChildren(numRange, "field");
+        Arrays.sort(expectedTopNChildrenCounts);
+
         for (int rangeID = 0; rangeID < numRange; rangeID++) {
           if (VERBOSE) {
             System.out.println("  range " + rangeID + " expectedCount=" + expectedCounts[rangeID]);
@@ -809,6 +863,12 @@ public class TestRangeFacetCounts extends FacetTestCase {
           LabelAndValue subNode = result.labelValues[rangeID];
           assertEquals("r" + rangeID, subNode.label);
           assertEquals(expectedCounts[rangeID], subNode.value.intValue());
+          // test topNChildren and assert topNResults are sorted by count
+          if (rangeID < topNResult.labelValues.length) {
+            LabelAndValue topNsubNode = topNResult.labelValues[rangeID];
+            assertEquals(
+                expectedTopNChildrenCounts[numRange - rangeID - 1], topNsubNode.value.intValue());
+          }
 
           LongRange range = ranges[rangeID];
 
@@ -828,6 +888,9 @@ public class TestRangeFacetCounts extends FacetTestCase {
         Facets facets = new LongRangeFacetCounts("field", vs, sfc, fastMatchQuery, ranges);
         FacetResult result = facets.getAllChildren("field");
         assertEquals(numRange, result.labelValues.length);
+        FacetResult topNResult = facets.getTopChildren(numRange, "field");
+        Arrays.sort(expectedTopNChildrenCounts);
+
         for (int rangeID = 0; rangeID < numRange; rangeID++) {
           if (VERBOSE) {
             System.out.println("  range " + rangeID + " expectedCount=" + expectedCounts[rangeID]);
@@ -836,6 +899,13 @@ public class TestRangeFacetCounts extends FacetTestCase {
           assertEquals("r" + rangeID, subNode.label);
           assertEquals(expectedCounts[rangeID], subNode.value.intValue());
 
+          // test topNChildren and assert topNResults are sorted by count
+          if (rangeID < topNResult.labelValues.length) {
+            LabelAndValue topNsubNode = topNResult.labelValues[rangeID];
+            assertEquals(
+                expectedTopNChildrenCounts[numRange - rangeID - 1], topNsubNode.value.intValue());
+          }
+
           LongRange range = ranges[rangeID];
 
           // Test drill-down:
@@ -895,6 +965,7 @@ public class TestRangeFacetCounts extends FacetTestCase {
       int numRange = TestUtil.nextInt(random(), 1, 100);
       LongRange[] ranges = new LongRange[numRange];
       int[] expectedCounts = new int[numRange];
+      int[] expectedTopNChildrenCounts = new int[numRange];
       long minAcceptedValue = Long.MAX_VALUE;
       long maxAcceptedValue = Long.MIN_VALUE;
       for (int rangeID = 0; rangeID < numRange; rangeID++) {
@@ -963,6 +1034,7 @@ public class TestRangeFacetCounts extends FacetTestCase {
             }
             if (accept) {
               expectedCounts[rangeID]++;
+              expectedTopNChildrenCounts[rangeID]++;
               minAcceptedValue = Math.min(minAcceptedValue, values[i][j]);
               maxAcceptedValue = Math.max(maxAcceptedValue, values[i][j]);
               break; // ensure each doc can contribute at most 1 count to each range
@@ -994,6 +1066,9 @@ public class TestRangeFacetCounts extends FacetTestCase {
       }
       FacetResult result = facets.getAllChildren("field");
       assertEquals(numRange, result.labelValues.length);
+      FacetResult topNResult = facets.getTopChildren(numRange, "field");
+      Arrays.sort(expectedTopNChildrenCounts);
+
       for (int rangeID = 0; rangeID < numRange; rangeID++) {
         if (VERBOSE) {
           System.out.println("  range " + rangeID + " expectedCount=" + expectedCounts[rangeID]);
@@ -1001,6 +1076,12 @@ public class TestRangeFacetCounts extends FacetTestCase {
         LabelAndValue subNode = result.labelValues[rangeID];
         assertEquals("r" + rangeID, subNode.label);
         assertEquals(expectedCounts[rangeID], subNode.value.intValue());
+        // test topNChildren and assert topNResults are sorted by count
+        if (rangeID < topNResult.labelValues.length) {
+          LabelAndValue topNsubNode = topNResult.labelValues[rangeID];
+          assertEquals(
+              expectedTopNChildrenCounts[numRange - rangeID - 1], topNsubNode.value.intValue());
+        }
 
         LongRange range = ranges[rangeID];
 
@@ -1052,6 +1133,7 @@ public class TestRangeFacetCounts extends FacetTestCase {
       int numRange = TestUtil.nextInt(random(), 1, 5);
       DoubleRange[] ranges = new DoubleRange[numRange];
       int[] expectedCounts = new int[numRange];
+      int[] expectedTopNChildrenCounts = new int[numRange];
       double minAcceptedValue = Double.POSITIVE_INFINITY;
       double maxAcceptedValue = Double.NEGATIVE_INFINITY;
       for (int rangeID = 0; rangeID < numRange; rangeID++) {
@@ -1117,6 +1199,7 @@ public class TestRangeFacetCounts extends FacetTestCase {
           }
           if (accept) {
             expectedCounts[rangeID]++;
+            expectedTopNChildrenCounts[rangeID]++;
             minAcceptedValue = Math.min(minAcceptedValue, values[i]);
             maxAcceptedValue = Math.max(maxAcceptedValue, values[i]);
           }
@@ -1152,6 +1235,9 @@ public class TestRangeFacetCounts extends FacetTestCase {
       }
       FacetResult result = facets.getAllChildren("field");
       assertEquals(numRange, result.labelValues.length);
+      FacetResult topNResult = facets.getTopChildren(numRange, "field");
+      Arrays.sort(expectedTopNChildrenCounts);
+
       for (int rangeID = 0; rangeID < numRange; rangeID++) {
         if (VERBOSE) {
           System.out.println("  range " + rangeID + " expectedCount=" + expectedCounts[rangeID]);
@@ -1159,6 +1245,12 @@ public class TestRangeFacetCounts extends FacetTestCase {
         LabelAndValue subNode = result.labelValues[rangeID];
         assertEquals("r" + rangeID, subNode.label);
         assertEquals(expectedCounts[rangeID], subNode.value.intValue());
+        // test topNChildren and assert topNResults are sorted by count
+        if (rangeID < topNResult.labelValues.length) {
+          LabelAndValue topNsubNode = topNResult.labelValues[rangeID];
+          assertEquals(
+              expectedTopNChildrenCounts[numRange - rangeID - 1], topNsubNode.value.intValue());
+        }
 
         DoubleRange range = ranges[rangeID];
 
@@ -1218,6 +1310,7 @@ public class TestRangeFacetCounts extends FacetTestCase {
       int numRange = TestUtil.nextInt(random(), 1, 5);
       DoubleRange[] ranges = new DoubleRange[numRange];
       int[] expectedCounts = new int[numRange];
+      int[] expectedTopNChildrenCounts = new int[numRange];
       double minAcceptedValue = Double.POSITIVE_INFINITY;
       double maxAcceptedValue = Double.NEGATIVE_INFINITY;
       for (int rangeID = 0; rangeID < numRange; rangeID++) {
@@ -1284,6 +1377,7 @@ public class TestRangeFacetCounts extends FacetTestCase {
             }
             if (accept) {
               expectedCounts[rangeID]++;
+              expectedTopNChildrenCounts[rangeID]++;
               minAcceptedValue = Math.min(minAcceptedValue, values[i][j]);
               maxAcceptedValue = Math.max(maxAcceptedValue, values[i][j]);
               break; // ensure each doc can contribute at most 1 count to each range
@@ -1319,6 +1413,8 @@ public class TestRangeFacetCounts extends FacetTestCase {
       }
       FacetResult result = facets.getAllChildren("field");
       assertEquals(numRange, result.labelValues.length);
+      FacetResult topNResult = facets.getTopChildren(numRange, "field");
+      Arrays.sort(expectedTopNChildrenCounts);
       for (int rangeID = 0; rangeID < numRange; rangeID++) {
         if (VERBOSE) {
           System.out.println("  range " + rangeID + " expectedCount=" + expectedCounts[rangeID]);
@@ -1326,6 +1422,12 @@ public class TestRangeFacetCounts extends FacetTestCase {
         LabelAndValue subNode = result.labelValues[rangeID];
         assertEquals("r" + rangeID, subNode.label);
         assertEquals(expectedCounts[rangeID], subNode.value.intValue());
+        // test topNChildren and assert topNResults are sorted by count
+        if (rangeID < topNResult.labelValues.length) {
+          LabelAndValue topNsubNode = topNResult.labelValues[rangeID];
+          assertEquals(
+              expectedTopNChildrenCounts[numRange - rangeID - 1], topNsubNode.value.intValue());
+        }
 
         DoubleRange range = ranges[rangeID];
 
@@ -1382,6 +1484,9 @@ public class TestRangeFacetCounts extends FacetTestCase {
     assertEquals(
         "dim=field path=[] value=16 childCount=5\n  less than 10 (8)\n  less than or equal to 10 (8)\n  over 90 (8)\n  90 or above (8)\n  over 1000 (0)\n",
         facets.getAllChildren("field").toString());
+    assertEquals(
+        "dim=field path=[] value=16 childCount=4\n  90 or above (8)\n  less than 10 (8)\n  less than or equal to 10 (8)\n  over 90 (8)\n",
+        facets.getTopChildren(4, "field").toString());
 
     w.close();
     IOUtils.close(r, d);
@@ -1424,6 +1529,9 @@ public class TestRangeFacetCounts extends FacetTestCase {
     assertEquals(
         "dim=field path=[] value=16 childCount=5\n  less than 10 (8)\n  less than or equal to 10 (8)\n  over 90 (8)\n  90 or above (8)\n  over 1000 (0)\n",
         facets.getAllChildren("field").toString());
+    assertEquals(
+        "dim=field path=[] value=16 childCount=4\n  90 or above (8)\n  less than 10 (8)\n  less than or equal to 10 (8)\n  over 90 (8)\n",
+        facets.getTopChildren(4, "field").toString());
 
     w.close();
     IOUtils.close(r, d);
@@ -1599,6 +1707,9 @@ public class TestRangeFacetCounts extends FacetTestCase {
     assertEquals(
         "dim=field path=[] value=3 childCount=6\n  < 1 (0)\n  < 2 (1)\n  < 5 (3)\n  < 10 (3)\n  < 20 (3)\n  < 50 (3)\n",
         facets.getAllChildren("field").toString());
+    assertEquals(
+        "dim=field path=[] value=3 childCount=5\n  < 10 (3)\n  < 20 (3)\n  < 5 (3)\n  < 50 (3)\n  < 2 (1)\n",
+        facets.getTopChildren(5, "field").toString());
     assertTrue(fastMatchFilter == null || filterWasUsed.get());
 
     DrillDownQuery ddq = new DrillDownQuery(config);
@@ -1643,6 +1754,9 @@ public class TestRangeFacetCounts extends FacetTestCase {
     assertEquals(
         "dim=field path=[] value=3 childCount=6\n  < 1 (0)\n  < 2 (1)\n  < 5 (3)\n  < 10 (3)\n  < 20 (3)\n  < 50 (3)\n",
         dsr.facets.getAllChildren("field").toString());
+    assertEquals(
+        "dim=field path=[] value=3 childCount=5\n  < 10 (3)\n  < 20 (3)\n  < 5 (3)\n  < 50 (3)\n  < 2 (1)\n",
+        dsr.facets.getTopChildren(5, "field").toString());
 
     writer.close();
     IOUtils.close(r, dir);