1010import org .elasticsearch .common .util .BigArrays ;
1111import org .elasticsearch .common .util .BinarySearcher ;
1212import org .elasticsearch .common .util .LongArray ;
13+ import org .elasticsearch .common .util .LongHash ;
1314import org .elasticsearch .core .Releasable ;
1415import org .elasticsearch .core .Releasables ;
1516import org .elasticsearch .search .sort .BucketedSort ;
@@ -27,15 +28,30 @@ public class LongTopNUniqueSort implements Releasable {
2728
2829 private final LongArray values ;
2930 private final LongBinarySearcher searcher ;
31+ /**
32+ * Hash holding the unique seen values.
33+ * Adding is an O(1) operation to check if the new value is already in the top, and avoid trying to add it again, which is O(log(n)).
34+ */
35+ private final LongHash seenUniqueValues ;
3036
3137 private int count ;
3238
3339 public LongTopNUniqueSort (BigArrays bigArrays , SortOrder order , int limit ) {
3440 this .order = order ;
3541 this .limit = limit ;
3642 this .count = 0 ;
37- this .values = bigArrays .newLongArray (limit , false );
38- this .searcher = new LongBinarySearcher (values , order );
43+
44+ boolean success = false ;
45+ try {
46+ this .values = bigArrays .newLongArray (limit , false );
47+ this .seenUniqueValues = new LongHash (1 , bigArrays );
48+ this .searcher = new LongBinarySearcher (values , order );
49+ success = true ;
50+ } finally {
51+ if (success == false ) {
52+ close ();
53+ }
54+ }
3955 }
4056
4157 public boolean collect (long value ) {
@@ -49,6 +65,10 @@ public boolean collect(long value) {
4965 return false ;
5066 }
5167
68+ if (seenUniqueValues .add (value ) < 0 ) {
69+ return true ;
70+ }
71+
5272 if (count == 0 ) {
5373 values .set (0 , value );
5474 count ++;
@@ -159,6 +179,6 @@ private boolean betterThan(long lhs, long rhs) {
159179
160180 @ Override
161181 public final void close () {
162- Releasables .close (values );
182+ Releasables .close (values , seenUniqueValues );
163183 }
164184}
0 commit comments