From 6c553f2345597ddb5afc6d7615c93185efef35a2 Mon Sep 17 00:00:00 2001
From: aleksandrovpv
Date: Wed, 17 Jun 2026 15:23:13 +0400
Subject: [PATCH 1/2] Search: document attribute-level access control and
Search Strategy API #4200
release-3.0.adoc:
- New Features: "Search Access Control by Attributes"
- Breaking Changes: "Search" (attribute-level access, SearchStrategy API, SearchUtils removal)
search-api.adoc:
- "Access Control and Pagination" updated with the attribute-level step
search-in-ui.adoc + search-ex1:
- "Custom Search Strategies" updated to the new API: extend AbstractOpenSearchStrategy /
AbstractElasticSearchStrategy and build the query via the platform QueryConfigurer so that
the search scope and security policies are applied
Co-Authored-By: Claude Opus 4.8 (1M context)
---
.../CustomOpenSearchSearchStrategy.java | 29 ++++++++++++-------
content/modules/search/pages/search-api.adoc | 2 +-
.../modules/search/pages/search-in-ui.adoc | 22 ++++++++------
.../modules/whats-new/pages/release-3.0.adoc | 14 +++++++++
4 files changed, 47 insertions(+), 20 deletions(-)
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..39a2aab5 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) {
+ queryConfigurer.configureRequest( // <3>
+ requestContext,
+ (queryBuilder, scope) -> // <4>
+ queryBuilder.multiMatch(multiMatchQueryBuilder ->
+ multiMatchQueryBuilder
+ .fields(scope.getFieldList()) // <5>
+ .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..e91bfcd7 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. 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..8b74e87f 100644
--- a/content/modules/search/pages/search-in-ui.adoc
+++ b/content/modules/search/pages/search-in-ui.adoc
@@ -62,21 +62,25 @@ 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.
-
-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.
+* `AbstractOpenSearchStrategy` - if you use OpenSearch.
+* `AbstractElasticSearchStrategy` - if you use Elasticsearch.
[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> 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.
+<4> The lambda receives the platform-specific query builder and the resolved `IndexSearchRequestScope`.
+<5> 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..7fff0c67 100644
--- a/content/modules/whats-new/pages/release-3.0.adoc
+++ b/content/modules/whats-new/pages/release-3.0.adoc
@@ -123,6 +123,11 @@ In addition to application-wide sorting customization, developers can now custom
This approach keeps sorting rules close to the grid configuration. It is useful when sorting requirements are specific to a single view.
+[[search-attribute-access]]
+=== Search Access Control by Attributes
+
+xref:search:index.adoc[Search] now takes entity attribute permissions into account: the search query includes only the attributes that the current user is allowed to read according to their xref:security:resource-roles.adoc[resource roles], so matches in restricted attributes are no longer returned. See xref:search:search-api.adoc#security-and-pagination[Access Control and Pagination] for details.
+
[[breaking-changes]]
== Breaking Changes
@@ -279,6 +284,15 @@ 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
+
+* Full-text search now applies attribute read permissions from xref:security:resource-roles.adoc[resource roles] when building the query: only attributes that the current user is allowed to read are searched, so matches in restricted attributes are no longer returned. xref:security:row-level-roles.adoc[Row-level roles] are still applied to the found records. No migration is required; the behavior is applied automatically.
+
+* The search strategy API has changed. `SearchStrategy` is now generic (`SearchStrategy`) with a single `configureRequest(SearchRequestContext)` method, and `ElasticsearchSearchStrategy` / `OpenSearchSearchStrategy` extend `SearchStrategy`. The previous `configureRequest(SearchRequest.Builder, SearchContext)` method and the `SearchUtils` class have been removed. If you implement a custom search strategy, build the request in `configureRequest(SearchRequestContext)` using the platform-specific `ElasticSearchQueryConfigurer` / `OpenSearchQueryConfigurer`, which resolves the permitted search scope (indexes and fields) and applies security policies.
+
+See https://github.com/jmix-framework/jmix/issues/4200[#4200^] for more information.
+
[[removed-dependencies]]
=== Removed Dependencies
From 50d5437e0d428d3bc6363359706d5b92e4e0721f Mon Sep 17 00:00:00 2001
From: aleksandrovpv
Date: Thu, 25 Jun 2026 15:01:06 +0400
Subject: [PATCH 2/2] Search docs: address review, correct release-notes
versions #4200
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- search-in-ui.adoc + example: describe the configureRequest(SearchRequestContext)
method explicitly (review comment), with a dedicated callout
- release-3.0.adoc: attribute-level access is a 2.7 feature, not 3.0 — keep only the
3.0 removal of the API deprecated in 2.7 (#4866)
- search-api.adoc: mark the attribute-level behavior as "Since Jmix 2.7"
Co-Authored-By: Claude Opus 4.8 (1M context)
---
.../service/CustomOpenSearchSearchStrategy.java | 8 ++++----
content/modules/search/pages/search-api.adoc | 2 +-
content/modules/search/pages/search-in-ui.adoc | 9 ++++++---
content/modules/whats-new/pages/release-3.0.adoc | 15 +++++++--------
4 files changed, 18 insertions(+), 16 deletions(-)
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 39a2aab5..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
@@ -21,13 +21,13 @@ public String getName() {
}
@Override
- public void configureRequest(SearchRequestContext requestContext) {
- queryConfigurer.configureRequest( // <3>
+ public void configureRequest(SearchRequestContext requestContext) { // <3>
+ queryConfigurer.configureRequest( // <4>
requestContext,
- (queryBuilder, scope) -> // <4>
+ (queryBuilder, scope) -> // <5>
queryBuilder.multiMatch(multiMatchQueryBuilder ->
multiMatchQueryBuilder
- .fields(scope.getFieldList()) // <5>
+ .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 e91bfcd7..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. 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.
+* 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 8b74e87f..656bcd29 100644
--- a/content/modules/search/pages/search-in-ui.adoc
+++ b/content/modules/search/pages/search-in-ui.adoc
@@ -67,15 +67,18 @@ Additionally, you can create a custom search strategy. Create a Spring bean that
* `AbstractOpenSearchStrategy` - if you use OpenSearch.
* `AbstractElasticSearchStrategy` - if you use Elasticsearch.
+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> 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.
-<4> The lambda receives the platform-specific query builder and the resolved `IndexSearchRequestScope`.
-<5> Use `scope.getFieldList()` to search only the permitted fields.
+<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]
====
diff --git a/content/modules/whats-new/pages/release-3.0.adoc b/content/modules/whats-new/pages/release-3.0.adoc
index 7fff0c67..6fd0d3d9 100644
--- a/content/modules/whats-new/pages/release-3.0.adoc
+++ b/content/modules/whats-new/pages/release-3.0.adoc
@@ -123,11 +123,6 @@ In addition to application-wide sorting customization, developers can now custom
This approach keeps sorting rules close to the grid configuration. It is useful when sorting requirements are specific to a single view.
-[[search-attribute-access]]
-=== Search Access Control by Attributes
-
-xref:search:index.adoc[Search] now takes entity attribute permissions into account: the search query includes only the attributes that the current user is allowed to read according to their xref:security:resource-roles.adoc[resource roles], so matches in restricted attributes are no longer returned. See xref:search:search-api.adoc#security-and-pagination[Access Control and Pagination] for details.
-
[[breaking-changes]]
== Breaking Changes
@@ -287,11 +282,15 @@ See https://github.com/jmix-framework/jmix/issues/5262[#5262^] for more informat
[[search-breaking-changes]]
=== Search
-* Full-text search now applies attribute read permissions from xref:security:resource-roles.adoc[resource roles] when building the query: only attributes that the current user is allowed to read are searched, so matches in restricted attributes are no longer returned. xref:security:row-level-roles.adoc[Row-level roles] are still applied to the found records. No migration is required; the behavior is applied automatically.
+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.
-* The search strategy API has changed. `SearchStrategy` is now generic (`SearchStrategy`) with a single `configureRequest(SearchRequestContext)` method, and `ElasticsearchSearchStrategy` / `OpenSearchSearchStrategy` extend `SearchStrategy`. The previous `configureRequest(SearchRequest.Builder, SearchContext)` method and the `SearchUtils` class have been removed. If you implement a custom search strategy, build the request in `configureRequest(SearchRequestContext)` using the platform-specific `ElasticSearchQueryConfigurer` / `OpenSearchQueryConfigurer`, which resolves the permitted search scope (indexes and fields) and applies security policies.
+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/4200[#4200^] for more information.
+See https://github.com/jmix-framework/jmix/issues/4866[#4866^] for more information.
[[removed-dependencies]]
=== Removed Dependencies