/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.elasticsearch.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.util.List;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.hibernate.search.annotations.DocumentId;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.SortableField;
import org.hibernate.search.backend.spi.Work;
import org.hibernate.search.backend.spi.WorkType;
import org.hibernate.search.engine.integration.impl.ExtendedSearchIntegrator;
import org.hibernate.search.exception.SearchException;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hibernate.search.query.engine.spi.DocumentExtractor;
import org.hibernate.search.query.engine.spi.EntityInfo;
import org.hibernate.search.query.engine.spi.HSQuery;
import org.hibernate.search.testsupport.TestForIssue;
import org.hibernate.search.testsupport.junit.SearchFactoryHolder;
import org.hibernate.search.testsupport.setup.TransactionContextForTest;
import org.junit.Rule;
import org.junit.Test;
/**
* @author Yoann Rodiere
*/
@TestForIssue( jiraKey = "HSEARCH-2189" )
public class ElasticsearchScrollingIT {
/**
* The default value for the index.max_result_window setting in Elasticsearch.
* @see https://www.elastic.co/guide/en/elasticsearch/reference/2.4/breaking_21_search_changes.html#_from_size_limits
*/
private static final int DEFAULT_MAX_RESULT_WINDOW = 10000;
@Rule
public SearchFactoryHolder sfHolder = new SearchFactoryHolder( IndexedObject.class );
@Test
public void searchBeforeMaxResultWindow() throws Exception {
generateData( 0, DEFAULT_MAX_RESULT_WINDOW + 10 );
Query query = builder().all().createQuery();
List<EntityInfo> results = getQuery( query )
.firstResult( DEFAULT_MAX_RESULT_WINDOW - 5 ).maxResults( 5 )
.queryEntityInfos();
assertEquals( 5, results.size() );
assertEquals( DEFAULT_MAX_RESULT_WINDOW - 5, results.get( 0 ).getId() );
}
@Test(expected = SearchException.class)
public void searchBeyondMaxResultWindow() throws Exception {
generateData( 0, DEFAULT_MAX_RESULT_WINDOW + 10 );
Query query = builder().all().createQuery();
getQuery( query )
.firstResult( DEFAULT_MAX_RESULT_WINDOW + 1 ).maxResults( 5 )
.queryEntityInfos();
}
@Test
public void scrollBeyondMaxResultWindow() throws Exception {
generateData( 0, DEFAULT_MAX_RESULT_WINDOW + 10 );
Query query = builder().all().createQuery();
try ( DocumentExtractor extractor = getQuery( query )
.queryDocumentExtractor() ) {
for ( int i = 0; i < DEFAULT_MAX_RESULT_WINDOW + 10; ++i ) {
EntityInfo info = extractor.extract(i);
assertNotNull( info );
assertEquals( i, info.getId() );
}
}
}
@Test
public void scrollForwardToArbitraryPosition() throws Exception {
generateData( 0, 1000 );
Query query = builder().all().createQuery();
try ( DocumentExtractor extractor = getQuery( query )
.queryDocumentExtractor() ) {
EntityInfo info = extractor.extract( 1 );
assertNotNull( info );
assertEquals( 1, info.getId() );
info = extractor.extract( 500 );
assertNotNull( info );
assertEquals( 500, info.getId() );
}
}
@Test
public void scrollBackward() throws Exception {
generateData( 0, 1001 );
Query query = builder().all().createQuery();
try ( DocumentExtractor extractor = getQuery( query )
.queryDocumentExtractor() ) {
EntityInfo info = extractor.extract( 1000 );
assertNotNull( info );
assertEquals( 1000, info.getId() );
// Backtrack exactly 1000 positions
info = extractor.extract( 0 );
assertNotNull( info );
assertEquals( 0, info.getId() );
}
}
private QueryBuilder builder() {
return sfHolder.getSearchFactory().buildQueryBuilder().forEntity( IndexedObject.class ).get();
}
private HSQuery getQuery(Query luceneQuery) {
ExtendedSearchIntegrator sf = sfHolder.getSearchFactory();
HSQuery hsQuery = sf.createHSQuery( luceneQuery, IndexedObject.class );
return hsQuery
.projection( "id" )
.sort( new Sort( new SortField( "idSort", SortField.Type.INT ) ) );
}
private void generateData(int firstId, int count) throws Exception {
TransactionContextForTest tc = new TransactionContextForTest();
for ( int id = firstId; id < firstId + count; ++id ) {
Work work = new Work( new IndexedObject( id ), id, WorkType.ADD, false );
sfHolder.getSearchFactory().getWorker().performWork( work, tc );
}
tc.end();
}
@Indexed
public static class IndexedObject {
@DocumentId
@Field(name = "idSort")
@SortableField(forField = "idSort")
private Integer id;
public IndexedObject(Integer id) {
super();
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
}
}