From ce95c11994342a9945c1aeef9fe87b0ff0c865a6 Mon Sep 17 00:00:00 2001 From: marko-bekhta Date: Wed, 26 Jul 2023 12:12:10 +0200 Subject: [PATCH] Add a configuration property for mapping configurers --- .../hibernate-search-orm-elasticsearch.adoc | 66 ++++++++++++++- ...csearchBuildTimeConfigPersistenceUnit.java | 29 +++++++ .../HibernateSearchElasticsearchRecorder.java | 23 ++++- .../runtime/bean/HibernateSearchBeanUtil.java | 2 +- .../analysis/AnalysisTestResource.java | 4 + .../AbstractCustomMappingConfigurer.java | 24 ++++++ ...ustomApplicationBeanMappingConfigurer.java | 26 ++++++ .../mapping/CustomClassMappingConfigurer.java | 24 ++++++ .../CustomDependentBeanMappingConfigurer.java | 26 ++++++ ...ustomSearchExtensionMappingConfigurer.java | 24 ++++++ .../mapping/MappingTestResource.java | 84 +++++++++++++++++++ .../MappingTestingApplicationBeanEntity.java | 16 ++++ .../mapping/MappingTestingClassEntity.java | 16 ++++ .../MappingTestingDependentBeanEntity.java | 16 ++++ .../mapping/MappingTestingEntityBase.java | 39 +++++++++ .../MappingTestingSearchExtensionEntity.java | 16 ++++ .../src/main/resources/application.properties | 1 + .../elasticsearch/MappingExtensionTest.java | 37 ++++++++ .../orm/elasticsearch/MappingInGraalIT.java | 7 ++ .../search/orm/elasticsearch/MappingTest.java | 22 +++++ 20 files changed, 498 insertions(+), 4 deletions(-) create mode 100644 integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/AbstractCustomMappingConfigurer.java create mode 100644 integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomApplicationBeanMappingConfigurer.java create mode 100644 integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomClassMappingConfigurer.java create mode 100644 integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomDependentBeanMappingConfigurer.java create mode 100644 integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomSearchExtensionMappingConfigurer.java create mode 100644 integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestResource.java create mode 100644 integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingApplicationBeanEntity.java create mode 100644 integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingClassEntity.java create mode 100644 integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingDependentBeanEntity.java create mode 100644 integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingEntityBase.java create mode 100644 integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingSearchExtensionEntity.java create mode 100644 integration-tests/hibernate-search-orm-elasticsearch/src/test/java/io/quarkus/it/hibernate/search/orm/elasticsearch/MappingExtensionTest.java create mode 100644 integration-tests/hibernate-search-orm-elasticsearch/src/test/java/io/quarkus/it/hibernate/search/orm/elasticsearch/MappingInGraalIT.java create mode 100644 integration-tests/hibernate-search-orm-elasticsearch/src/test/java/io/quarkus/it/hibernate/search/orm/elasticsearch/MappingTest.java diff --git a/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc b/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc index 0770d96f26937..2c60a66e0f921 100644 --- a/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc +++ b/docs/src/main/asciidoc/hibernate-search-orm-elasticsearch.adoc @@ -378,6 +378,69 @@ The nice thing with `@IndexedEmbedded` is that it is able to automatically reind `@IndexedEmbedded` also supports nested documents (using the `storage = NESTED` attribute), but we don't need it here. You can also specify the fields you want to include in your parent index using the `includePaths` attribute if you don't want them all. +[programmatic-mapping] +=== Programmatic mapping + +If, for some reason, adding Hibernate Search annotations to entities is not possible, +mapping can be applied programmatically instead. +Programmatic mapping is configured through the `ProgrammaticMappingConfigurationContext` +that is exposed via a mapping configurer (`HibernateOrmSearchMappingConfigurer`). + +[NOTE] +==== +A mapping configurer (`HibernateOrmSearchMappingConfigurer`) allows much more than just programmatic mapping capabilities. +It also allows link:{hibernate-search-docs-url}#mapper-orm-mapping-configurer[configuring annotation mapping, bridges, and more]. +==== + +Below is an example of a mapping configurer that applies programmatic mapping: + +[source,java] +---- +package org.acme.hibernate.search.elasticsearch.config; + +import org.hibernate.search.mapper.orm.mapping.HibernateOrmMappingConfigurationContext; +import org.hibernate.search.mapper.orm.mapping.HibernateOrmSearchMappingConfigurer; +import org.hibernate.search.mapper.pojo.mapping.definition.programmatic.TypeMappingStep; + +import io.quarkus.hibernate.search.orm.elasticsearch.SearchExtension; + +@SearchExtension // <1> +public class CustomMappingConfigurer implements HibernateOrmSearchMappingConfigurer { + + @Override + public void configure(HibernateOrmMappingConfigurationContext context) { + TypeMappingStep type = context.programmaticMapping() // <2> + .type(SomeIndexedEntity.class); // <3> + type.indexed() // <4> + .index(SomeIndexedEntity.INDEX_NAME); // <5> + type.property("id").documentId(); // <6> + type.property("text").fullTextField(); // <7> + } +} +---- +<1> Annotate the configurer implementation with the `@SearchExtension` qualifier +to tell Quarkus it should be used by Hibernate Search in the default persistence unit. ++ +The annotation can also target a specific persistence unit (`@SearchExtension(persistenceUnit = "nameOfYourPU")`). +<2> Access the programmatic mapping context. +<3> Create mapping step for the `SomeIndexedEntity` entity. +<4> Define the `SomeIndexedEntity` entity as indexed. +<5> Provide an index name to be used for the `SomeIndexedEntity` entity. +<6> Define the document id property. +<7> Define a full-text search field for the `text` property. + +[TIP] +==== +Alternatively, if for some reason you can't or don't want to annotate your mapping configurer with `@SearchExtension`, +you can simply annotate it with `@Dependent @Named("myMappingConfigurer")` +and then reference it from configuration properties: + +[source,properties] +---- +quarkus.hibernate-search-orm.mapping.configurer=bean:myMappingConfigurer +---- +==== + == Analyzers and normalizers === Introduction @@ -438,8 +501,7 @@ package org.acme.hibernate.search.elasticsearch.config; import org.hibernate.search.backend.elasticsearch.analysis.ElasticsearchAnalysisConfigurationContext; import org.hibernate.search.backend.elasticsearch.analysis.ElasticsearchAnalysisConfigurer; -import jakarta.enterprise.context.Dependent; -import jakarta.inject.Named; +import io.quarkus.hibernate.search.orm.elasticsearch.SearchExtension; @SearchExtension // <1> public class AnalysisConfigurer implements ElasticsearchAnalysisConfigurer { diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit.java index 9eb871bb3f8bc..ad2ce67d61b9b 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit.java +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit.java @@ -50,6 +50,11 @@ public interface HibernateSearchElasticsearchBuildTimeConfigPersistenceUnit { */ CoordinationConfig coordination(); + /** + * Configuration for mapping. + */ + MappingConfig mapping(); + @ConfigGroup interface ElasticsearchBackendBuildTimeConfig { /** @@ -214,4 +219,28 @@ interface CoordinationConfig { Optional strategy(); } + @ConfigGroup + interface MappingConfig { + /** + * One or more xref:hibernate-search-orm-elasticsearch.adoc#bean-reference-note-anchor[bean references] + * to the component(s) used to configure Hibernate Search mapping. + * + * The referenced beans must implement `HibernateOrmSearchMappingConfigurer`. + * + * See xref:hibernate-search-orm-elasticsearch.adoc#programmatic-mapping[Programmatic mapping] for an example + * on how mapping configurers can be used to apply programmatic mappings. + * + * [NOTE] + * ==== + * Instead of setting this configuration property, + * you can simply annotate your custom `HibernateOrmSearchMappingConfigurer` implementations with `@SearchExtension` + * and leave the configuration property unset: Hibernate Search will use the annotated implementation automatically. + * If this configuration property is set, it takes precedence over any `@SearchExtension` annotation. + * ==== + * + * @asciidoclet + */ + Optional> configurer(); + } + } diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java index 1144918f8c249..7f72c8a5a8b82 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/HibernateSearchElasticsearchRecorder.java @@ -4,6 +4,7 @@ import static io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchConfigUtil.addBackendIndexConfig; import static io.quarkus.hibernate.search.orm.elasticsearch.runtime.HibernateSearchConfigUtil.addConfig; +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -28,6 +29,7 @@ import org.hibernate.search.backend.elasticsearch.index.layout.IndexLayoutStrategy; import org.hibernate.search.engine.cfg.BackendSettings; import org.hibernate.search.engine.cfg.EngineSettings; +import org.hibernate.search.engine.environment.bean.BeanReference; import org.hibernate.search.engine.reporting.FailureHandler; import org.hibernate.search.mapper.orm.Search; import org.hibernate.search.mapper.orm.automaticindexing.session.AutomaticIndexingSynchronizationStrategy; @@ -35,6 +37,7 @@ import org.hibernate.search.mapper.orm.bootstrap.spi.HibernateOrmIntegrationBooter; import org.hibernate.search.mapper.orm.cfg.HibernateOrmMapperSettings; import org.hibernate.search.mapper.orm.coordination.common.spi.CoordinationStrategy; +import org.hibernate.search.mapper.orm.mapping.HibernateOrmSearchMappingConfigurer; import org.hibernate.search.mapper.orm.mapping.SearchMapping; import org.hibernate.search.mapper.orm.session.SearchSession; import org.hibernate.search.mapper.pojo.work.IndexingPlanSynchronizationStrategy; @@ -209,7 +212,7 @@ public void contributeBootProperties(BiConsumer propertyCollecto addConfig(propertyCollector, HibernateOrmMapperSettings.MAPPING_CONFIGURER, - new QuarkusHibernateOrmSearchMappingConfigurer(rootAnnotationMappedClasses)); + collectAllHibernateOrmSearchMappingConfigurers()); addConfig(propertyCollector, HibernateOrmMapperSettings.COORDINATION_STRATEGY, @@ -242,6 +245,24 @@ public void contributeBootProperties(BiConsumer propertyCollecto } } + private List> collectAllHibernateOrmSearchMappingConfigurers() { + List> configurers = new ArrayList<>(); + // 1. We add the quarkus-specific configurer: + configurers + .add(BeanReference.ofInstance(new QuarkusHibernateOrmSearchMappingConfigurer(rootAnnotationMappedClasses))); + // 2. Then we check if any configurers were supplied by a user be it through a property or via an extension: + Optional>> beanReferences = HibernateSearchBeanUtil + .multiExtensionBeanReferencesFor( + buildTimeConfig.mapping().configurer(), + HibernateOrmSearchMappingConfigurer.class, + persistenceUnitName, null, null); + if (beanReferences.isPresent()) { + configurers.addAll(beanReferences.get()); + } + + return configurers; + } + @Override public void onMetadataInitialized(Metadata metadata, BootstrapContext bootstrapContext, BiConsumer propertyCollector) { diff --git a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/bean/HibernateSearchBeanUtil.java b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/bean/HibernateSearchBeanUtil.java index 734a4ce827772..ca02fd677c7bc 100644 --- a/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/bean/HibernateSearchBeanUtil.java +++ b/extensions/hibernate-search-orm-elasticsearch/runtime/src/main/java/io/quarkus/hibernate/search/orm/elasticsearch/runtime/bean/HibernateSearchBeanUtil.java @@ -70,7 +70,7 @@ private static Optional>> multiExtensionBeanReferences return Optional.of(references); } - public static InjectableInstance extensionInstanceFor(Class beanType, String persistenceUnitName, + private static InjectableInstance extensionInstanceFor(Class beanType, String persistenceUnitName, String backendName, String indexName) { return Arc.container().select(beanType, new SearchExtension.Literal(persistenceUnitName, backendName == null ? "" : backendName, diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/analysis/AnalysisTestResource.java b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/analysis/AnalysisTestResource.java index 2932dc8e44c6e..4cb5428e396b3 100644 --- a/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/analysis/AnalysisTestResource.java +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/analysis/AnalysisTestResource.java @@ -34,6 +34,7 @@ public void initData() { entityManager.persist(new Analysis3TestingEntity("irrelevant")); entityManager.persist(new Analysis4TestingEntity("irrelevant")); entityManager.persist(new Analysis5TestingEntity("irrelevant")); + entityManager.persist(new Analysis6TestingEntity("irrelevant")); } @GET @@ -59,6 +60,9 @@ public String testAnalysisConfigured() { assertThat(findTypesMatching("text", "token_inserted_by_index_analysis_5")) .containsExactlyInAnyOrder(Analysis5TestingEntity.class); + assertThat(findTypesMatching("text", "token_inserted_by_index_analysis_6")) + .containsExactlyInAnyOrder(Analysis6TestingEntity.class); + return "OK"; } diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/AbstractCustomMappingConfigurer.java b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/AbstractCustomMappingConfigurer.java new file mode 100644 index 0000000000000..39de986473a70 --- /dev/null +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/AbstractCustomMappingConfigurer.java @@ -0,0 +1,24 @@ +package io.quarkus.it.hibernate.search.orm.elasticsearch.mapping; + +import org.hibernate.search.mapper.orm.mapping.HibernateOrmMappingConfigurationContext; +import org.hibernate.search.mapper.orm.mapping.HibernateOrmSearchMappingConfigurer; +import org.hibernate.search.mapper.pojo.mapping.definition.programmatic.TypeMappingStep; + +public abstract class AbstractCustomMappingConfigurer implements HibernateOrmSearchMappingConfigurer { + + private final Class type; + private final String indexName; + + protected AbstractCustomMappingConfigurer(Class type, String indexName) { + this.type = type; + this.indexName = indexName; + } + + @Override + public void configure(HibernateOrmMappingConfigurationContext context) { + TypeMappingStep type = context.programmaticMapping().type(this.type); + type.indexed().index(this.indexName); + type.property("id").documentId(); + type.property("text").fullTextField(); + } +} diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomApplicationBeanMappingConfigurer.java b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomApplicationBeanMappingConfigurer.java new file mode 100644 index 0000000000000..f13cedc0dcfba --- /dev/null +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomApplicationBeanMappingConfigurer.java @@ -0,0 +1,26 @@ +package io.quarkus.it.hibernate.search.orm.elasticsearch.mapping; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; + +import org.hibernate.search.mapper.orm.mapping.HibernateOrmMappingConfigurationContext; + +import io.quarkus.it.hibernate.search.orm.elasticsearch.analysis.MyCdiContext; + +@ApplicationScoped +@Named("custom-application-bean-mapping-configurer") +public class CustomApplicationBeanMappingConfigurer extends AbstractCustomMappingConfigurer { + @Inject + MyCdiContext cdiContext; + + public CustomApplicationBeanMappingConfigurer() { + super(MappingTestingApplicationBeanEntity.class, MappingTestingApplicationBeanEntity.INDEX); + } + + @Override + public void configure(HibernateOrmMappingConfigurationContext context) { + MyCdiContext.checkAvailable(cdiContext); + super.configure(context); + } +} diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomClassMappingConfigurer.java b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomClassMappingConfigurer.java new file mode 100644 index 0000000000000..5c4e79cd41582 --- /dev/null +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomClassMappingConfigurer.java @@ -0,0 +1,24 @@ +package io.quarkus.it.hibernate.search.orm.elasticsearch.mapping; + +import jakarta.inject.Inject; + +import org.hibernate.search.mapper.orm.mapping.HibernateOrmMappingConfigurationContext; + +import io.quarkus.it.hibernate.search.orm.elasticsearch.analysis.MyCdiContext; + +public class CustomClassMappingConfigurer extends AbstractCustomMappingConfigurer { + + @Inject + MyCdiContext cdiContext; + + public CustomClassMappingConfigurer() { + super(MappingTestingClassEntity.class, MappingTestingClassEntity.INDEX); + } + + @Override + public void configure(HibernateOrmMappingConfigurationContext context) { + MyCdiContext.checkNotAvailable(cdiContext); + + super.configure(context); + } +} diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomDependentBeanMappingConfigurer.java b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomDependentBeanMappingConfigurer.java new file mode 100644 index 0000000000000..8b34f3efbfe0f --- /dev/null +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomDependentBeanMappingConfigurer.java @@ -0,0 +1,26 @@ +package io.quarkus.it.hibernate.search.orm.elasticsearch.mapping; + +import jakarta.enterprise.context.Dependent; +import jakarta.inject.Inject; +import jakarta.inject.Named; + +import org.hibernate.search.mapper.orm.mapping.HibernateOrmMappingConfigurationContext; + +import io.quarkus.it.hibernate.search.orm.elasticsearch.analysis.MyCdiContext; + +@Dependent +@Named("custom-dependent-bean-mapping-configurer") +public class CustomDependentBeanMappingConfigurer extends AbstractCustomMappingConfigurer { + @Inject + MyCdiContext cdiContext; + + public CustomDependentBeanMappingConfigurer() { + super(MappingTestingDependentBeanEntity.class, MappingTestingDependentBeanEntity.INDEX); + } + + @Override + public void configure(HibernateOrmMappingConfigurationContext context) { + MyCdiContext.checkAvailable(cdiContext); + super.configure(context); + } +} diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomSearchExtensionMappingConfigurer.java b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomSearchExtensionMappingConfigurer.java new file mode 100644 index 0000000000000..da285e6e5874f --- /dev/null +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/CustomSearchExtensionMappingConfigurer.java @@ -0,0 +1,24 @@ +package io.quarkus.it.hibernate.search.orm.elasticsearch.mapping; + +import jakarta.inject.Inject; + +import org.hibernate.search.mapper.orm.mapping.HibernateOrmMappingConfigurationContext; + +import io.quarkus.hibernate.search.orm.elasticsearch.SearchExtension; +import io.quarkus.it.hibernate.search.orm.elasticsearch.analysis.MyCdiContext; + +@SearchExtension +public class CustomSearchExtensionMappingConfigurer extends AbstractCustomMappingConfigurer { + @Inject + MyCdiContext cdiContext; + + public CustomSearchExtensionMappingConfigurer() { + super(MappingTestingSearchExtensionEntity.class, MappingTestingSearchExtensionEntity.INDEX); + } + + @Override + public void configure(HibernateOrmMappingConfigurationContext context) { + MyCdiContext.checkAvailable(cdiContext); + super.configure(context); + } +} diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestResource.java b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestResource.java new file mode 100644 index 0000000000000..1e4ec3d651b28 --- /dev/null +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestResource.java @@ -0,0 +1,84 @@ +package io.quarkus.it.hibernate.search.orm.elasticsearch.mapping; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; +import jakarta.transaction.Transactional; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.PUT; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; + +import org.hibernate.search.mapper.orm.Search; +import org.hibernate.search.mapper.orm.session.SearchSession; +import org.hibernate.search.util.common.SearchException; + +@Path("/test/mapping") +public class MappingTestResource { + + @Inject + EntityManager entityManager; + + @PUT + @Path("/init-data") + @Transactional + public void initData() { + entityManager.persist(new MappingTestingApplicationBeanEntity("text")); + entityManager.persist(new MappingTestingDependentBeanEntity("text")); + entityManager.persist(new MappingTestingClassEntity("text")); + entityManager.persist(new MappingTestingSearchExtensionEntity("text")); + } + + @GET + @Path("/mapping-property") + @Produces(MediaType.TEXT_PLAIN) + @Transactional + public String testMappingConfiguredProperty() { + SearchSession searchSession = Search.session(entityManager); + assertSearch(searchSession, MappingTestingApplicationBeanEntity.class); + assertSearch(searchSession, MappingTestingDependentBeanEntity.class); + assertSearch(searchSession, MappingTestingClassEntity.class); + // since the property overrides this mapper from the extension: + assertThatThrownBy(() -> assertSearch(searchSession, MappingTestingSearchExtensionEntity.class)) + .isInstanceOf(SearchException.class) + .hasMessageContainingAll("No matching indexed entity types for types", + MappingTestingSearchExtensionEntity.class.getName()); + return "OK"; + } + + @GET + @Path("/mapping-extension") + @Produces(MediaType.TEXT_PLAIN) + @Transactional + public String testMappingConfiguredExtension() { + SearchSession searchSession = Search.session(entityManager); + + assertThatThrownBy(() -> assertSearch(searchSession, MappingTestingApplicationBeanEntity.class)) + .isInstanceOf(SearchException.class) + .hasMessageContainingAll("No matching indexed entity types for types", + MappingTestingApplicationBeanEntity.class.getName()); + assertThatThrownBy(() -> assertSearch(searchSession, MappingTestingDependentBeanEntity.class)) + .isInstanceOf(SearchException.class) + .hasMessageContainingAll("No matching indexed entity types for types", + MappingTestingDependentBeanEntity.class.getName()); + assertThatThrownBy(() -> assertSearch(searchSession, MappingTestingClassEntity.class)) + .isInstanceOf(SearchException.class) + .hasMessageContainingAll("No matching indexed entity types for types", + MappingTestingClassEntity.class.getName()); + + assertSearch(searchSession, MappingTestingSearchExtensionEntity.class); + return "OK"; + } + + private void assertSearch(SearchSession searchSession, Class type) { + assertThat(searchSession.search(type) + .select(f -> f.field("text")) + .where(f -> f.match().field("text").matching("text")) + .fetchAllHits()) + .hasSize(1) + .containsOnly("text"); + } +} diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingApplicationBeanEntity.java b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingApplicationBeanEntity.java new file mode 100644 index 0000000000000..2f6cc63c05c89 --- /dev/null +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingApplicationBeanEntity.java @@ -0,0 +1,16 @@ +package io.quarkus.it.hibernate.search.orm.elasticsearch.mapping; + +import jakarta.persistence.Entity; + +@Entity +public class MappingTestingApplicationBeanEntity extends MappingTestingEntityBase { + + public static final String INDEX = "mapping-testing-application-bean-entity"; + + public MappingTestingApplicationBeanEntity() { + } + + public MappingTestingApplicationBeanEntity(String text) { + super(text); + } +} diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingClassEntity.java b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingClassEntity.java new file mode 100644 index 0000000000000..5f64ec6dae474 --- /dev/null +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingClassEntity.java @@ -0,0 +1,16 @@ +package io.quarkus.it.hibernate.search.orm.elasticsearch.mapping; + +import jakarta.persistence.Entity; + +@Entity +public class MappingTestingClassEntity extends MappingTestingEntityBase { + + public static final String INDEX = "mapping-testing-class-entity"; + + public MappingTestingClassEntity() { + } + + public MappingTestingClassEntity(String text) { + super(text); + } +} diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingDependentBeanEntity.java b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingDependentBeanEntity.java new file mode 100644 index 0000000000000..5574e98f6829e --- /dev/null +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingDependentBeanEntity.java @@ -0,0 +1,16 @@ +package io.quarkus.it.hibernate.search.orm.elasticsearch.mapping; + +import jakarta.persistence.Entity; + +@Entity +public class MappingTestingDependentBeanEntity extends MappingTestingEntityBase { + + public static final String INDEX = "mapping-testing-dependent-bean-entity"; + + public MappingTestingDependentBeanEntity() { + } + + public MappingTestingDependentBeanEntity(String text) { + super(text); + } +} diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingEntityBase.java b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingEntityBase.java new file mode 100644 index 0000000000000..3eca369267c6b --- /dev/null +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingEntityBase.java @@ -0,0 +1,39 @@ +package io.quarkus.it.hibernate.search.orm.elasticsearch.mapping; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; + +@MappedSuperclass +public class MappingTestingEntityBase { + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mappingSeq") + private Long id; + + private String text; + + public MappingTestingEntityBase() { + } + + public MappingTestingEntityBase(String text) { + this.text = text; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } +} diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingSearchExtensionEntity.java b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingSearchExtensionEntity.java new file mode 100644 index 0000000000000..e5e268dcd54f1 --- /dev/null +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/main/java/io/quarkus/it/hibernate/search/orm/elasticsearch/mapping/MappingTestingSearchExtensionEntity.java @@ -0,0 +1,16 @@ +package io.quarkus.it.hibernate.search.orm.elasticsearch.mapping; + +import jakarta.persistence.Entity; + +@Entity +public class MappingTestingSearchExtensionEntity extends MappingTestingEntityBase { + + public static final String INDEX = "mapping-testing-search-extension-entity"; + + public MappingTestingSearchExtensionEntity() { + } + + public MappingTestingSearchExtensionEntity(String text) { + super(text); + } +} diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/main/resources/application.properties b/integration-tests/hibernate-search-orm-elasticsearch/src/main/resources/application.properties index 5d6c5b394c047..50ea8e73a8cd4 100644 --- a/integration-tests/hibernate-search-orm-elasticsearch/src/main/resources/application.properties +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/main/resources/application.properties @@ -5,6 +5,7 @@ quarkus.datasource.jdbc.max-size=8 quarkus.hibernate-orm.database.generation=drop-and-create +quarkus.hibernate-search-orm.mapping.configurer=bean:custom-dependent-bean-mapping-configurer,class:io.quarkus.it.hibernate.search.orm.elasticsearch.mapping.CustomClassMappingConfigurer,bean:custom-application-bean-mapping-configurer quarkus.hibernate-search-orm.elasticsearch.version=7 quarkus.hibernate-search-orm.elasticsearch.analysis.configurer=bean:backend-analysis quarkus.hibernate-search-orm.elasticsearch.indexes.Analysis1TestingEntity.analysis.configurer=class:io.quarkus.it.hibernate.search.orm.elasticsearch.analysis.IndexAnalysis1Configurer diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/test/java/io/quarkus/it/hibernate/search/orm/elasticsearch/MappingExtensionTest.java b/integration-tests/hibernate-search-orm-elasticsearch/src/test/java/io/quarkus/it/hibernate/search/orm/elasticsearch/MappingExtensionTest.java new file mode 100644 index 0000000000000..7ae4dde3c4ced --- /dev/null +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/test/java/io/quarkus/it/hibernate/search/orm/elasticsearch/MappingExtensionTest.java @@ -0,0 +1,37 @@ +package io.quarkus.it.hibernate.search.orm.elasticsearch; + +import static org.hamcrest.Matchers.is; + +import java.util.Map; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; +import io.restassured.RestAssured; + +@QuarkusTest +@DisabledOnOs(OS.WINDOWS) +@TestProfile(MappingExtensionTest.Profile.class) +public class MappingExtensionTest { + public static class Profile implements QuarkusTestProfile { + @Override + public Map getConfigOverrides() { + // No mapping configurers so the one from the extension should be in effect + return Map.of("quarkus.hibernate-search-orm.mapping.configurer", ""); + } + } + + @Test + public void testMapping() { + RestAssured.when().put("/test/mapping/init-data").then() + .statusCode(204); + + RestAssured.when().get("/test/mapping/mapping-extension").then() + .statusCode(200) + .body(is("OK")); + } +} diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/test/java/io/quarkus/it/hibernate/search/orm/elasticsearch/MappingInGraalIT.java b/integration-tests/hibernate-search-orm-elasticsearch/src/test/java/io/quarkus/it/hibernate/search/orm/elasticsearch/MappingInGraalIT.java new file mode 100644 index 0000000000000..7bf664a1529b5 --- /dev/null +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/test/java/io/quarkus/it/hibernate/search/orm/elasticsearch/MappingInGraalIT.java @@ -0,0 +1,7 @@ +package io.quarkus.it.hibernate.search.orm.elasticsearch; + +import io.quarkus.test.junit.QuarkusIntegrationTest; + +@QuarkusIntegrationTest +public class MappingInGraalIT extends MappingTest { +} diff --git a/integration-tests/hibernate-search-orm-elasticsearch/src/test/java/io/quarkus/it/hibernate/search/orm/elasticsearch/MappingTest.java b/integration-tests/hibernate-search-orm-elasticsearch/src/test/java/io/quarkus/it/hibernate/search/orm/elasticsearch/MappingTest.java new file mode 100644 index 0000000000000..219dab3153ae0 --- /dev/null +++ b/integration-tests/hibernate-search-orm-elasticsearch/src/test/java/io/quarkus/it/hibernate/search/orm/elasticsearch/MappingTest.java @@ -0,0 +1,22 @@ +package io.quarkus.it.hibernate.search.orm.elasticsearch; + +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; + +@QuarkusTest +public class MappingTest { + + @Test + public void testMapping() { + RestAssured.when().put("/test/mapping/init-data").then() + .statusCode(204); + + RestAssured.when().get("/test/mapping/mapping-property").then() + .statusCode(200) + .body(is("OK")); + } +}