/*
* 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.bridge;
import static org.fest.assertions.Assertions.assertThat;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.search.Query;
import org.hibernate.search.backend.spi.Work;
import org.hibernate.search.backend.spi.WorkType;
import org.hibernate.search.cfg.Environment;
import org.hibernate.search.elasticsearch.cfg.ElasticsearchEnvironment;
import org.hibernate.search.elasticsearch.schema.impl.model.DynamicType;
import org.hibernate.search.engine.integration.impl.ExtendedSearchIntegrator;
import org.hibernate.search.exception.ErrorContext;
import org.hibernate.search.exception.ErrorHandler;
import org.hibernate.search.exception.SearchException;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hibernate.search.query.engine.spi.EntityInfo;
import org.hibernate.search.testsupport.junit.SearchFactoryHolder;
import org.hibernate.search.testsupport.setup.TransactionContextForTest;
import org.junit.Rule;
import org.junit.Test;
/**
* Checks that there is a way to create fields in an Elasticsearch indexes having dynamic attributes even when the
* global option is set to strict.
*
* @author Davide D'Alto
*/
public class DynamicMappingIT {
private static final String DYNAMIC_MAPPING = "hibernate.search." + ElasticsearchDynamicIndexedValueHolder.INDEX_NAME + "."
+ ElasticsearchEnvironment.DYNAMIC_MAPPING;
@Rule
public SearchFactoryHolder sfHolder = new SearchFactoryHolder( ElasticsearchDynamicIndexedValueHolder.class )
.withProperty( Environment.ERROR_HANDLER, TestExceptionHandler.class.getName() )
.withProperty( DYNAMIC_MAPPING, DynamicType.STRICT.name() );
@Test
public void testIndexingWithDynamicField() {
// Store some not defined data:
ElasticsearchDynamicIndexedValueHolder holder = new ElasticsearchDynamicIndexedValueHolder( "1" )
.dynamicProperty( "age", "227" )
.dynamicProperty( "name", "Thorin" )
.dynamicProperty( "surname", "Oakenshield" )
.dynamicProperty( "race", "dwarf" );
index( holder );
List<EntityInfo> fieldValue = searchField( "dynamicField.name" );
assertThat( fieldValue ).hasSize( 1 );
assertThat( fieldValue.get( 0 ).getProjection()[0] ).isEqualTo( "Thorin" );
}
@Test
public void testIndexingWithStrictField() {
// This property should be mapped with dynamic: strict, this means that we cannot change the value adding new
// properties and therefore an error should occurs
ElasticsearchDynamicIndexedValueHolder holder = new ElasticsearchDynamicIndexedValueHolder( "2" )
.strictProperty( "age", "64" )
.strictProperty( "name", "Gimli" )
.strictProperty( "race", "dwarf" );
index( holder );
TestExceptionHandler errorHandler = getErrorHandler();
assertThat( errorHandler.getHandleInvocations() ).hasSize( 1 );
ErrorContext errorContext = errorHandler.getHandleInvocations().get( 0 );
Throwable throwable = errorContext.getThrowable();
assertThat( throwable ).isInstanceOf( SearchException.class );
assertThat( throwable.getMessage() ).startsWith( "HSEARCH400007" );
assertThat( throwable.getMessage() ).contains( "strict_dynamic_mapping_exception" );
assertThat( throwable.getMessage() ).contains( "strictField" );
}
private TestExceptionHandler getErrorHandler() {
ExtendedSearchIntegrator searchFactory = sfHolder.getSearchFactory();
ErrorHandler errorHandler = searchFactory.getErrorHandler();
assertThat( errorHandler ).isInstanceOf( TestExceptionHandler.class );
return (TestExceptionHandler) errorHandler;
}
private void index(ElasticsearchDynamicIndexedValueHolder holder) {
ExtendedSearchIntegrator searchFactory = sfHolder.getSearchFactory();
Work work = new Work( holder, holder.id, WorkType.ADD, false );
TransactionContextForTest tc = new TransactionContextForTest();
searchFactory.getWorker().performWork( work, tc );
tc.end();
}
private List<EntityInfo> searchField(String field) {
ExtendedSearchIntegrator searchFactory = sfHolder.getSearchFactory();
QueryBuilder guestQueryBuilder = searchFactory.buildQueryBuilder().forEntity( ElasticsearchDynamicIndexedValueHolder.class ).get();
Query queryAllGuests = guestQueryBuilder.all().createQuery();
return searchFactory.createHSQuery( queryAllGuests, ElasticsearchDynamicIndexedValueHolder.class )
.projection( field )
.queryEntityInfos();
}
public static class TestExceptionHandler implements ErrorHandler {
private List<ErrorContext> handleInvocations = new ArrayList<>();
@Override
public void handle(ErrorContext context) {
handleInvocations.add( context );
}
@Override
public void handleException(String errorMsg, Throwable exception) {
}
public List<ErrorContext> getHandleInvocations() {
return handleInvocations;
}
public void reset() {
handleInvocations.clear();
}
}
}