/*
* 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.bridge;
import java.net.URI;
import java.net.URL;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.UUID;
import org.apache.lucene.analysis.core.SimpleAnalyzer;
import org.apache.lucene.document.DateTools;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.hibernate.HibernateException;
import org.hibernate.Transaction;
import org.hibernate.dialect.PostgreSQL81Dialect;
import org.hibernate.search.cfg.Environment;
import org.hibernate.search.FullTextQuery;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.hibernate.search.annotations.Resolution;
import org.hibernate.search.bridge.BridgeException;
import org.hibernate.search.bridge.builtin.StringEncodingCalendarBridge;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hibernate.search.test.SearchTestBase;
import org.hibernate.search.testsupport.TestConstants;
import org.hibernate.testing.SkipForDialect;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author Emmanuel Bernard
*/
public class BridgeTest extends SearchTestBase {
@Test
public void testDefaultAndNullBridges() throws Exception {
Cloud cloud = new Cloud();
cloud.setMyDate( null );
cloud.setDouble1( null );
cloud.setDouble2( 2.1d );
cloud.setIntegerv1( null );
cloud.setIntegerv2( 2 );
cloud.setFloat1( null );
cloud.setFloat2( 2.1f );
cloud.setLong1( null );
cloud.setLong2( 2l );
cloud.setString( null );
cloud.setType( CloudType.DOG );
cloud.setChar1( null );
cloud.setChar2( 'P' );
cloud.setStorm( false );
cloud.setClazz( Cloud.class );
cloud.setUri( new URI( "http://www.hibernate.org" ) );
cloud.setUrl( new URL( "http://www.hibernate.org" ) );
cloud.setUuid( UUID.fromString( "f49c6ba8-8d7f-417a-a255-d594dddf729f" ) );
org.hibernate.Session s = openSession();
Transaction tx = s.beginTransaction();
s.persist( cloud );
s.flush();
tx.commit();
tx = s.beginTransaction();
FullTextSession session = Search.getFullTextSession( s );
QueryParser parser = new QueryParser( "id", TestConstants.standardAnalyzer );
Query query;
List result;
BooleanQuery booleanQuery = new BooleanQuery.Builder()
.add( NumericRangeQuery.newDoubleRange( "double2", 2.1, 2.1, true, true ), BooleanClause.Occur.MUST )
.add( NumericRangeQuery.newFloatRange( "float2", 2.1f, 2.1f, true, true ), BooleanClause.Occur.MUST )
.add( NumericRangeQuery.newIntRange( "integerv2", 2, 3, true, true ), BooleanClause.Occur.MUST )
.add( NumericRangeQuery.newLongRange( "long2", 2l, 3l, true, true ), BooleanClause.Occur.MUST )
.add( new TermQuery( new Term( "type", "dog" ) ), BooleanClause.Occur.MUST )
.add( new TermQuery( new Term( "storm", "false" ) ), BooleanClause.Occur.MUST )
.build();
result = session.createFullTextQuery( booleanQuery ).list();
assertEquals( "find primitives and do not fail on null", 1, result.size() );
booleanQuery = new BooleanQuery.Builder()
.add( NumericRangeQuery.newDoubleRange( "double1", 2.1, 2.1, true, true ), BooleanClause.Occur.MUST )
.add( NumericRangeQuery.newFloatRange( "float1", 2.1f, 2.1f, true, true ), BooleanClause.Occur.MUST )
.add( NumericRangeQuery.newIntRange( "integerv1", 2, 3, true, true ), BooleanClause.Occur.MUST )
.add( NumericRangeQuery.newLongRange( "long1", 2l, 3l, true, true ), BooleanClause.Occur.MUST )
.build();
result = session.createFullTextQuery( booleanQuery ).list();
assertEquals( "null elements should not be stored", 0, result.size() ); //the query is dumb because restrictive
query = parser.parse( "type:dog" );
result = session.createFullTextQuery( query ).setProjection( "type" ).list();
assertEquals( "Enum projection works", 1, result.size() ); //the query is dumb because restrictive
query = new TermQuery( new Term( "clazz", Cloud.class.getName() ) );
result = session.createFullTextQuery( query ).setProjection( "clazz" ).list();
assertEquals( "Clazz projection works", 1, result.size() );
assertEquals(
"Clazz projection works",
Cloud.class.getName(),
( (Class) ( (Object[]) result.get( 0 ) )[0] ).getName()
);
BooleanQuery bQuery = new BooleanQuery.Builder()
.add( new TermQuery( new Term( "uri", "http://www.hibernate.org" ) ), BooleanClause.Occur.MUST )
.add( new TermQuery( new Term( "url", "http://www.hibernate.org" ) ), BooleanClause.Occur.MUST )
.build();
result = session.createFullTextQuery( bQuery ).setProjection( "clazz" ).list();
assertEquals( "Clazz projection works", 1, result.size() );
bQuery = new BooleanQuery.Builder()
.add( new TermQuery( new Term( "uuid", "f49c6ba8-8d7f-417a-a255-d594dddf729f" ) ), BooleanClause.Occur.MUST )
.build();
result = session.createFullTextQuery( bQuery ).setProjection( "clazz" ).list();
assertEquals( "Clazz projection works", 1, result.size() );
query = parser.parse( "char1:[" + String.valueOf( Character.MIN_VALUE ) + " TO " + String.valueOf( Character.MAX_VALUE ) + "]" );
result = session.createFullTextQuery( query ).setProjection( "char1" ).list();
assertEquals( "Null elements should not be stored, CharacterBridge is not working", 0, result.size() );
query = parser.parse( "char2:P" );
result = session.createFullTextQuery( query ).setProjection( "char2" ).list();
assertEquals( "Wrong results number, CharacterBridge is not working", 1, result.size() );
assertEquals( "Wrong result, CharacterBridge is not working", 'P', ( (Object[]) result.get( 0 ) )[0] );
tx.commit();
s.close();
}
@Test
public void testCustomBridges() throws Exception {
Cloud cloud = new Cloud();
cloud.setCustomFieldBridge( "This is divided by 2" );
cloud.setCustomStringBridge( "This is div by 4" );
cloud.setChar2( 's' );
org.hibernate.Session s = openSession();
Transaction tx = s.beginTransaction();
s.persist( cloud );
s.flush();
tx.commit();
tx = s.beginTransaction();
FullTextSession session = Search.getFullTextSession( s );
QueryParser parser = new QueryParser( "id", TestConstants.simpleAnalyzer );
Query query;
List result;
query = parser.parse( "customFieldBridge:This AND customStringBridge:This" );
result = session.createFullTextQuery( query ).list();
assertEquals( "Properties not mapped", 1, result.size() );
query = parser.parse( "customFieldBridge:by AND customStringBridge:is" );
result = session.createFullTextQuery( query ).list();
assertEquals( "Custom types not taken into account", 0, result.size() );
tx.commit();
s.close();
}
@Test
@SkipForDialect(PostgreSQL81Dialect.class)//PosgreSQL doesn't allow storing null with these column types
public void testDateBridge() throws Exception {
Calendar c = Calendar.getInstance( TimeZone.getTimeZone( "Europe/Rome" ), Locale.ROOT ); //for the sake of tests
c.set( 2000, Calendar.DECEMBER, 15, 3, 43, 2 );
c.set( Calendar.MILLISECOND, 5 );
Date date = new Date( c.getTimeInMillis() );
Cloud cloud = new Cloud();
cloud.setMyDate( date ); //5 millisecond
cloud.setDateDay( date );
cloud.setDateHour( date );
cloud.setDateMillisecond( date );
cloud.setDateMinute( date );
cloud.setDateMonth( date );
cloud.setDateSecond( date );
cloud.setDateYear( date );
org.hibernate.Session s = openSession();
Transaction tx = s.beginTransaction();
s.persist( cloud );
s.flush();
tx.commit();
tx = s.beginTransaction();
FullTextSession session = Search.getFullTextSession( s );
BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder();
Date myDate = DateTools.round( date, DateTools.Resolution.MILLISECOND );
NumericRangeQuery numericRangeQuery = NumericRangeQuery.newLongRange(
"myDate", myDate.getTime(), myDate.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
Date dateDay = DateTools.round( date, DateTools.Resolution.DAY );
numericRangeQuery = NumericRangeQuery.newLongRange(
"dateDay", dateDay.getTime(), dateDay.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
Date dateMonth = DateTools.round( date, DateTools.Resolution.MONTH );
numericRangeQuery = NumericRangeQuery.newLongRange(
"dateMonth", dateMonth.getTime(), dateMonth.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
Date dateYear = DateTools.round( date, DateTools.Resolution.YEAR );
numericRangeQuery = NumericRangeQuery.newLongRange(
"dateYear", dateYear.getTime(), dateYear.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
Date dateHour = DateTools.round( date, DateTools.Resolution.HOUR );
numericRangeQuery = NumericRangeQuery.newLongRange(
"dateHour", dateHour.getTime(), dateHour.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
Date dateMinute = DateTools.round( date, DateTools.Resolution.MINUTE );
numericRangeQuery = NumericRangeQuery.newLongRange(
"dateMinute", dateMinute.getTime(), dateMinute.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
Date dateSecond = DateTools.round( date, DateTools.Resolution.SECOND );
numericRangeQuery = NumericRangeQuery.newLongRange(
"dateSecond", dateSecond.getTime(), dateSecond.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
Date dateMillisecond = DateTools.round( date, DateTools.Resolution.MILLISECOND );
numericRangeQuery = NumericRangeQuery.newLongRange(
"dateMillisecond", dateMillisecond.getTime(), dateMillisecond.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
BooleanQuery booleanQuery = booleanQueryBuilder.build();
List result = session.createFullTextQuery( booleanQuery ).list();
assertEquals( "Date not found or not property truncated", 1, result.size() );
tx.commit();
s.close();
}
@Test
public void testCalendarBridge() throws Exception {
Cloud cloud = new Cloud();
Calendar calendar = Calendar.getInstance( TimeZone.getTimeZone( "Europe/Rome" ), Locale.ROOT ); //for the sake of tests
calendar.set( 2000, 11, 15, 3, 43, 2 );
calendar.set( Calendar.MILLISECOND, 5 );
cloud.setMyCalendar( calendar ); // 5 millisecond
cloud.setCalendarDay( calendar );
cloud.setCalendarHour( calendar );
cloud.setCalendarMillisecond( calendar );
cloud.setCalendarMinute( calendar );
cloud.setCalendarMonth( calendar );
cloud.setCalendarSecond( calendar );
cloud.setCalendarYear( calendar );
cloud.setChar2( 's' );
org.hibernate.Session s = openSession();
Transaction tx = s.beginTransaction();
s.persist( cloud );
s.flush();
tx.commit();
tx = s.beginTransaction();
FullTextSession session = Search.getFullTextSession( s );
Date date = calendar.getTime();
BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder();
Date myDate = DateTools.round( date, DateTools.Resolution.MILLISECOND );
NumericRangeQuery numericRangeQuery = NumericRangeQuery.newLongRange(
"myCalendar", myDate.getTime(), myDate.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
Date dateDay = DateTools.round( date, DateTools.Resolution.DAY );
numericRangeQuery = NumericRangeQuery.newLongRange(
"calendarDay", dateDay.getTime(), dateDay.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
Date dateMonth = DateTools.round( date, DateTools.Resolution.MONTH );
numericRangeQuery = NumericRangeQuery.newLongRange(
"calendarMonth", dateMonth.getTime(), dateMonth.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
Date dateYear = DateTools.round( date, DateTools.Resolution.YEAR );
numericRangeQuery = NumericRangeQuery.newLongRange(
"calendarYear", dateYear.getTime(), dateYear.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
Date dateHour = DateTools.round( date, DateTools.Resolution.HOUR );
numericRangeQuery = NumericRangeQuery.newLongRange(
"calendarHour", dateHour.getTime(), dateHour.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
Date dateMinute = DateTools.round( date, DateTools.Resolution.MINUTE );
numericRangeQuery = NumericRangeQuery.newLongRange(
"calendarMinute", dateMinute.getTime(), dateMinute.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
Date dateSecond = DateTools.round( date, DateTools.Resolution.SECOND );
numericRangeQuery = NumericRangeQuery.newLongRange(
"calendarSecond", dateSecond.getTime(), dateSecond.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
Date dateMillisecond = DateTools.round( date, DateTools.Resolution.MILLISECOND );
numericRangeQuery = NumericRangeQuery.newLongRange(
"calendarMillisecond", dateMillisecond.getTime(), dateMillisecond.getTime(), true, true
);
booleanQueryBuilder.add( numericRangeQuery, BooleanClause.Occur.MUST );
BooleanQuery booleanQuery = booleanQueryBuilder.build();
List result = session.createFullTextQuery( booleanQuery ).list();
assertEquals( "Calendar not found or not property truncated", 1, result.size() );
tx.commit();
s.close();
//now unit-test the bridge directly:
StringEncodingCalendarBridge bridge = new StringEncodingCalendarBridge();
HashMap<String, String> bridgeParams = new HashMap<String, String>();
bridgeParams.put( "resolution", Resolution.YEAR.toString() );
bridge.setParameterValues( bridgeParams );
assertEquals( "2000", bridge.objectToString( calendar ) );
bridgeParams.put( "resolution", Resolution.DAY.toString() );
bridge.setParameterValues( bridgeParams );
assertEquals( "20001215", bridge.objectToString( calendar ) );
}
@Test
public void testIncorrectSetBridge() throws Exception {
IncorrectSet incorrect = new IncorrectSet();
incorrect.setSubIncorrect( new IncorrectSet.SubIncorrect() );
incorrect.getSubIncorrect().setName( "This is a name not a class" );
FullTextSession s = Search.getFullTextSession( openSession() );
Transaction tx = s.beginTransaction();
try {
s.persist( incorrect );
s.flush();
s.flushToIndexes();
fail( "Incorrect bridge should fail" );
}
catch (BridgeException e) {
tx.rollback();
}
catch (HibernateException e) {
final Throwable throwable = e.getCause();
if ( throwable instanceof BridgeException ) {
//expected
assertTrue( throwable.getMessage().contains( "class: " + IncorrectSet.class.getName() ) );
assertTrue( throwable.getMessage().contains( "path: subIncorrect.name" ) );
tx.rollback();
}
else {
e.printStackTrace();
fail( "Incorrect bridge should raise a SearchException: " + e.toString() );
}
}
catch (Exception e) {
e.printStackTrace();
fail( "Incorrect bridge should raise a SearchException" );
}
s.close();
}
@Test
public void testIncorrectGetBridge() throws Exception {
IncorrectGet incorrect = new IncorrectGet();
incorrect.setSubIncorrect( new IncorrectGet.SubIncorrect() );
incorrect.getSubIncorrect().setName( "This is a name not a class" );
FullTextSession s = Search.getFullTextSession( openSession() );
Transaction tx = s.beginTransaction();
s.persist( incorrect );
tx.commit();
s.clear();
tx = s.beginTransaction();
final QueryBuilder builder = s.getSearchFactory().buildQueryBuilder().forEntity( IncorrectGet.class ).get();
final Query query = builder.keyword().onField( "subIncorrect.name" ).matching( "name" ).createQuery();
try {
final FullTextQuery textQuery = s.createFullTextQuery( query, IncorrectGet.class ).setProjection( "subIncorrect.name" );
textQuery.list();
fail( "Incorrect bridge should fail" );
}
catch (BridgeException e) {
tx.rollback();
}
catch (HibernateException e) {
final Throwable throwable = e.getCause();
if ( throwable instanceof BridgeException ) {
//expected
//System.out.println( throwable.getMessage() );
assertTrue( throwable.getMessage().contains( "class: " + IncorrectGet.class.getName() ) );
assertTrue( throwable.getMessage().contains( "path: subIncorrect.name" ) );
tx.rollback();
}
else {
e.printStackTrace();
fail( "Incorrect bridge should raise a SearchException: " + e.toString() );
}
}
catch (Exception e) {
e.printStackTrace();
fail( "Incorrect bridge should raise a SearchException" );
}
s.close();
}
@Test
public void testIncorrectObjectToStringBridge() throws Exception {
IncorrectObjectToString incorrect = new IncorrectObjectToString();
incorrect.setName( "test" );
FullTextSession s = Search.getFullTextSession( openSession() );
Transaction tx = s.beginTransaction();
try {
s.persist( incorrect );
s.flush();
s.flushToIndexes();
fail( "Incorrect bridge should fail" );
}
catch (BridgeException e) {
tx.rollback();
}
catch (HibernateException e) {
final Throwable throwable = e.getCause();
if ( throwable instanceof BridgeException ) {
//expected
assertTrue( throwable.getMessage().contains( "class: " + IncorrectObjectToString.class.getName() ) );
assertTrue( throwable.getMessage().contains( "path: id" ) );
tx.rollback();
}
else {
e.printStackTrace();
fail( "Incorrect bridge should raise a SearchException: " + e.toString() );
}
}
catch (Exception e) {
e.printStackTrace();
fail( "Incorrect bridge should raise a SearchException" );
}
s.close();
}
@Override
public Class<?>[] getAnnotatedClasses() {
return new Class[] {
Cloud.class,
IncorrectSet.class,
IncorrectGet.class,
IncorrectObjectToString.class
};
}
@Override
public void configure(Map<String,Object> cfg) {
cfg.put( Environment.ANALYZER_CLASS, SimpleAnalyzer.class.getName() );
}
}