diff --git a/content/modules/search/examples/search-ex1/src/main/java/com/company/demo/service/CustomOpenSearchSearchStrategy.java b/content/modules/search/examples/search-ex1/src/main/java/com/company/demo/service/CustomOpenSearchSearchStrategy.java index c96e81b3..b053a54c 100644 --- a/content/modules/search/examples/search-ex1/src/main/java/com/company/demo/service/CustomOpenSearchSearchStrategy.java +++ b/content/modules/search/examples/search-ex1/src/main/java/com/company/demo/service/CustomOpenSearchSearchStrategy.java @@ -1,27 +1,36 @@ package com.company.demo.service; import io.jmix.search.searching.SearchRequestContext; -import io.jmix.searchopensearch.searching.strategy.OpenSearchSearchStrategy; +import io.jmix.searchopensearch.searching.strategy.OpenSearchQueryConfigurer; +import io.jmix.searchopensearch.searching.strategy.impl.AbstractOpenSearchStrategy; +import org.opensearch.client.opensearch._types.query_dsl.Operator; import org.opensearch.client.opensearch.core.SearchRequest; import org.springframework.stereotype.Component; // tag::strategy[] @Component -public class CustomOpenSearchSearchStrategy implements OpenSearchSearchStrategy { +public class CustomOpenSearchSearchStrategy extends AbstractOpenSearchStrategy { + + public CustomOpenSearchSearchStrategy(OpenSearchQueryConfigurer queryConfigurer) { + super(queryConfigurer); // <1> + } @Override public String getName() { - return "CustomStrategy"; + return "CustomStrategy"; // <2> } @Override - public void configureRequest(SearchRequestContext searchRequestContext) { - //configure your request - searchRequestContext.getRequestBuilder().query(queryBuilder -> - queryBuilder.multiMatch(multiMatchQueryBuilder -> - multiMatchQueryBuilder.fields("*") - .query(searchRequestContext.getSearchContext().getSearchText()) - ) + public void configureRequest(SearchRequestContext requestContext) { // <3> + queryConfigurer.configureRequest( // <4> + requestContext, + (queryBuilder, scope) -> // <5> + queryBuilder.multiMatch(multiMatchQueryBuilder -> + multiMatchQueryBuilder + .fields(scope.getFieldList()) // <6> + .query(requestContext.getSearchContext().getEscapedSearchText()) + .operator(Operator.Or) + ) ); } } diff --git a/content/modules/search/pages/search-api.adoc b/content/modules/search/pages/search-api.adoc index b872cfd1..103271f5 100644 --- a/content/modules/search/pages/search-api.adoc +++ b/content/modules/search/pages/search-api.adoc @@ -97,7 +97,7 @@ The Search add-on provides an MBean with `jmix.search:type=EntityIndexing` objec Data access control is performed by the Search add-on in two steps: -* Pre-search - checks xref:security:resource-roles.adoc#entity-policy[entity policies] and excludes indexes related to forbidden entities. +* Pre-search - checks xref:security:resource-roles.adoc#entity-policy[entity policies] and excludes indexes related to forbidden entities. Since Jmix 2.7, xref:security:resource-roles.adoc#entity-attribute-policy[attribute policies] are applied as well: only entity attributes that the current user is allowed to read are included in the search query, so matches in restricted attributes are not returned. If the current user has no access to any of the requested entities, the search returns an empty result. * Post-search - checks if there are any xref:security:row-level-roles.adoc#policies[row-level policies] configured for found entities. If they exist, the found instances are reloaded to apply security policies. diff --git a/content/modules/search/pages/search-in-ui.adoc b/content/modules/search/pages/search-in-ui.adoc index b5bbaf30..656bcd29 100644 --- a/content/modules/search/pages/search-in-ui.adoc +++ b/content/modules/search/pages/search-in-ui.adoc @@ -62,21 +62,28 @@ include::example$/search-ex1/src/main/resources/application.properties[tags=defa [[custom-search-strategies]] === Custom Search Strategies -Additionally, you can create a custom search strategy. For that purpose, you need to create a new Spring bean implementing one of the platform-specific interfaces: +Additionally, you can create a custom search strategy. Create a Spring bean that extends one of the platform-specific base classes: -* `OpenSearchSearchStrategy` - if you use OpenSearch. -* `ElasticsearchSearchStrategy` - if you use Elasticsearch. +* `AbstractOpenSearchStrategy` - if you use OpenSearch. +* `AbstractElasticSearchStrategy` - if you use Elasticsearch. -Then you need to implement 2 methods: - -* `String getName()` - should return a unique strategy name. - -* `void configureRequest(SearchRequest.Builder requestBuilder, SearchContext searchContext)` - configure your search request using the provided builder according to your requirements. +Implement `getName()` and `configureRequest(SearchRequestContext)`: [source,java,indent=0] ---- include::example$/search-ex1/src/main/java/com/company/demo/service/CustomOpenSearchSearchStrategy.java[tags=strategy] ---- +<1> The base class is initialized with the platform-specific query configurer (`OpenSearchQueryConfigurer` or `ElasticSearchQueryConfigurer`), available as the `queryConfigurer` field. +<2> `getName()` returns a unique strategy name used to select the strategy. +<3> `configureRequest(SearchRequestContext)` is the method the search engine invokes for the strategy — implement it to build the search request. +<4> Build the query through `queryConfigurer.configureRequest()`. The configurer resolves the search scope - the target indexes and the fields the current user is permitted to read - and applies security policies. +<5> The lambda receives the platform-specific query builder and the resolved `IndexSearchRequestScope`. +<6> Use `scope.getFieldList()` to search only the permitted fields. + +[NOTE] +==== +Always build the query through `queryConfigurer`. If you configure the request builder directly (for example, with `fields("*")`), the search scope is not resolved and xref:security:resource-roles.adoc#entity-attribute-policy[attribute policies] are not applied, which can make the search return no results. +==== After that, you can assign your custom strategy to the xref:search-field-component.adoc[SearchField] or xref:full-text-filter-component.adoc[FullTextFilter] component using the strategy name. diff --git a/content/modules/whats-new/pages/release-3.0.adoc b/content/modules/whats-new/pages/release-3.0.adoc index 8d0e7e5a..6fd0d3d9 100644 --- a/content/modules/whats-new/pages/release-3.0.adoc +++ b/content/modules/whats-new/pages/release-3.0.adoc @@ -279,6 +279,19 @@ Getter return types changed in the following properties: See https://github.com/jmix-framework/jmix/issues/5262[#5262^] for more information. +[[search-breaking-changes]] +=== Search + +The search strategy API and helper classes deprecated in 2.7 have been removed: + +* The `configureRequest(SearchRequest.Builder, SearchContext)` method of `ElasticsearchSearchStrategy` / `OpenSearchSearchStrategy`. Implement `configureRequest(SearchRequestContext)` instead and build the query through the platform-specific `ElasticSearchQueryConfigurer` / `OpenSearchQueryConfigurer`. + +* The `SearchUtils` class. Use `SearchRequestScopeProvider`, `SearchFieldsProvider`, and `SearchSecurityDecorator` instead. + +If you implemented a custom search strategy, migrate it to the new API — see xref:search:search-in-ui.adoc#custom-search-strategies[Custom Search Strategies]. + +See https://github.com/jmix-framework/jmix/issues/4866[#4866^] for more information. + [[removed-dependencies]] === Removed Dependencies