Skip to content

Commit aee4d20

Browse files
committed
HSEARCH-4070 Test that the configured totalHitCountThreshold is taken into account for scrolls
Signed-off-by: Yoann Rodière <yoann@hibernate.org>
1 parent 75e2dd6 commit aee4d20

File tree

4 files changed

+119
-4
lines changed

4 files changed

+119
-4
lines changed

integrationtest/backend/elasticsearch/src/test/java/org/hibernate/search/integrationtest/backend/elasticsearch/testsupport/util/ElasticsearchTckBackendFeatures.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,17 @@ public boolean supportsValuesForDynamicField(Class<?> javaType) {
106106
}
107107

108108
@Override
109-
public boolean supportsTotalHitsThreshold() {
109+
public boolean supportsTotalHitsThresholdForSearch() {
110110
return dialect.supportsSkipOrLimitingTotalHitCount();
111111
}
112112

113+
@Override
114+
public boolean supportsTotalHitsThresholdForScroll() {
115+
// If we try to customize track_total_hits for a scroll, we get an error:
116+
// "disabling [track_total_hits] is not allowed in a scroll context"
117+
return false;
118+
}
119+
113120
@Override
114121
public boolean supportsTruncateAfterForScroll() {
115122
// See https://hibernate.atlassian.net/browse/HSEARCH-4029

integrationtest/backend/tck/src/main/java/org/hibernate/search/integrationtest/backend/tck/search/query/SearchQueryBaseIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ public void totalHitCount_deprecated() {
116116
public void resultTotal_totalHitCountThreshold() {
117117
assumeTrue(
118118
"This backend doesn't take totalHitsThreshold() into account.",
119-
TckConfiguration.get().getBackendFeatures().supportsTotalHitsThreshold()
119+
TckConfiguration.get().getBackendFeatures().supportsTotalHitsThresholdForSearch()
120120
);
121121

122122
initData( 5000 );

integrationtest/backend/tck/src/main/java/org/hibernate/search/integrationtest/backend/tck/search/query/SearchQueryScrollIT.java

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
package org.hibernate.search.integrationtest.backend.tck.search.query;
88

99
import static org.assertj.core.api.Assertions.assertThat;
10+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
1011
import static org.hibernate.search.util.impl.integrationtest.common.assertion.SearchHitsAssert.assertThatHits;
1112
import static org.hibernate.search.util.impl.integrationtest.mapper.stub.StubMapperUtils.documentProvider;
1213
import static org.junit.Assert.assertFalse;
1314
import static org.junit.Assert.assertNotNull;
15+
import static org.junit.Assume.assumeTrue;
1416

1517
import java.util.List;
1618
import java.util.Locale;
@@ -19,10 +21,13 @@
1921
import org.hibernate.search.engine.backend.document.IndexFieldReference;
2022
import org.hibernate.search.engine.backend.document.model.dsl.IndexSchemaElement;
2123
import org.hibernate.search.engine.backend.types.Sortable;
24+
import org.hibernate.search.engine.search.query.SearchResultTotal;
2225
import org.hibernate.search.engine.search.query.SearchScroll;
2326
import org.hibernate.search.engine.search.query.SearchScrollResult;
2427
import org.hibernate.search.engine.search.query.dsl.SearchQueryOptionsStep;
28+
import org.hibernate.search.integrationtest.backend.tck.testsupport.util.TckConfiguration;
2529
import org.hibernate.search.integrationtest.backend.tck.testsupport.util.rule.SearchSetupHelper;
30+
import org.hibernate.search.util.common.SearchException;
2631
import org.hibernate.search.util.impl.integrationtest.mapper.stub.SimpleMappedIndex;
2732

2833
import org.junit.BeforeClass;
@@ -31,7 +36,7 @@
3136

3237
public class SearchQueryScrollIT {
3338

34-
private static final int DOCUMENT_COUNT = 200;
39+
private static final int DOCUMENT_COUNT = 2000;
3540
private static final int CHUNK_SIZE = 30;
3641
private static final int EXACT_DIVISOR_CHUNK_SIZE = 25;
3742

@@ -117,6 +122,100 @@ public void tookAndTimedOut() {
117122
}
118123
}
119124

125+
@Test
126+
public void resultTotal() {
127+
try ( SearchScroll<DocumentReference> scroll = matchAllSortedByScoreQuery()
128+
.scroll( CHUNK_SIZE ) ) {
129+
for ( SearchScrollResult<DocumentReference> chunk = scroll.next(); chunk.hasHits();
130+
chunk = scroll.next() ) {
131+
SearchResultTotal total = scroll.next().total();
132+
133+
assertThat( total.isHitCountExact() ).isTrue();
134+
assertThat( total.isHitCountLowerBound() ).isFalse();
135+
assertThat( total.hitCount() ).isEqualTo( DOCUMENT_COUNT );
136+
assertThat( total.hitCountLowerBound() ).isEqualTo( DOCUMENT_COUNT );
137+
}
138+
}
139+
}
140+
141+
@Test
142+
public void resultTotal_totalHitCountThreshold() {
143+
assumeTrue(
144+
"This backend doesn't take totalHitsThreshold() into account for scrolls.",
145+
TckConfiguration.get().getBackendFeatures().supportsTotalHitsThresholdForScroll()
146+
);
147+
148+
try ( SearchScroll<DocumentReference> scroll = matchAllSortedByScoreQuery()
149+
.totalHitCountThreshold( 100 )
150+
.scroll( CHUNK_SIZE ) ) {
151+
int chunkCountSoFar = 0;
152+
for ( SearchScrollResult<DocumentReference> chunk = scroll.next(); chunk.hasHits();
153+
chunk = scroll.next() ) {
154+
SearchResultTotal total = scroll.next().total();
155+
156+
++chunkCountSoFar;
157+
158+
// Even when approximate, the total hit count should be greater than or equal to
159+
// the number of hits processed so far.
160+
assertThat( total.hitCountLowerBound() ).isGreaterThanOrEqualTo( CHUNK_SIZE * chunkCountSoFar );
161+
162+
if ( chunkCountSoFar == 1 ) {
163+
// The first chunk definitely cannot have an exact count,
164+
// considering the high number of hits.
165+
assertThat( total.isHitCountExact() ).isFalse();
166+
assertThat( total.isHitCountLowerBound() ).isTrue();
167+
assertThat( total.hitCountLowerBound() ).isLessThanOrEqualTo( DOCUMENT_COUNT );
168+
169+
assertThatThrownBy( () -> total.hitCount() )
170+
.isInstanceOf( SearchException.class )
171+
.hasMessageContaining(
172+
"Trying to get the exact total hit count, but it is a lower bound." );
173+
}
174+
else {
175+
// The next chunks *may* have an exact count,
176+
// depending on the internal implementation (depending on how many hits are retrieved).
177+
178+
if ( total.isHitCountExact() ) {
179+
assertThat( total.isHitCountLowerBound() ).isFalse();
180+
assertThat( total.hitCount() ).isEqualTo( DOCUMENT_COUNT );
181+
assertThat( total.hitCountLowerBound() ).isEqualTo( DOCUMENT_COUNT );
182+
}
183+
else {
184+
assertThat( total.isHitCountLowerBound() ).isTrue();
185+
assertThat( total.hitCountLowerBound() ).isLessThanOrEqualTo( DOCUMENT_COUNT );
186+
187+
assertThatThrownBy( () -> total.hitCount() )
188+
.isInstanceOf( SearchException.class )
189+
.hasMessageContaining(
190+
"Trying to get the exact total hit count, but it is a lower bound." );
191+
}
192+
}
193+
}
194+
}
195+
}
196+
197+
@Test
198+
public void resultTotal_totalHitCountThreshold_veryHigh() {
199+
assumeTrue(
200+
"This backend doesn't take totalHitsThreshold() into account for scrolls.",
201+
TckConfiguration.get().getBackendFeatures().supportsTotalHitsThresholdForScroll()
202+
);
203+
204+
try ( SearchScroll<DocumentReference> scroll = matchAllSortedByScoreQuery()
205+
.totalHitCountThreshold( DOCUMENT_COUNT * 2 )
206+
.scroll( CHUNK_SIZE ) ) {
207+
for ( SearchScrollResult<DocumentReference> chunk = scroll.next(); chunk.hasHits();
208+
chunk = scroll.next() ) {
209+
SearchResultTotal total = scroll.next().total();
210+
211+
assertThat( total.isHitCountExact() ).isTrue();
212+
assertThat( total.isHitCountLowerBound() ).isFalse();
213+
assertThat( total.hitCount() ).isEqualTo( DOCUMENT_COUNT );
214+
assertThat( total.hitCountLowerBound() ).isEqualTo( DOCUMENT_COUNT );
215+
}
216+
}
217+
}
218+
120219
private void checkScrolling(SearchScroll<DocumentReference> scroll, int documentCount, int chunkSize) {
121220
int docIndex = 0;
122221
int quotient = documentCount / chunkSize;
@@ -162,6 +261,11 @@ private void checkScrolling(SearchScroll<DocumentReference> scroll, int document
162261
.sort( f -> f.field( "integer" ).asc() );
163262
}
164263

264+
private SearchQueryOptionsStep<?, DocumentReference, ?, ?, ?> matchAllSortedByScoreQuery() {
265+
return index.query()
266+
.where( f -> f.matchAll() );
267+
}
268+
165269
private SearchQueryOptionsStep<?, DocumentReference, ?, ?, ?> matchFirstHalfQuery() {
166270
return index.query()
167271
.where( f -> f.range().field( "integer" ).lessThan( DOCUMENT_COUNT / 2 ) )

integrationtest/backend/tck/src/main/java/org/hibernate/search/integrationtest/backend/tck/testsupport/util/TckBackendFeatures.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ public boolean projectionPreservesNulls() {
5959
return true;
6060
}
6161

62-
public boolean supportsTotalHitsThreshold() {
62+
public boolean supportsTotalHitsThresholdForSearch() {
63+
return true;
64+
}
65+
66+
public boolean supportsTotalHitsThresholdForScroll() {
6367
return true;
6468
}
6569

0 commit comments

Comments
 (0)