/*
* 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.metadata;
import java.net.URI;
import java.util.Date;
import java.util.Set;
import org.hibernate.annotations.common.reflection.java.JavaReflectionManager;
import org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.DocumentId;
import org.hibernate.search.annotations.Facet;
import org.hibernate.search.annotations.FacetEncodingType;
import org.hibernate.search.annotations.Facets;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Fields;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.NumericField;
import org.hibernate.search.cfg.spi.SearchConfiguration;
import org.hibernate.search.engine.impl.ConfigContext;
import org.hibernate.search.engine.metadata.impl.AnnotationMetadataProvider;
import org.hibernate.search.engine.metadata.impl.DocumentFieldMetadata;
import org.hibernate.search.engine.metadata.impl.FacetMetadata;
import org.hibernate.search.engine.metadata.impl.TypeMetadata;
import org.hibernate.search.exception.SearchException;
import org.hibernate.search.indexes.spi.LuceneEmbeddedIndexManagerType;
import org.hibernate.search.testsupport.TestForIssue;
import org.hibernate.search.testsupport.setup.BuildContextForTest;
import org.hibernate.search.testsupport.setup.SearchConfigurationForTest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* @author Hardy Ferentschik
*/
@TestForIssue(jiraKey = "HSEARCH-809")
public class DocumentFieldMetadataTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
private AnnotationMetadataProvider metadataProvider;
@Before
public void setUp() {
SearchConfiguration searchConfiguration = new SearchConfigurationForTest();
ConfigContext configContext = new ConfigContext(
searchConfiguration,
new BuildContextForTest( searchConfiguration )
);
metadataProvider = new AnnotationMetadataProvider( new JavaReflectionManager(), configContext );
}
@Test
public void testStringFieldCanBeCOnfiguredForFaceting() {
FacetMetadata facetMetadata = getSingleFacetMetadata( Foo.class, "name" );
assertEquals( "Unexpected facet name", "name", facetMetadata.getAbsoluteName() );
assertEquals( "Unexpected encoding type", FacetEncodingType.STRING, facetMetadata.getEncoding() );
}
@Test
public void testDateFieldCanBeConfiguredForFaceting() {
FacetMetadata facetMetadata = getSingleFacetMetadata( Foobar.class, "date" );
assertEquals( "Unexpected facet name", "date", facetMetadata.getAbsoluteName() );
assertEquals( "Unexpected encoding type", FacetEncodingType.LONG, facetMetadata.getEncoding() );
}
@Test
public void testExplicitFacetName() {
FacetMetadata facetMetadata = getSingleFacetMetadata( Fubar.class, "name" );
assertEquals( "Unexpected facet name", "facet_name", facetMetadata.getAbsoluteName() );
assertEquals( "Unexpected encoding type", FacetEncodingType.STRING, facetMetadata.getEncoding() );
}
@Test
public void testFacetFieldTargetsSpecificFieldAnnotation() {
FacetMetadata facetMetadata = getSingleFacetMetadata( Baz.class, "facet_value" );
assertEquals( "Unexpected facet name", "facet_value", facetMetadata.getAbsoluteName() );
assertEquals( "Unexpected encoding type", FacetEncodingType.DOUBLE, facetMetadata.getEncoding() );
}
@Test
public void testMultipleFacetsAnnotation() {
FacetMetadata facetMetadata = getSingleFacetMetadata( Qux.class, "value" );
assertEquals( "Unexpected facet name", "value", facetMetadata.getAbsoluteName() );
assertEquals( "Unexpected encoding type", FacetEncodingType.DOUBLE, facetMetadata.getEncoding() );
facetMetadata = getSingleFacetMetadata( Qux.class, "facet_value" );
assertEquals( "Unexpected facet name", "facet_value", facetMetadata.getAbsoluteName() );
assertEquals( "Unexpected encoding type", FacetEncodingType.STRING, facetMetadata.getEncoding() );
}
@Test
public void testAddingFacetToUnsupportedTypeThrowsException() {
try {
metadataProvider.getTypeMetadataFor( Bar.class, LuceneEmbeddedIndexManagerType.INSTANCE );
fail( "Invalid facet configuration should throw exception. URI type cannot be faceted" );
}
catch (SearchException e) {
assertTrue( "Unexpected error message: " + e.getMessage(), e.getMessage().startsWith( "HSEARCH000264" ) );
}
}
@Test
public void testAddingFacetToUnanalyzedFieldThrowsException() {
try {
metadataProvider.getTypeMetadataFor( Snafu.class, LuceneEmbeddedIndexManagerType.INSTANCE );
fail( "Field targeted for faceting cannot be analyzed" );
}
catch (SearchException e) {
assertTrue( "Unexpected error message: " + e.getMessage(), e.getMessage().startsWith( "HSEARCH000273" ) );
}
}
@Test
public void testNumericFieldReferencingNonExistingFieldThrowsException() {
expectedException.expect( SearchException.class );
expectedException.expectMessage( "HSEARCH000262" );
metadataProvider.getTypeMetadataFor( TypeWithNumericFieldReferringToNonExistantField.class,
LuceneEmbeddedIndexManagerType.INSTANCE );
}
@Test
public void testNumericFieldWithoutFieldThrowsException() {
expectedException.expect( SearchException.class );
expectedException.expectMessage( "HSEARCH000262" );
metadataProvider.getTypeMetadataFor( TypeWithNumericFieldWithoutField.class, LuceneEmbeddedIndexManagerType.INSTANCE );
}
@Test
public void testSeveralNumericFieldsReferringToSameFieldThrowException() {
expectedException.expect( SearchException.class );
expectedException.expectMessage( "HSEARCH000300" );
metadataProvider.getTypeMetadataFor( TypeWithSeveralNumericFieldsReferringToSameField.class,
LuceneEmbeddedIndexManagerType.INSTANCE );
}
private FacetMetadata getSingleFacetMetadata(Class<?> type, String fieldName) {
TypeMetadata typeMetadata = metadataProvider.getTypeMetadataFor( type, LuceneEmbeddedIndexManagerType.INSTANCE );
DocumentFieldMetadata documentFieldMetadata = typeMetadata.getDocumentFieldMetadataFor( fieldName );
assertTrue( "The field should be enabled for faceting", documentFieldMetadata.hasFacets() );
Set<FacetMetadata> facetMetadataSet = documentFieldMetadata.getFacetMetadata();
assertEquals( "Unexpected number of metadata instances", 1, facetMetadataSet.size() );
return facetMetadataSet.iterator().next();
}
@Indexed
public class Foo {
@DocumentId
private Integer id;
@Field(analyze = Analyze.NO)
@Facet
private String name;
}
@Indexed
public class Bar {
@DocumentId
private Integer id;
@Field(analyze = Analyze.NO)
@Facet
private URI uri;
}
@Indexed
public class Foobar {
@DocumentId
private Integer id;
@Field(analyze = Analyze.NO)
@Facet
private Date date;
}
@Indexed
public class Fubar {
@DocumentId
private Integer id;
@Field(analyze = Analyze.NO)
@Facet(name = "facet_name")
private String name;
}
@Indexed
public class Baz {
@DocumentId
private Integer id;
@Fields({
@Field(analyze = Analyze.NO),
@Field(analyze = Analyze.NO, name = "facet_value")
})
@Facet(forField = "facet_value")
private double value;
}
@Indexed
public class Qux {
@DocumentId
private Integer id;
@Fields({
@Field(analyze = Analyze.NO),
@Field(analyze = Analyze.NO, name = "facet_value")
})
@Facets({
@Facet,
@Facet(forField = "facet_value", encoding = FacetEncodingType.STRING)
})
private double value;
}
@Indexed
public class Snafu {
@DocumentId
private Integer id;
@Field
@Facet
private String name;
}
@Indexed
public class TypeWithNumericFieldReferringToNonExistantField {
@DocumentId
private Integer id;
@NumericField(forField = "nonExistant")
@Field
private short name;
}
@Indexed
public class TypeWithNumericFieldWithoutField {
@DocumentId
private Integer id;
@NumericField
private short name;
}
@Indexed
public class TypeWithSeveralNumericFieldsReferringToSameField {
@DocumentId
private Integer id;
@NumericField(forField = "name")
@NumericField(forField = "name") //Intentionally illegal
@Field(name = "name")
private short name;
}
}