/*
* 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.test.query.facet;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Locale;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.search.FullTextQuery;
import org.hibernate.search.exception.SearchException;
import org.hibernate.search.query.engine.spi.FacetManager;
import org.hibernate.search.query.facet.Facet;
import org.hibernate.search.query.facet.FacetSortOrder;
import org.hibernate.search.query.facet.FacetingRequest;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author Hardy Ferentschik
*/
public class RangeFacetingTest extends AbstractFacetTest {
private static final String indexFieldName = "price";
private static final String priceRange = "priceRange";
@Test
public void testRangeQueryForInteger() {
FacetingRequest rangeRequest = queryBuilder( Cd.class ).facet()
.name( priceRange )
.onField( indexFieldName )
.range()
.from( 0 ).to( 1000 )
.from( 1001 ).to( 1500 )
.from( 1501 ).to( 3000 )
.from( 3001 ).to( 8000 )
.includeZeroCounts( true )
.createFacetingRequest();
FullTextQuery query = createMatchAllQuery( Cd.class );
FacetManager facetManager = query.getFacetManager();
facetManager.enableFaceting( rangeRequest );
List<Facet> facets = facetManager.getFacets( priceRange );
assertFacetCounts( facets, new int[] { 5, 3, 2, 0 } );
}
@Test
public void testRangeBelow() {
FacetingRequest rangeRequest = queryBuilder( Cd.class ).facet()
.name( priceRange )
.onField( indexFieldName )
.range()
.below( 1500 )
.createFacetingRequest();
FullTextQuery query = createMatchAllQuery( Cd.class );
FacetManager facetManager = query.getFacetManager();
facetManager.enableFaceting( rangeRequest );
List<Facet> facets = facetManager.getFacets( priceRange );
assertFacetCounts( facets, new int[] { 5 } );
}
@Test
public void testRangeBelowExcludeLimit() {
FacetingRequest rangeRequest = queryBuilder( Cd.class ).facet()
.name( priceRange )
.onField( indexFieldName )
.range()
.below( 1500 ).excludeLimit()
.createFacetingRequest();
FullTextQuery query = createMatchAllQuery( Cd.class );
FacetManager facetManager = query.getFacetManager();
facetManager.enableFaceting( rangeRequest );
List<Facet> facets = facetManager.getFacets( priceRange );
assertFacetCounts( facets, new int[] { 2 } );
}
@Test
public void testRangeAbove() {
FacetingRequest rangeRequest = queryBuilder( Cd.class ).facet()
.name( priceRange )
.onField( indexFieldName )
.range()
.above( 1500 )
.createFacetingRequest();
FullTextQuery query = createMatchAllQuery( Cd.class );
FacetManager facetManager = query.getFacetManager();
facetManager.enableFaceting( rangeRequest );
List<Facet> facets = facetManager.getFacets( priceRange );
assertFacetCounts( facets, new int[] { 8 } );
}
@Test
public void testRangeAboveExcludeLimit() {
FacetingRequest rangeRequest = queryBuilder( Cd.class ).facet()
.name( priceRange )
.onField( indexFieldName )
.range()
.above( 1500 ).excludeLimit()
.createFacetingRequest();
FullTextQuery query = createMatchAllQuery( Cd.class );
FacetManager facetManager = query.getFacetManager();
facetManager.enableFaceting( rangeRequest );
List<Facet> facets = facetManager.getFacets( priceRange );
assertFacetCounts( facets, new int[] { 5 } );
}
@Test
public void testRangeAboveBelow() {
FacetingRequest rangeRequest = queryBuilder( Cd.class ).facet()
.name( priceRange )
.onField( indexFieldName )
.range()
.below( 1500 )
.above( 1500 ).excludeLimit()
.createFacetingRequest();
FullTextQuery query = createMatchAllQuery( Cd.class );
FacetManager facetManager = query.getFacetManager();
facetManager.enableFaceting( rangeRequest );
List<Facet> facets = facetManager.getFacets( priceRange );
assertFacetCounts( facets, new int[] { 5, 5 } );
}
@Test
public void testRangeBelowMiddleAbove() {
final String facetingName = "cdPriceFaceting";
FacetingRequest rangeRequest = queryBuilder( Cd.class ).facet()
.name( facetingName )
.onField( indexFieldName )
.range()
.below( 1000 )
.from( 1001 ).to( 1500 )
.above( 1501 )
.createFacetingRequest();
FullTextQuery query = createMatchAllQuery( Cd.class );
query.getFacetManager().enableFaceting( rangeRequest );
List<Facet> facets = query.getFacetManager().getFacets( facetingName );
assertFacetCounts( facets, new int[] { 5, 3, 2 } );
}
@Test
public void testRangeWithExcludeLimitsAtEachLevel() {
final String facetingName = "cdPriceFaceting";
FacetingRequest rangeRequest = queryBuilder( Cd.class ).facet()
.name( facetingName )
.onField( indexFieldName )
.range()
.below( 1000 ).excludeLimit()
.from( 1000 ).to( 1500 ).excludeLimit()
.from( 1500 ).to( 2000 ).excludeLimit()
.above( 2000 )
.orderedBy( FacetSortOrder.RANGE_DEFINITION_ORDER )
.includeZeroCounts( true )
.createFacetingRequest();
FullTextQuery query = createMatchAllQuery( Cd.class );
query.getFacetManager().enableFaceting( rangeRequest );
List<Facet> facets = query.getFacetManager().getFacets( facetingName );
assertFacetCounts( facets, new int[] { 2, 0, 6, 2 } );
rangeRequest = queryBuilder( Cd.class ).facet()
.name( facetingName )
.onField( indexFieldName )
.range()
.below( 1000 )
.from( 1000 ).excludeLimit().to( 1500 )
.from( 1500 ).excludeLimit().to( 2000 )
.above( 2000 ).excludeLimit()
.orderedBy( FacetSortOrder.RANGE_DEFINITION_ORDER )
.createFacetingRequest();
query = createMatchAllQuery( Cd.class );
query.getFacetManager().enableFaceting( rangeRequest );
facets = query.getFacetManager().getFacets( facetingName );
assertFacetCounts( facets, new int[] { 2, 3, 4, 1 } );
}
@Test
@TestForIssue(jiraKey = "HSEARCH-770")
public void testRangeBelowWithFacetSelection() {
final String facetingName = "truckHorsePowerFaceting";
FacetingRequest rangeRequest = queryBuilder( Truck.class ).facet()
.name( facetingName )
.onField( "horsePower" )
.range()
.below( 1000 )
.createFacetingRequest();
FullTextQuery query = createMatchAllQuery( Truck.class );
FacetManager facetManager = query.getFacetManager();
query.getFacetManager().enableFaceting( rangeRequest );
List<Facet> facets = facetManager.getFacets( facetingName ); // OK
facets = facetManager.getFacets( facetingName ); // Still OK
assertFacetCounts( facets, new int[] { 4 } );
facetManager.getFacetGroup( facetingName ).selectFacets( facets.get( 0 ) ); // narrow search on facet
facets = facetManager.getFacets( facetingName ); // Exception...
assertFacetCounts( facets, new int[] { 4 } );
}
@Test
public void testRangeQueryForDoubleWithZeroCount() {
FacetingRequest rangeRequest = queryBuilder( Fruit.class ).facet()
.name( priceRange )
.onField( indexFieldName )
.range()
.from( 0.00 ).to( 1.00 )
.from( 1.01 ).to( 1.50 )
.from( 1.51 ).to( 3.00 )
.from( 4.00 ).to( 5.00 )
.includeZeroCounts( true )
.createFacetingRequest();
FullTextQuery query = createMatchAllQuery( Fruit.class );
FacetManager facetManager = query.getFacetManager();
facetManager.enableFaceting( rangeRequest );
List<Facet> facets = facetManager.getFacets( priceRange );
assertFacetCounts( facets, new int[] { 5, 3, 2, 0 } );
}
@Test
public void testRangeQueryForDoubleWithoutZeroCount() {
FacetingRequest rangeRequest = queryBuilder( Fruit.class ).facet()
.name( priceRange )
.onField( indexFieldName )
.range()
.from( 0.00 ).to( 1.00 )
.from( 1.01 ).to( 1.50 )
.from( 1.51 ).to( 3.00 )
.from( 4.00 ).to( 5.00 )
.includeZeroCounts( false )
.orderedBy( FacetSortOrder.COUNT_ASC )
.createFacetingRequest();
FullTextQuery query = createMatchAllQuery( Fruit.class );
FacetManager facetManager = query.getFacetManager();
facetManager.enableFaceting( rangeRequest );
List<Facet> facets = query.getFacetManager().getFacets( priceRange );
assertFacetCounts( facets, new int[] { 2, 3, 5 } );
assertEquals( "[0.0, 1.0]", facets.get( 0 ).getValue() );
assertEquals( "[1.01, 1.5]", facets.get( 1 ).getValue() );
assertEquals( "[1.51, 3.0]", facets.get( 2 ).getValue() );
}
@Test
public void testRangeQueryRangeDefOrderHigherMaxCount() {
FacetingRequest rangeRequest = queryBuilder( Fruit.class ).facet()
.name( priceRange )
.onField( indexFieldName )
.range()
.from( 0.00 ).to( 1.00 )
.from( 1.01 ).to( 1.50 )
.from( 1.51 ).to( 3.00 )
.from( 4.00 ).to( 5.00 )
.includeZeroCounts( false )
.orderedBy( FacetSortOrder.RANGE_DEFINITION_ORDER )
.maxFacetCount( 5 )
.createFacetingRequest();
FullTextQuery query = createMatchAllQuery( Fruit.class );
FacetManager facetManager = query.getFacetManager();
facetManager.enableFaceting( rangeRequest );
List<Facet> facets = query.getFacetManager().getFacets( priceRange );
assertFacetCounts( facets, new int[] { 2, 3, 5 } );
assertEquals( "[0.0, 1.0]", facets.get( 0 ).getValue() );
assertEquals( "[1.01, 1.5]", facets.get( 1 ).getValue() );
assertEquals( "[1.51, 3.0]", facets.get( 2 ).getValue() );
}
@Test
public void testDateRangeFaceting() throws Exception {
final String facetingName = "albumYearFaceting";
final String fieldName = "releaseYear";
final DateFormat formatter = new SimpleDateFormat( "yyyy", Locale.ROOT );
FacetingRequest rangeRequest = queryBuilder( Cd.class ).facet()
.name( facetingName )
.onField( fieldName )
.range()
.below( formatter.parse( "1970" ) ).excludeLimit()
.from( formatter.parse( "1970" ) ).to( formatter.parse( "1979" ) )
.from( formatter.parse( "1980" ) ).to( formatter.parse( "1989" ) )
.from( formatter.parse( "1990" ) ).to( formatter.parse( "1999" ) )
.above( formatter.parse( "2000" ) ).excludeLimit()
.orderedBy( FacetSortOrder.RANGE_DEFINITION_ORDER )
.createFacetingRequest();
FullTextQuery query = createMatchAllQuery( Cd.class );
FacetManager facetManager = query.getFacetManager();
facetManager.enableFaceting( rangeRequest );
List<Facet> facets = facetManager.getFacets( facetingName );
assertFacetCounts( facets, new int[] { 1, 2, 2, 5 } );
facetManager.getFacetGroup( facetingName ).selectFacets( facets.get( 3 ) );
facets = facetManager.getFacets( facetingName );
assertFacetCounts( facets, new int[] { 5 } );
}
@Test
public void testRangeQueryWithUnsupportedType() {
try {
queryBuilder( Cd.class ).facet()
.name( priceRange )
.onField( indexFieldName )
.range()
.from( new Object() ).to( new Object() )
.createFacetingRequest();
fail( "Unsupported range faceting type" );
}
catch (SearchException e) {
assertTrue( "Unexpected error message: " + e.getMessage(), e.getMessage().startsWith( "HSEARCH000269" ) );
}
}
@Test
public void testRangeQueryWithNullToAndFrom() {
try {
queryBuilder( Cd.class ).facet()
.name( priceRange )
.onField( indexFieldName )
.range()
.from( null ).to( null )
.createFacetingRequest();
fail( "Unsupported range faceting type" );
}
catch (SearchException e) {
assertTrue( "Unexpected error message: " + e.getMessage(), e.getMessage().startsWith( "HSEARCH000270" ) );
}
}
@Test
public void testUnsupportedRangeParameterTypeThrowsException() {
FacetingRequest rangeRequest = queryBuilder( Fruit.class ).facet()
.name( priceRange )
.onField( indexFieldName )
.range()
.from( "0.00" ).to( "1.00" )
.createFacetingRequest();
FullTextQuery query = createMatchAllQuery( Fruit.class );
FacetManager facetManager = query.getFacetManager();
facetManager.enableFaceting( rangeRequest );
try {
query.getFacetManager().getFacets( priceRange );
fail( "Trying to so a range query using string as parameter should fail." );
}
catch (SearchException e) {
assertTrue( "Unexpected error message: " + e.getMessage(), e.getMessage().startsWith( "HSEARCH000266" ) );
}
}
@Override
public void loadTestData(Session session) {
Transaction tx = session.beginTransaction();
for ( int i = 0; i < albums.length; i++ ) {
Cd cd = new Cd( albums[i], albumPrices[i], releaseDates[i] );
session.save( cd );
}
for ( int i = 0; i < fruits.length; i++ ) {
Fruit fruit = new Fruit( fruits[i], fruitPrices[i] );
session.save( fruit );
}
for ( Integer horsePower : horsePowers ) {
Truck truck = new Truck( horsePower );
session.save( truck );
}
tx.commit();
session.clear();
}
@Override
public Class<?>[] getAnnotatedClasses() {
return new Class[] {
Cd.class,
Fruit.class,
Truck.class
};
}
}