diff --git a/documentation/src/main/asciidoc/reference/indexing-automatic.asciidoc b/documentation/src/main/asciidoc/reference/indexing-automatic.asciidoc index ce2f429ef54..c4502358c1f 100644 --- a/documentation/src/main/asciidoc/reference/indexing-automatic.asciidoc +++ b/documentation/src/main/asciidoc/reference/indexing-automatic.asciidoc @@ -99,12 +99,12 @@ Automatic indexing is affected by the synchronization strategy in use in the `Se See <> for more information. -[[indexing-automatic-filter]] -== Automatic indexing type filter +[[indexing-plan-filter]] +== Indexing plan filter include::components/incubating-warning.asciidoc[] -In some scenarios, it might be helpful to pause the automatic indexing programmatically, for example, +In some scenarios, it might be helpful to pause the indexing programmatically, for example, when importing larger amounts of data. Hibernate Search allows configuring application-wide and session-level filters to manage which types are tracked for changes and indexed. @@ -112,40 +112,77 @@ and session-level filters to manage which types are tracked for changes and inde ==== [source, JAVA, indent=0, subs="+callouts"] ---- -include::{sourcedir}/org/hibernate/search/documentation/mapper/orm/automaticindexing/HibernateOrmAutomaticIndexingFilterIT.java[tags=application-filter] +include::{sourcedir}/org/hibernate/search/documentation/mapper/orm/automaticindexing/HibernateOrmIndexingPlanFilterIT.java[tags=application-filter] ---- -Configuring an application-wide filter requires either an instance of `EntityManagerFactory` or `SessionFactory`. +Configuring an application-wide filter requires an instance of the `SearchMapping`. -<1> Start the declaration of the automatic indexing filter. -<2> Configure included/excluded types through the `PojoAutomaticIndexingTypeFilterConfigurer` +<1> <>. +<2> Start the declaration of the indexing plan filter. +<3> Configure included/excluded types through the `SearchIndexingPlanFilter` ==== .Configuring a session-level filter ==== [source, JAVA, indent=0, subs="+callouts"] ---- -include::{sourcedir}/org/hibernate/search/documentation/mapper/orm/automaticindexing/HibernateOrmAutomaticIndexingFilterIT.java[tags=session-filter] +include::{sourcedir}/org/hibernate/search/documentation/mapper/orm/automaticindexing/HibernateOrmIndexingPlanFilterIT.java[tags=session-filter] ---- Configuring a session level filter is available through an instance of the `SearchSession`. -<1> Receive an instance of the `SearchSession`. -<2> Configure included/excluded types through the `PojoAutomaticIndexingTypeFilterConfigurer` +<1> <> +<2> Configure included/excluded types through the `SearchIndexingPlanFilter` ==== +Filter can be defined by providing indexed and contained types as well as their supertypes. +Interfaces are not allowed and passing an interface class to any of the filter definition methods will result in an exception. +If dynamic types represented by a `Map` are used then their names must be used to configure the filter. Filter rules are: -* If the type `A` is explicitly included by the filter, then a change to an object that is exactly of an indexed type `A` is processed. -* If the type `A` is explicitly excluded by the filter, then a change to an object that is exactly of an indexed type `A` is ignored. -* If the type `A` is explicitly included by the filter, then a change to an object that is exactly of an indexed type `B`, +* If the type `A` is explicitly included by the filter, then a change to an object that is exactly of a type `A` is processed. +* If the type `A` is explicitly excluded by the filter, then a change to an object that is exactly of a type `A` is ignored. +* If the type `A` is explicitly included by the filter, then a change to an object that is exactly of a type `B`, which is a subtype of the type `A`, is processed unless the filter explicitly excludes a more specific supertype of a type `B`. -* If the type `A` is excluded by the filter explicitly, then a change to an object that is exactly of an indexed type `B`, +* If the type `A` is excluded by the filter explicitly, then a change to an object that is exactly of a type `B`, which is a subtype of type the `A`, is ignored unless the filter explicitly includes a more specific supertype of a type `B`. A session-level filter takes precedence over an application-wide one. If the session-level filter configuration does not -either explicitly or through inheritance include/exclude the exact type of entity, then the decision will be made by +either explicitly or through inheritance include/exclude the exact type of an entity, then the decision will be made by the application-wide filter. If an application-wide filter also has no explicit configuration for a type, then this type is considered to be included. +In some cases we might need to disable the indexing entirely. Listing all entities one by one might be cumbersome, +but since filter configuration is implicitly applied to subtypes, `.exclude(Object.class)` can be used to exclude all types. +Conversely, `.include(Object.class)` can be used to enable indexing within a session filter when +the application-wide filter disables indexing completely. + +.Disable all indexing within a session +==== +[source, JAVA, indent=0, subs="+callouts"] +---- +include::{sourcedir}/org/hibernate/search/documentation/mapper/orm/automaticindexing/HibernateOrmIndexingPlanFilterIT.java[tags=session-filter-exclude-all] +---- +Configuring a session level filter is available through an instance of the `SearchSession`. + +<1> <> +<2> Excluding `Object.class` will lead to excluding all its subtypes which means nothing will be included. +==== + +.Enable indexing in the session while application-wide indexing is paused +==== +[source, JAVA, indent=0, subs="+callouts"] +---- +include::{sourcedir}/org/hibernate/search/documentation/mapper/orm/automaticindexing/HibernateOrmIndexingPlanFilterIT.java[tags=session-filter-exclude-include-all-application] +---- +---- +include::{sourcedir}/org/hibernate/search/documentation/mapper/orm/automaticindexing/HibernateOrmIndexingPlanFilterIT.java[tags=session-filter-exclude-include-all-session] +---- + +<1> <>. +<2> An application-wide filter disables any indexing +<3> <> +<4> A session level filter re-enables indexing of all indexed types *for changes happening in current session only* +==== + [NOTE] ==== Trying to configure the same type as both included and excluded at the same time by the same filter @@ -154,7 +191,7 @@ will lead to an exception being thrown. [NOTE] ==== -Only an application-wide filter is safe to use when using the outbox polling coordination strategy. +Only an application-wide filter is safe to use when using the <>. When this coordination strategy is in use, entities are loaded and indexed in a different session from the one where they were changed. It might lead to unexpected results as the session where events are processed will not apply the filter configured by the session in which entities were modified. diff --git a/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/automaticindexing/HibernateOrmIndexingPlanFilterIT.java b/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/automaticindexing/HibernateOrmIndexingPlanFilterIT.java index e6170964ad5..ccc24612311 100644 --- a/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/automaticindexing/HibernateOrmIndexingPlanFilterIT.java +++ b/documentation/src/test/java/org/hibernate/search/documentation/mapper/orm/automaticindexing/HibernateOrmIndexingPlanFilterIT.java @@ -19,6 +19,8 @@ import org.hibernate.search.documentation.testsupport.BackendConfigurations; import org.hibernate.search.documentation.testsupport.DocumentationSetupHelper; import org.hibernate.search.mapper.orm.Search; +import org.hibernate.search.mapper.orm.mapping.SearchMapping; +import org.hibernate.search.mapper.orm.session.SearchSession; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed; @@ -43,8 +45,12 @@ public void setup() { @Test public void applicationFilterOnly() { // tag::application-filter[] - Search.mapping( entityManagerFactory ).indexingPlanFilter( // <1> - ctx -> ctx.exclude( EntityA.class ) // <2> + SearchMapping searchMapping = /* ... */ // <1> + // end::application-filter[] + Search.mapping( entityManagerFactory ); + // tag::application-filter[] + searchMapping.indexingPlanFilter( // <2> + ctx -> ctx.exclude( EntityA.class ) // <3> .include( EntityExtendsA2.class ) ); // end::application-filter[] @@ -68,11 +74,14 @@ public void applicationFilterOnly() { public void sessionFilterOnly() { with( entityManagerFactory ).runInTransaction( entityManager -> { // tag::session-filter[] - Search.session( entityManager ) // <1> - .indexingPlanFilter( // <2> - ctx -> ctx.exclude( EntityA.class ) - .include( EntityExtendsA2.class ) - ); + SearchSession session = /* ... */ // <1> + // end::session-filter[] + Search.session( entityManager ); + // tag::session-filter[] + session.indexingPlanFilter( + ctx -> ctx.exclude( EntityA.class ) // <2> + .include( EntityExtendsA2.class ) + ); // end::session-filter[] entityManager.persist( new EntityA( 10, "test" ) ); @@ -90,6 +99,73 @@ public void sessionFilterOnly() { } ); } + @Test + public void disableAll() { + with( entityManagerFactory ).runInTransaction( entityManager -> { + // tag::session-filter-exclude-all[] + SearchSession searchSession = /* ... */ // <1> + // end::session-filter-exclude-all[] + Search.session( entityManager ); + // tag::session-filter-exclude-all[] + searchSession.indexingPlanFilter( + ctx -> ctx.exclude( Object.class ) // <2> + ); + // end::session-filter-exclude-all[] + + entityManager.persist( new EntityA( 10, "test" ) ); + entityManager.persist( new EntityExtendsA1( 20, "test" ) ); + entityManager.persist( new EntityExtendsA2( 30, "test" ) ); + } ); + + with( entityManagerFactory ).runInTransaction( entityManager -> { + // Nothing is getting indexed: + assertThat( Search.session( entityManager ).search( EntityA.class ) + .select( f -> f.id() ) + .where( f -> f.matchAll() ) + .fetchAllHits() + ).isEmpty(); + } ); + } + + @Test + public void disableAllApplicationEnableSession() { + // tag::session-filter-exclude-include-all-application[] + SearchMapping searchMapping = /* ... */ // <1> + // end::session-filter-exclude-include-all-application[] + Search.mapping( entityManagerFactory ); + // tag::session-filter-exclude-include-all-application[] + searchMapping.indexingPlanFilter( + ctx -> ctx.exclude( Object.class ) // <2> + ); + // end::session-filter-exclude-include-all-application[] + with( entityManagerFactory ).runInTransaction( entityManager -> { + // tag::session-filter-exclude-include-all-session[] + SearchSession searchSession = /* ... */ // <3> + // end::session-filter-exclude-include-all-session[] + Search.session( entityManager ); + // tag::session-filter-exclude-include-all-session[] + searchSession.indexingPlanFilter( + ctx -> ctx.include( Object.class ) // <4> + ); + // end::session-filter-exclude-include-all-session[] + + entityManager.persist( new EntityA( 10, "test" ) ); + entityManager.persist( new EntityExtendsA1( 20, "test" ) ); + entityManager.persist( new EntityExtendsA2( 30, "test" ) ); + } ); + + with( entityManagerFactory ).runInTransaction( entityManager -> { + // Nothing is getting indexed: + assertThat( Search.session( entityManager ).search( EntityA.class ) + .select( f -> f.id() ) + .where( f -> f.matchAll() ) + .fetchAllHits() + ).containsOnly( + 10, 20, 30 + ); + } ); + } + @Entity(name = EntityA.INDEX) @Indexed @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) diff --git a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/mapping/SearchMapping.java b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/mapping/SearchMapping.java index cb4567bfd8a..7b2a0a46d8b 100644 --- a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/mapping/SearchMapping.java +++ b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/mapping/SearchMapping.java @@ -123,7 +123,9 @@ default SearchScope scope(Class expectedSuperType, String entityName) Backend backend(String backendName); /** - * Set a filter defining which types must be included/excluded when indexed within the current application. + * Set a filter defining which types must be included/excluded when indexed within indexing plans (either automatically or manually). + *

+ * This does not affect indexing that does not rely on indexing plans, like the mass indexer. *

* By default, all indexed types are included. * diff --git a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/SearchSession.java b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/SearchSession.java index 8486f2a504f..7788a605f19 100644 --- a/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/SearchSession.java +++ b/mapper/orm/src/main/java/org/hibernate/search/mapper/orm/session/SearchSession.java @@ -250,8 +250,13 @@ default SearchScope scope(Class expectedSuperType, String entityName) void indexingPlanSynchronizationStrategy(IndexingPlanSynchronizationStrategy synchronizationStrategy); /** - * Set a filter configuration and define which types must be included/excluded when indexed within the current session. - * If the type is not explicitly included/excluded directly or as a supertype the decision will be made by + * Set a filter configuration and define which types must be included/excluded when indexed within indexing plans + * of the current session (either automatically or manually). + *

+ * This does not affect indexing that does not rely on indexing plans, like the mass indexer. + *

+ * If a type is not explicitly included/excluded directly or through an included/excluded supertype, + * the decision will be made by * {@link SearchMapping#indexingPlanFilter(SearchIndexingPlanFilter) an application filter}, which defaults to including all types. * * @param filter The filter that includes/excludes types when indexed. diff --git a/mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/work/SearchIndexingPlanFilterContext.java b/mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/work/SearchIndexingPlanFilterContext.java index 05c93b6a028..8023edf6208 100644 --- a/mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/work/SearchIndexingPlanFilterContext.java +++ b/mapper/pojo-base/src/main/java/org/hibernate/search/mapper/pojo/work/SearchIndexingPlanFilterContext.java @@ -10,22 +10,26 @@ import org.hibernate.search.util.common.annotation.Incubating; /** - * A context that helps with the indexing filter configuration. + * A context that helps with the indexing plan filter configuration. *

- * Note that only indexed types can be passed to this context's methods. Passing any other types will lead to an - * exception being thrown. Methods only accept classes and not interfaces. + * Note that only indexed and contained types with their supertypes can be passed to this context's methods. + * Methods that accept {@link Class classes} as their parameters will not allow interfaces, even if they are among supertypes. + * Passing any other types will lead to an exception being thrown. + *

+ * {@code Object.class} being a supertype to other types can be passed to context methods and will result in including/excluding all types + * with possible further fine-tuning using more specific types. *

* Include/exclude rules work as follows: *

    - *
  • If the type {@code A} is explicitly included by the filter, then a change to an object that is exactly of an indexed type {@code A} is processed.
  • - *
  • If the type {@code A} is explicitly excluded by the filter, then a change to an object that is exactly of an indexed type {@code A} is ignored.
  • + *
  • If the type {@code A} is explicitly included by the filter, then a change to an object that is exactly of an type {@code A} is processed.
  • + *
  • If the type {@code A} is explicitly excluded by the filter, then a change to an object that is exactly of an type {@code A} is ignored.
  • *
  • - * If the type {@code A} is explicitly included by the filter, then a change to an object that is exactly of an indexed type {@code B}, + * If the type {@code A} is explicitly included by the filter, then a change to an object that is exactly of an type {@code B}, * which is a subtype of the type {@code A}, * is processed unless the filter explicitly excludes a more specific supertype of a type {@code B}. *
  • *
  • - * If the type {@code A} is excluded by the filter explicitly, then a change to an object that is exactly of an indexed type {@code B}, + * If the type {@code A} is excluded by the filter explicitly, then a change to an object that is exactly of an type {@code B}, * which is a subtype of the type {@code A}, * is ignored unless the filter explicitly includes a more specific supertype of a type {@code B}. *
  • @@ -37,9 +41,9 @@ public interface SearchIndexingPlanFilterContext { /** - * Specify an indexed type to include, along with (unless specified otherwise) all its subtypes. + * Specify a name of an indexed/contained type (or a name of its named supertype) to include, along with (unless specified otherwise) all its subtypes. * - * @param name The name of an indexed type. + * @param name The name of a named type to include according to include/exclude rules. * @return The same context, for chained calls. * * @see Indexed#index() @@ -47,17 +51,17 @@ public interface SearchIndexingPlanFilterContext { SearchIndexingPlanFilterContext include(String name); /** - * Specify an indexed type to include, along with (unless specified otherwise) all its subtypes. + * Specify an indexed/contained type (or its supertype class) to include, along with (unless specified otherwise) all its subtypes. * - * @param clazz The class of an indexed type. + * @param clazz The class to include according to include/exclude rules. * @return The same context, for chained calls. */ SearchIndexingPlanFilterContext include(Class clazz); /** - * Specify an indexed type to exclude, along with (unless specified otherwise) all its subtypes. + * Specify a name of an indexed/contained type (or a name of its named supertype) to exclude, along with (unless specified otherwise) all its subtypes. * - * @param name The name of an indexed type. + * @param name The name of a named type to exclude according to include/exclude rules. * @return The same context, for chained calls. * * @see Indexed#index() @@ -65,9 +69,9 @@ public interface SearchIndexingPlanFilterContext { SearchIndexingPlanFilterContext exclude(String name); /** - * Specify an indexed type to exclude, along with (unless specified otherwise) all its subtypes. + * Specify an indexed/contained type (or its supertype class) to exclude, along with (unless specified otherwise) all its subtypes. * - * @param clazz The class of an indexed type. + * @param clazz The class to exclude according to include/exclude rules. * @return The same context, for chained calls. */ SearchIndexingPlanFilterContext exclude(Class clazz);