You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2021/12/21 09:59:09 UTC

[syncope] branch master updated: Upgrading Elasticsearch (#298)

This is an automated email from the ASF dual-hosted git repository.

ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git


The following commit(s) were added to refs/heads/master by this push:
     new 4880e0d  Upgrading Elasticsearch (#298)
4880e0d is described below

commit 4880e0dd52ab243c1a56e41bb2d7a6ff2dd6e37d
Author: Francesco Chicchiriccò <il...@users.noreply.github.com>
AuthorDate: Tue Dec 21 10:57:18 2021 +0100

    Upgrading Elasticsearch (#298)
---
 ext/elasticsearch/client-elasticsearch/pom.xml     |  18 +-
 .../client/ElasticsearchClientContext.java         |   5 +-
 .../client/ElasticsearchClientFactoryBean.java     |  32 +-
 .../client/ElasticsearchIndexManager.java          | 177 +++++-----
 .../elasticsearch/client/ElasticsearchUtils.java   | 118 +++----
 ext/elasticsearch/persistence-jpa/pom.xml          |   5 +
 .../jpa/ElasticsearchPersistenceContext.java       |   4 +-
 .../jpa/dao/ElasticsearchAnySearchDAO.java         | 377 ++++++++++++---------
 .../jpa/dao/ElasticsearchAnySearchDAOTest.java     |  58 ++--
 ext/elasticsearch/provisioning-java/pom.xml        |   2 +-
 .../java/job/ElasticsearchReindex.java             |  52 +--
 .../syncope/fit/core/NotificationTaskITCase.java   |   6 +-
 pom.xml                                            |  30 +-
 13 files changed, 459 insertions(+), 425 deletions(-)

diff --git a/ext/elasticsearch/client-elasticsearch/pom.xml b/ext/elasticsearch/client-elasticsearch/pom.xml
index d7bd416..10bf6c8 100644
--- a/ext/elasticsearch/client-elasticsearch/pom.xml
+++ b/ext/elasticsearch/client-elasticsearch/pom.xml
@@ -45,25 +45,21 @@ under the License.
     </dependency>
       
     <dependency>
-      <groupId>org.elasticsearch.client</groupId>
-      <artifactId>elasticsearch-rest-high-level-client</artifactId>
+      <groupId>co.elastic.clients</groupId>
+      <artifactId>elasticsearch-java</artifactId>
     </dependency>
     <dependency>
       <groupId>org.elasticsearch.client</groupId>
       <artifactId>elasticsearch-rest-client</artifactId>
     </dependency>
     <dependency>
-      <groupId>org.elasticsearch</groupId>
-      <artifactId>elasticsearch</artifactId>
-    </dependency>
-    
-    <dependency>
-      <groupId>com.fasterxml.jackson.dataformat</groupId>
-      <artifactId>jackson-dataformat-smile</artifactId>
+      <groupId>jakarta.json</groupId>
+      <artifactId>jakarta.json-api</artifactId>
     </dependency>
+
     <dependency>
-      <groupId>com.fasterxml.jackson.dataformat</groupId>
-      <artifactId>jackson-dataformat-cbor</artifactId>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
     </dependency>
   </dependencies>
 
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientContext.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientContext.java
index 7e37d50..dbacb5e 100644
--- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientContext.java
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientContext.java
@@ -18,13 +18,12 @@
  */
 package org.apache.syncope.ext.elasticsearch.client;
 
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
 import java.util.List;
-
 import org.apache.http.HttpHost;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
 import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.elasticsearch.client.RestHighLevelClient;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
@@ -61,7 +60,7 @@ public class ElasticsearchClientContext {
     @Bean
     @Autowired
     public ElasticsearchIndexManager elasticsearchIndexManager(
-            final RestHighLevelClient client,
+            final ElasticsearchClient client,
             final ElasticsearchUtils elasticsearchUtils) {
 
         return new ElasticsearchIndexManager(client, elasticsearchUtils);
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java
index 39246a9..1a6aae4 100644
--- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchClientFactoryBean.java
@@ -18,6 +18,9 @@
  */
 package org.apache.syncope.ext.elasticsearch.client;
 
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.json.jackson.JacksonJsonpMapper;
+import co.elastic.clients.transport.rest_client.RestClientTransport;
 import java.nio.charset.StandardCharsets;
 import java.util.Base64;
 import java.util.List;
@@ -31,14 +34,13 @@ import org.apache.http.impl.client.BasicCredentialsProvider;
 import org.apache.http.message.BasicHeader;
 import org.elasticsearch.client.RestClient;
 import org.elasticsearch.client.RestClientBuilder;
-import org.elasticsearch.client.RestHighLevelClient;
 import org.springframework.beans.factory.DisposableBean;
 import org.springframework.beans.factory.FactoryBean;
 
 /**
- * Spring {@link FactoryBean} for getting the Elasticsearch's {@link RestHighLevelClient} singleton instance.
+ * Spring {@link FactoryBean} for getting the {@link ElasticsearchClient} singleton instance.
  */
-public class ElasticsearchClientFactoryBean implements FactoryBean<RestHighLevelClient>, DisposableBean {
+public class ElasticsearchClientFactoryBean implements FactoryBean<ElasticsearchClient>, DisposableBean {
 
     private final List<HttpHost> hosts;
 
@@ -52,7 +54,9 @@ public class ElasticsearchClientFactoryBean implements FactoryBean<RestHighLevel
 
     private String apiKeySecret;
 
-    private RestHighLevelClient client;
+    private RestClient restClient;
+
+    private ElasticsearchClient client;
 
     public ElasticsearchClientFactoryBean(final List<HttpHost> hosts) {
         this.hosts = hosts;
@@ -91,25 +95,27 @@ public class ElasticsearchClientFactoryBean implements FactoryBean<RestHighLevel
     }
 
     @Override
-    public RestHighLevelClient getObject() throws Exception {
+    public ElasticsearchClient getObject() throws Exception {
         synchronized (this) {
             if (client == null) {
-                RestClientBuilder restClient = RestClient.builder(hosts.toArray(new HttpHost[0]));
+                RestClientBuilder builder = RestClient.builder(hosts.toArray(HttpHost[]::new));
                 if (username != null && password != null) {
                     CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                     credentialsProvider.setCredentials(
                             AuthScope.ANY, new UsernamePasswordCredentials(username, password));
-                    restClient.setHttpClientConfigCallback(b -> b.setDefaultCredentialsProvider(credentialsProvider));
+                    builder.setHttpClientConfigCallback(b -> b.setDefaultCredentialsProvider(credentialsProvider));
                 } else if (serviceToken != null) {
-                    restClient.setDefaultHeaders(
+                    builder.setDefaultHeaders(
                             new Header[] { new BasicHeader(HttpHeaders.AUTHORIZATION, "Bearer " + serviceToken) });
                 } else if (apiKeyId != null && apiKeySecret != null) {
                     String apiKeyAuth = Base64.getEncoder().encodeToString(
                             (apiKeyId + ":" + apiKeySecret).getBytes(StandardCharsets.UTF_8));
-                    restClient.setDefaultHeaders(
+                    builder.setDefaultHeaders(
                             new Header[] { new BasicHeader(HttpHeaders.AUTHORIZATION, "ApiKey " + apiKeyAuth) });
                 }
-                client = new RestHighLevelClient(restClient);
+
+                restClient = builder.build();
+                client = new ElasticsearchClient(new RestClientTransport(restClient, new JacksonJsonpMapper()));
             }
         }
         return client;
@@ -117,13 +123,13 @@ public class ElasticsearchClientFactoryBean implements FactoryBean<RestHighLevel
 
     @Override
     public Class<?> getObjectType() {
-        return RestHighLevelClient.class;
+        return ElasticsearchClient.class;
     }
 
     @Override
     public void destroy() throws Exception {
-        if (client != null) {
-            client.close();
+        if (restClient != null) {
+            restClient.close();
         }
     }
 }
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
index b313dca..6aaef95 100644
--- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchIndexManager.java
@@ -18,30 +18,32 @@
  */
 package org.apache.syncope.ext.elasticsearch.client;
 
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.ElasticsearchException;
+import co.elastic.clients.elasticsearch._types.analysis.CustomNormalizer;
+import co.elastic.clients.elasticsearch._types.analysis.Normalizer;
+import co.elastic.clients.elasticsearch._types.mapping.DynamicTemplate;
+import co.elastic.clients.elasticsearch._types.mapping.KeywordProperty;
+import co.elastic.clients.elasticsearch._types.mapping.Property;
+import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
+import co.elastic.clients.elasticsearch.core.DeleteRequest;
+import co.elastic.clients.elasticsearch.core.DeleteResponse;
+import co.elastic.clients.elasticsearch.core.IndexRequest;
+import co.elastic.clients.elasticsearch.core.IndexResponse;
+import co.elastic.clients.elasticsearch.indices.CreateIndexRequest;
+import co.elastic.clients.elasticsearch.indices.CreateIndexResponse;
+import co.elastic.clients.elasticsearch.indices.DeleteIndexRequest;
+import co.elastic.clients.elasticsearch.indices.DeleteIndexResponse;
+import co.elastic.clients.elasticsearch.indices.IndexSettings;
+import co.elastic.clients.elasticsearch.indices.IndexSettingsAnalysis;
 import java.io.IOException;
+import java.util.List;
+import java.util.Map;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.persistence.api.entity.Any;
 import org.apache.syncope.core.provisioning.api.event.AnyCreatedUpdatedEvent;
 import org.apache.syncope.core.provisioning.api.event.AnyDeletedEvent;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
-import org.elasticsearch.ElasticsearchStatusException;
-import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
-import org.elasticsearch.action.delete.DeleteRequest;
-import org.elasticsearch.action.delete.DeleteResponse;
-import org.elasticsearch.action.get.GetRequest;
-import org.elasticsearch.action.get.GetResponse;
-import org.elasticsearch.action.index.IndexRequest;
-import org.elasticsearch.action.index.IndexResponse;
-import org.elasticsearch.action.support.master.AcknowledgedResponse;
-import org.elasticsearch.action.update.UpdateRequest;
-import org.elasticsearch.action.update.UpdateResponse;
-import org.elasticsearch.client.RequestOptions;
-import org.elasticsearch.client.RestHighLevelClient;
-import org.elasticsearch.client.indices.CreateIndexRequest;
-import org.elasticsearch.client.indices.CreateIndexResponse;
-import org.elasticsearch.client.indices.GetIndexRequest;
-import org.elasticsearch.common.xcontent.XContentBuilder;
-import org.elasticsearch.common.xcontent.XContentFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.transaction.event.TransactionalEventListener;
@@ -53,12 +55,12 @@ public class ElasticsearchIndexManager {
 
     private static final Logger LOG = LoggerFactory.getLogger(ElasticsearchIndexManager.class);
 
-    protected final RestHighLevelClient client;
+    protected final ElasticsearchClient client;
 
     protected final ElasticsearchUtils elasticsearchUtils;
 
     public ElasticsearchIndexManager(
-            final RestHighLevelClient client,
+            final ElasticsearchClient client,
             final ElasticsearchUtils elasticsearchUtils) {
 
         this.client = client;
@@ -67,127 +69,104 @@ public class ElasticsearchIndexManager {
 
     public boolean existsIndex(final String domain, final AnyTypeKind kind) throws IOException {
         return client.indices().exists(
-                new GetIndexRequest(ElasticsearchUtils.getContextDomainName(domain, kind)), RequestOptions.DEFAULT);
+                new co.elastic.clients.elasticsearch.indices.ExistsRequest.Builder().
+                        index(ElasticsearchUtils.getContextDomainName(domain, kind)).build()).
+                value();
     }
 
-    public XContentBuilder defaultSettings() throws IOException {
-        return XContentFactory.jsonBuilder().
-                startObject().
-                startObject("analysis").
-                startObject("normalizer").
-                startObject("string_lowercase").
-                field("type", "custom").
-                field("char_filter", new Object[0]).
-                field("filter").
-                startArray().
-                value("lowercase").
-                endArray().
-                endObject().
-                endObject().
-                endObject().
-                startObject("index").
-                field("number_of_shards", elasticsearchUtils.getNumberOfShards()).
-                field("number_of_replicas", elasticsearchUtils.getNumberOfReplicas()).
-                endObject().
-                endObject();
+    public IndexSettings defaultSettings() throws IOException {
+        return new IndexSettings.Builder().
+                analysis(new IndexSettingsAnalysis.Builder().
+                        normalizer("string_lowercase", new Normalizer.Builder().
+                                custom(new CustomNormalizer.Builder().
+                                        charFilter(List.of()).
+                                        filter("lowercase").
+                                        build()).
+                                build()).
+                        build()).
+                numberOfShards(elasticsearchUtils.getNumberOfShards()).
+                numberOfReplicas(elasticsearchUtils.getNumberOfReplicas()).
+                build();
     }
 
-    public XContentBuilder defaultMapping() throws IOException {
-        return XContentFactory.jsonBuilder().
-                startObject().
-                startArray("dynamic_templates").
-                startObject().
-                startObject("strings").
-                field("match_mapping_type", "string").
-                startObject("mapping").
-                field("type", "keyword").
-                field("normalizer", "string_lowercase").
-                endObject().
-                endObject().
-                endObject().
-                endArray().
-                endObject();
+    public TypeMapping defaultMapping() throws IOException {
+        return new TypeMapping.Builder().
+                dynamicTemplates(List.of(Map.of(
+                        "strings",
+                        new DynamicTemplate.Builder().
+                                matchMappingType("string").
+                                mapping(new Property.Builder().
+                                        keyword(new KeywordProperty.Builder().normalizer("string_lowercase").build()).
+                                        build()).
+                                build()))).
+                build();
     }
 
     protected CreateIndexResponse doCreateIndex(
             final String domain,
             final AnyTypeKind kind,
-            final XContentBuilder settings,
-            final XContentBuilder mapping) throws IOException {
+            final IndexSettings settings,
+            final TypeMapping mappings) throws IOException {
 
         return client.indices().create(
-                new CreateIndexRequest(ElasticsearchUtils.getContextDomainName(domain, kind)).
+                new CreateIndexRequest.Builder().
+                        index(ElasticsearchUtils.getContextDomainName(domain, kind)).
                         settings(settings).
-                        mapping(mapping), RequestOptions.DEFAULT);
+                        mappings(mappings).
+                        build());
     }
 
     public void createIndex(
             final String domain,
             final AnyTypeKind kind,
-            final XContentBuilder settings,
-            final XContentBuilder mapping)
+            final IndexSettings settings,
+            final TypeMapping mappings)
             throws IOException {
 
         try {
-            CreateIndexResponse response = doCreateIndex(domain, kind, settings, mapping);
+            CreateIndexResponse response = doCreateIndex(domain, kind, settings, mappings);
 
             LOG.debug("Successfully created {} for {}: {}",
                     ElasticsearchUtils.getContextDomainName(domain, kind), kind.name(), response);
-        } catch (ElasticsearchStatusException e) {
+        } catch (ElasticsearchException e) {
             LOG.debug("Could not create index {} because it already exists",
                     ElasticsearchUtils.getContextDomainName(domain, kind), e);
 
             removeIndex(domain, kind);
-            doCreateIndex(domain, kind, settings, mapping);
+            doCreateIndex(domain, kind, settings, mappings);
         }
     }
 
     public void removeIndex(final String domain, final AnyTypeKind kind) throws IOException {
-        AcknowledgedResponse acknowledgedResponse = client.indices().delete(
-                new DeleteIndexRequest(ElasticsearchUtils.getContextDomainName(domain, kind)), RequestOptions.DEFAULT);
+        DeleteIndexResponse response = client.indices().delete(
+                new DeleteIndexRequest.Builder().index(ElasticsearchUtils.getContextDomainName(domain, kind)).build());
         LOG.debug("Successfully removed {}: {}",
-                ElasticsearchUtils.getContextDomainName(domain, kind), acknowledgedResponse);
+                ElasticsearchUtils.getContextDomainName(domain, kind), response);
     }
 
     @TransactionalEventListener
     public void after(final AnyCreatedUpdatedEvent<Any<?>> event) throws IOException {
-        GetRequest getRequest = new GetRequest(
-                ElasticsearchUtils.getContextDomainName(
-                        AuthContextUtils.getDomain(), event.getAny().getType().getKind()),
-                event.getAny().getKey());
-        GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
-        if (getResponse.isExists()) {
-            LOG.debug("About to update index for {}", event.getAny());
-
-            UpdateRequest request = new UpdateRequest(
-                    ElasticsearchUtils.getContextDomainName(
-                            AuthContextUtils.getDomain(), event.getAny().getType().getKind()),
-                    event.getAny().getKey()).
-                    retryOnConflict(elasticsearchUtils.getRetryOnConflict()).
-                    doc(elasticsearchUtils.builder(event.getAny(), event.getDomain()));
-            UpdateResponse response = client.update(request, RequestOptions.DEFAULT);
-            LOG.debug("Index successfully updated for {}: {}", event.getAny(), response);
-        } else {
-            LOG.debug("About to create index for {}", event.getAny());
-
-            IndexRequest request = new IndexRequest(
-                    ElasticsearchUtils.getContextDomainName(
-                            AuthContextUtils.getDomain(), event.getAny().getType().getKind())).
-                    id(event.getAny().getKey()).
-                    source(elasticsearchUtils.builder(event.getAny(), event.getDomain()));
-            IndexResponse response = client.index(request, RequestOptions.DEFAULT);
-            LOG.debug("Index successfully created for {}: {}", event.getAny(), response);
-        }
+        String index = ElasticsearchUtils.getContextDomainName(
+                AuthContextUtils.getDomain(), event.getAny().getType().getKind());
+
+        IndexRequest<Map<String, Object>> request = new IndexRequest.Builder<Map<String, Object>>().
+                index(index).
+                id(event.getAny().getKey()).
+                document(elasticsearchUtils.document(event.getAny(), event.getDomain())).
+                build();
+        IndexResponse response = client.index(request);
+        LOG.debug("Index successfully created or updated for {}: {}", event.getAny(), response);
     }
 
     @TransactionalEventListener
     public void after(final AnyDeletedEvent event) throws IOException {
         LOG.debug("About to delete index for {}[{}]", event.getAnyTypeKind(), event.getAnyKey());
 
-        DeleteRequest request = new DeleteRequest(
-                ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), event.getAnyTypeKind()),
-                event.getAnyKey());
-        DeleteResponse response = client.delete(request, RequestOptions.DEFAULT);
+        DeleteRequest request = new DeleteRequest.Builder().index(
+                ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), event.getAnyTypeKind())).
+                id(event.getAnyKey()).
+                build();
+        DeleteResponse response = client.delete(request);
         LOG.debug("Index successfully deleted for {}[{}]: {}",
                 event.getAnyTypeKind(), event.getAnyKey(), response);
     }
diff --git a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
index 8ea8a24..54d2385 100644
--- a/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
+++ b/ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java
@@ -21,8 +21,10 @@ package org.apache.syncope.ext.elasticsearch.client;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
@@ -37,8 +39,6 @@ import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
 import org.apache.syncope.core.persistence.api.entity.group.Group;
 import org.apache.syncope.core.persistence.api.entity.user.User;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
-import org.elasticsearch.common.xcontent.XContentBuilder;
-import org.elasticsearch.common.xcontent.XContentFactory;
 import org.springframework.transaction.annotation.Transactional;
 
 /**
@@ -60,9 +60,9 @@ public class ElasticsearchUtils {
 
     protected int retryOnConflict = 5;
 
-    protected int numberOfShards = 1;
+    protected String numberOfShards = "1";
 
-    protected int numberOfReplicas = 1;
+    protected String numberOfReplicas = "1";
 
     public ElasticsearchUtils(final UserDAO userDAO, final GroupDAO groupDAO, final AnyObjectDAO anyObjectDAO) {
         this.userDAO = userDAO;
@@ -86,32 +86,32 @@ public class ElasticsearchUtils {
         return retryOnConflict;
     }
 
-    public int getNumberOfShards() {
+    public String getNumberOfShards() {
         return numberOfShards;
     }
 
     public void setNumberOfShards(final int numberOfShards) {
-        this.numberOfShards = numberOfShards;
+        this.numberOfShards = String.valueOf(numberOfShards);
     }
 
-    public int getNumberOfReplicas() {
+    public String getNumberOfReplicas() {
         return numberOfReplicas;
     }
 
     public void setNumberOfReplicas(final int numberOfReplicas) {
-        this.numberOfReplicas = numberOfReplicas;
+        this.numberOfReplicas = String.valueOf(numberOfReplicas);
     }
 
     /**
-     * Returns the builder specialized with content from the provided any.
+     * Returns the document specialized with content from the provided any.
      *
      * @param any user, group or any object to index
      * @param domain tenant information
-     * @return builder specialized with content from the provided any
+     * @return document specialized with content from the provided any
      * @throws IOException in case of errors
      */
     @Transactional
-    public XContentBuilder builder(final Any<?> any, final String domain) throws IOException {
+    public Map<String, Object> document(final Any<?> any, final String domain) throws IOException {
         Set<String> resources = new HashSet<>();
         List<String> dynRealms = new ArrayList<>();
         AuthContextUtils.callAsAdmin(domain, () -> {
@@ -128,28 +128,27 @@ public class ElasticsearchUtils {
             return null;
         });
 
-        XContentBuilder builder = XContentFactory.jsonBuilder().
-                startObject().
-                field("id", any.getKey()).
-                field("realm", any.getRealm().getFullPath()).
-                field("anyType", any.getType().getKey()).
-                field("creationDate", any.getCreationDate()).
-                field("creationContext", any.getCreationContext()).
-                field("creator", any.getCreator()).
-                field("lastChangeDate", any.getLastChangeDate()).
-                field("lastModifier", any.getLastModifier()).
-                field("lastChangeContext", any.getLastChangeContext()).
-                field("status", any.getStatus()).
-                field("resources", resources).
-                field("dynRealms", dynRealms);
+        Map<String, Object> builder = new HashMap<>();
+        builder.put("id", any.getKey());
+        builder.put("realm", any.getRealm().getFullPath());
+        builder.put("anyType", any.getType().getKey());
+        builder.put("creationDate", any.getCreationDate());
+        builder.put("creationContext", any.getCreationContext());
+        builder.put("creator", any.getCreator());
+        builder.put("lastChangeDate", any.getLastChangeDate());
+        builder.put("lastModifier", any.getLastModifier());
+        builder.put("lastChangeContext", any.getLastChangeContext());
+        builder.put("status", any.getStatus());
+        builder.put("resources", resources);
+        builder.put("dynRealms", dynRealms);
 
         if (any instanceof AnyObject) {
             AnyObject anyObject = ((AnyObject) any);
-            builder = builder.field("name", anyObject.getName());
+            builder.put("name", anyObject.getName());
 
             Collection<String> memberships = AuthContextUtils.callAsAdmin(
                     domain, () -> anyObjectDAO.findAllGroupKeys(anyObject));
-            builder = builder.field("memberships", memberships);
+            builder.put("memberships", memberships);
 
             List<String> relationships = new ArrayList<>();
             List<String> relationshipTypes = new ArrayList<>();
@@ -160,18 +159,18 @@ public class ElasticsearchUtils {
                 });
                 return null;
             });
-            builder = builder.field("relationships", relationships);
-            builder = builder.field("relationshipTypes", relationshipTypes);
+            builder.put("relationships", relationships);
+            builder.put("relationshipTypes", relationshipTypes);
 
-            builder = customizeBuilder(builder, anyObject, domain);
+            ElasticsearchUtils.this.customizeDocument(builder, anyObject, domain);
         } else if (any instanceof Group) {
             Group group = ((Group) any);
-            builder = builder.field("name", group.getName());
+            builder.put("name", group.getName());
             if (group.getUserOwner() != null) {
-                builder = builder.field("userOwner", group.getUserOwner().getKey());
+                builder.put("userOwner", group.getUserOwner().getKey());
             }
             if (group.getGroupOwner() != null) {
-                builder = builder.field("groupOwner", group.getGroupOwner().getKey());
+                builder.put("groupOwner", group.getGroupOwner().getKey());
             }
 
             Set<String> members = new HashSet<>();
@@ -184,20 +183,19 @@ public class ElasticsearchUtils {
                 members.addAll(groupDAO.findADynMembers(group));
                 return null;
             });
-            builder = builder.field("members", members);
+            builder.put("members", members);
 
-            builder = customizeBuilder(builder, group, domain);
+            ElasticsearchUtils.this.customizeDocument(builder, group, domain);
         } else if (any instanceof User) {
             User user = ((User) any);
-            builder = builder.
-                    field("username", user.getUsername()).
-                    field("token", user.getToken()).
-                    field("tokenExpireTime", user.getTokenExpireTime()).
-                    field("changePwdDate", user.getChangePwdDate()).
-                    field("failedLogins", user.getFailedLogins()).
-                    field("lastLoginDate", user.getLastLoginDate()).
-                    field("suspended", user.isSuspended()).
-                    field("mustChangePassword", user.isMustChangePassword());
+            builder.put("username", user.getUsername());
+            builder.put("token", user.getToken());
+            builder.put("tokenExpireTime", user.getTokenExpireTime());
+            builder.put("changePwdDate", user.getChangePwdDate());
+            builder.put("failedLogins", user.getFailedLogins());
+            builder.put("lastLoginDate", user.getLastLoginDate());
+            builder.put("suspended", user.isSuspended());
+            builder.put("mustChangePassword", user.isMustChangePassword());
 
             List<String> roles = new ArrayList<>();
             Set<String> privileges = new HashSet<>();
@@ -208,12 +206,12 @@ public class ElasticsearchUtils {
                 });
                 return null;
             });
-            builder = builder.field("roles", roles);
-            builder = builder.field("privileges", privileges);
+            builder.put("roles", roles);
+            builder.put("privileges", privileges);
 
             Collection<String> memberships = AuthContextUtils.callAsAdmin(
                     domain, () -> userDAO.findAllGroupKeys(user));
-            builder = builder.field("memberships", memberships);
+            builder.put("memberships", memberships);
 
             List<String> relationships = new ArrayList<>();
             Set<String> relationshipTypes = new HashSet<>();
@@ -221,10 +219,10 @@ public class ElasticsearchUtils {
                 relationships.add(relationship.getRightEnd().getKey());
                 relationshipTypes.add(relationship.getType().getKey());
             });
-            builder = builder.field("relationships", relationships);
-            builder = builder.field("relationshipTypes", relationshipTypes);
+            builder.put("relationships", relationships);
+            builder.put("relationshipTypes", relationshipTypes);
 
-            builder = customizeBuilder(builder, user, domain);
+            customizeDocument(builder, user, domain);
         }
 
         for (PlainAttr<?> plainAttr : any.getPlainAttrs()) {
@@ -235,29 +233,23 @@ public class ElasticsearchUtils {
                 values.add(plainAttr.getUniqueValue().getValue());
             }
 
-            builder = builder.field(plainAttr.getSchema().getKey(), values.size() == 1 ? values.get(0) : values);
+            builder.put(plainAttr.getSchema().getKey(), values.size() == 1 ? values.get(0) : values);
         }
 
-        return builder.endObject();
+        return builder;
     }
 
-    protected XContentBuilder customizeBuilder(
-            final XContentBuilder builder, final AnyObject anyObject, final String domain)
+    protected void customizeDocument(
+            final Map<String, Object> builder, final AnyObject anyObject, final String domain)
             throws IOException {
-
-        return builder;
     }
 
-    protected XContentBuilder customizeBuilder(
-            final XContentBuilder builder, final Group group, final String domain)
+    protected void customizeDocument(
+            final Map<String, Object> builder, final Group group, final String domain)
             throws IOException {
-
-        return builder;
     }
 
-    protected XContentBuilder customizeBuilder(final XContentBuilder builder, final User user, final String domain)
+    protected void customizeDocument(final Map<String, Object> builder, final User user, final String domain)
             throws IOException {
-
-        return builder;
     }
 }
diff --git a/ext/elasticsearch/persistence-jpa/pom.xml b/ext/elasticsearch/persistence-jpa/pom.xml
index b9dcfcd..3c935e8 100644
--- a/ext/elasticsearch/persistence-jpa/pom.xml
+++ b/ext/elasticsearch/persistence-jpa/pom.xml
@@ -66,6 +66,11 @@ under the License.
       <artifactId>mockito-junit-jupiter</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.assertj</groupId>
+      <artifactId>assertj-core</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
diff --git a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java
index a869507..ccc8421 100644
--- a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java
+++ b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/ElasticsearchPersistenceContext.java
@@ -30,7 +30,7 @@ import org.apache.syncope.core.persistence.api.entity.EntityFactory;
 import org.apache.syncope.core.persistence.jpa.dao.ElasticsearchAnySearchDAO;
 import org.apache.syncope.ext.elasticsearch.client.ElasticsearchIndexManager;
 import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils;
-import org.elasticsearch.client.RestHighLevelClient;
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.context.annotation.Bean;
@@ -59,7 +59,7 @@ public class ElasticsearchPersistenceContext {
             final PlainSchemaDAO schemaDAO,
             final EntityFactory entityFactory,
             final AnyUtilsFactory anyUtilsFactory,
-            final RestHighLevelClient client,
+            final ElasticsearchClient client,
             final @Lazy ElasticsearchUtils elasticsearchUtils) {
 
         return new ElasticsearchAnySearchDAO(
diff --git a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
index c2aaa9b..bd014ba 100644
--- a/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
+++ b/ext/elasticsearch/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAO.java
@@ -18,15 +18,29 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.FieldSort;
+import co.elastic.clients.elasticsearch._types.FieldValue;
+import co.elastic.clients.elasticsearch._types.SearchType;
+import co.elastic.clients.elasticsearch._types.SortOptions;
+import co.elastic.clients.elasticsearch._types.SortOrder;
+import co.elastic.clients.elasticsearch._types.query_dsl.MatchAllQuery;
+import co.elastic.clients.elasticsearch._types.query_dsl.MatchNoneQuery;
+import co.elastic.clients.elasticsearch._types.query_dsl.Query;
+import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
+import co.elastic.clients.elasticsearch.core.CountRequest;
+import co.elastic.clients.elasticsearch.core.SearchRequest;
+import co.elastic.clients.elasticsearch.core.search.Hit;
+import co.elastic.clients.json.JsonData;
 import java.io.IOException;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.commons.lang3.tuple.Triple;
@@ -67,30 +81,18 @@ import org.apache.syncope.core.persistence.api.entity.Realm;
 import org.apache.syncope.core.provisioning.api.utils.RealmUtils;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils;
-import org.elasticsearch.action.search.SearchRequest;
-import org.elasticsearch.action.search.SearchType;
-import org.elasticsearch.client.RequestOptions;
-import org.elasticsearch.client.RestHighLevelClient;
-import org.elasticsearch.client.core.CountRequest;
-import org.elasticsearch.index.query.DisMaxQueryBuilder;
-import org.elasticsearch.index.query.MatchAllQueryBuilder;
-import org.elasticsearch.index.query.MatchNoneQueryBuilder;
-import org.elasticsearch.index.query.QueryBuilder;
-import org.elasticsearch.index.query.QueryBuilders;
-import org.elasticsearch.search.SearchHit;
-import org.elasticsearch.search.builder.SearchSourceBuilder;
-import org.elasticsearch.search.sort.FieldSortBuilder;
-import org.elasticsearch.search.sort.SortBuilder;
-import org.elasticsearch.search.sort.SortOrder;
+import org.springframework.util.CollectionUtils;
 
 /**
  * Search engine implementation for users, groups and any objects, based on Elasticsearch.
  */
 public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
 
-    protected static final QueryBuilder MATCH_NONE_QUERY_BUILDER = new MatchNoneQueryBuilder();
+    protected static final Query MATCH_NONE_QUERY =
+            new Query.Builder().matchNone(new MatchNoneQuery.Builder().build()).build();
 
-    protected static final QueryBuilder MATCH_ALL_QUERY_BUILDER = new MatchAllQueryBuilder();
+    protected static final Query MATCH_ALL_QUERY =
+            new Query.Builder().matchAll(new MatchAllQuery.Builder().build()).build();
 
     protected static final char[] ELASTICSEARCH_REGEX_CHARS = new char[] {
         '.', '?', '+', '*', '|', '{', '}', '[', ']', '(', ')', '"', '\\', '&' };
@@ -107,7 +109,7 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
         return output.toString();
     }
 
-    protected final RestHighLevelClient client;
+    protected final ElasticsearchClient client;
 
     protected final ElasticsearchUtils elasticsearchUtils;
 
@@ -120,7 +122,7 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
             final PlainSchemaDAO schemaDAO,
             final EntityFactory entityFactory,
             final AnyUtilsFactory anyUtilsFactory,
-            final RestHighLevelClient client,
+            final ElasticsearchClient client,
             final ElasticsearchUtils elasticsearchUtils) {
 
         super(realmDAO, dynRealmDAO, userDAO, groupDAO, anyObjectDAO, schemaDAO, entityFactory, anyUtilsFactory);
@@ -128,13 +130,12 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
         this.elasticsearchUtils = elasticsearchUtils;
     }
 
-    protected Triple<Optional<QueryBuilder>, Set<String>, Set<String>> getAdminRealmsFilter(
+    protected Triple<Optional<Query>, Set<String>, Set<String>> getAdminRealmsFilter(
             final AnyTypeKind kind, final Set<String> adminRealms) {
 
-        DisMaxQueryBuilder builder = QueryBuilders.disMaxQuery();
-
         Set<String> dynRealmKeys = new HashSet<>();
         Set<String> groupOwners = new HashSet<>();
+        List<Query> queries = new ArrayList<>();
 
         adminRealms.forEach(realmPath -> {
             Optional<Pair<String, String>> goRealm = RealmUtils.parseGroupOwnerRealm(realmPath);
@@ -147,8 +148,10 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
                     noRealm.getElements().add("Invalid realm specified: " + realmPath);
                     throw noRealm;
                 } else {
-                    realmDAO.findDescendants(realm).forEach(
-                            descendant -> builder.add(QueryBuilders.termQuery("realm", descendant.getFullPath())));
+                    realmDAO.findDescendants(realm).forEach(descendant -> queries.add(
+                            new Query.Builder().term(QueryBuilders.term().
+                                    field("realm").value(FieldValue.of(descendant.getFullPath())).build()).
+                                    build()));
                 }
             } else {
                 DynRealm dynRealm = dynRealmDAO.find(realmPath);
@@ -156,59 +159,66 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
                     LOG.warn("Ignoring invalid dynamic realm {}", realmPath);
                 } else {
                     dynRealmKeys.add(dynRealm.getKey());
-                    builder.add(QueryBuilders.termQuery("dynRealm", dynRealm.getKey()));
+                    queries.add(new Query.Builder().term(QueryBuilders.term().
+                            field("dynRealm").value(FieldValue.of(dynRealm.getKey())).build()).
+                            build());
                 }
             }
         });
 
         return Triple.of(
-                dynRealmKeys.isEmpty() && groupOwners.isEmpty() ? Optional.of(builder) : Optional.empty(),
+                dynRealmKeys.isEmpty() && groupOwners.isEmpty()
+                ? Optional.of(new Query.Builder().disMax(QueryBuilders.disMax().queries(queries).build()).build())
+                : Optional.empty(),
                 dynRealmKeys,
                 groupOwners);
     }
 
-    protected QueryBuilder getQueryBuilder(
+    protected Query getQuery(
             final Set<String> adminRealms,
             final SearchCond cond,
             final AnyTypeKind kind) {
 
-        Triple<Optional<QueryBuilder>, Set<String>, Set<String>> filter = getAdminRealmsFilter(kind, adminRealms);
-        QueryBuilder queryBuilder;
+        Triple<Optional<Query>, Set<String>, Set<String>> filter = getAdminRealmsFilter(kind, adminRealms);
+        Query query;
         if (SyncopeConstants.FULL_ADMIN_REALMS.equals(adminRealms)) {
-            queryBuilder = getQueryBuilder(cond, kind);
+            query = getQuery(cond, kind);
         } else {
-            queryBuilder = getQueryBuilder(buildEffectiveCond(cond, filter.getMiddle(), filter.getRight(), kind), kind);
+            query = getQuery(buildEffectiveCond(cond, filter.getMiddle(), filter.getRight(), kind), kind);
 
             if (filter.getLeft().isPresent()) {
-                queryBuilder = QueryBuilders.boolQuery().
-                        must(filter.getLeft().get()).
-                        must(queryBuilder);
+                query = new Query.Builder().bool(
+                        QueryBuilders.bool().
+                                must(filter.getLeft().get()).
+                                must(query).build()).
+                        build();
             }
         }
 
-        return queryBuilder;
+        return query;
     }
 
     @Override
     protected int doCount(final Set<String> adminRealms, final SearchCond cond, final AnyTypeKind kind) {
-        CountRequest request = new CountRequest(
-                ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), kind)).
-                query(getQueryBuilder(adminRealms, cond, kind));
+        CountRequest request = new CountRequest.Builder().
+                index(ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), kind)).
+                query(getQuery(adminRealms, cond, kind)).
+                build();
         try {
-            return (int) client.count(request, RequestOptions.DEFAULT).getCount();
+            return (int) client.count(request).count();
         } catch (IOException e) {
             LOG.error("Search error", e);
             return 0;
         }
     }
 
-    protected List<SortBuilder<?>> sortBuilders(
+    protected List<SortOptions> sortBuilders(
             final AnyTypeKind kind,
             final List<OrderByClause> orderBy) {
 
         AnyUtils anyUtils = anyUtilsFactory.getInstance(kind);
 
-        List<SortBuilder<?>> builders = new ArrayList<>();
+        List<SortOptions> options = new ArrayList<>();
         orderBy.forEach(clause -> {
             String sortName = null;
 
@@ -228,10 +238,16 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
             if (sortName == null) {
                 LOG.warn("Cannot build any valid clause from {}", clause);
             } else {
-                builders.add(new FieldSortBuilder(sortName).order(SortOrder.valueOf(clause.getDirection().name())));
+                options.add(new SortOptions.Builder().field(
+                        new FieldSort.Builder().
+                                field(sortName).
+                                order(clause.getDirection() == OrderByClause.Direction.ASC
+                                        ? SortOrder.Asc : SortOrder.Desc).
+                                build()).
+                        build());
             }
         });
-        return builders;
+        return options;
     }
 
     @Override
@@ -243,229 +259,252 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
             final List<OrderByClause> orderBy,
             final AnyTypeKind kind) {
 
-        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().
-                query(getQueryBuilder(adminRealms, cond, kind)).
+        SearchRequest request = new SearchRequest.Builder().
+                index(ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), kind)).
+                searchType(SearchType.QueryThenFetch).
+                query(getQuery(adminRealms, cond, kind)).
                 from(itemsPerPage * (page <= 0 ? 0 : page - 1)).
-                size(itemsPerPage < 0 ? elasticsearchUtils.getIndexMaxResultWindow() : itemsPerPage);
-        sortBuilders(kind, orderBy).forEach(sourceBuilder::sort);
-
-        SearchRequest request = new SearchRequest(
-                ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), kind)).
-                searchType(SearchType.QUERY_THEN_FETCH).
-                source(sourceBuilder);
+                size(itemsPerPage < 0 ? elasticsearchUtils.getIndexMaxResultWindow() : itemsPerPage).
+                sort(sortBuilders(kind, orderBy)).
+                build();
 
-        SearchHit[] esResult = null;
+        @SuppressWarnings("rawtypes")
+        List<Hit<Map>> esResult = null;
         try {
-            esResult = client.search(request, RequestOptions.DEFAULT).getHits().getHits();
+            esResult = client.search(request, Map.class).hits().hits();
         } catch (Exception e) {
             LOG.error("While searching in Elasticsearch", e);
         }
 
-        return ArrayUtils.isEmpty(esResult)
+        return CollectionUtils.isEmpty(esResult)
                 ? List.of()
-                : buildResult(Stream.of(esResult).map(SearchHit::getId).collect(Collectors.toList()), kind);
+                : buildResult(esResult.stream().map(Hit::id).collect(Collectors.toList()), kind);
     }
 
-    protected QueryBuilder getQueryBuilder(final SearchCond cond, final AnyTypeKind kind) {
-        QueryBuilder builder = null;
+    protected Query getQuery(final SearchCond cond, final AnyTypeKind kind) {
+        Query query = null;
 
         switch (cond.getType()) {
             case LEAF:
             case NOT_LEAF:
-                builder = cond.getLeaf(AnyTypeCond.class).
+                query = cond.getLeaf(AnyTypeCond.class).
                         filter(leaf -> AnyTypeKind.ANY_OBJECT == kind).
-                        map(this::getQueryBuilder).
+                        map(this::getQuery).
                         orElse(null);
 
-                if (builder == null) {
-                    builder = cond.getLeaf(RelationshipTypeCond.class).
+                if (query == null) {
+                    query = cond.getLeaf(RelationshipTypeCond.class).
                             filter(leaf -> AnyTypeKind.GROUP != kind).
-                            map(this::getQueryBuilder).
+                            map(this::getQuery).
                             orElse(null);
                 }
 
-                if (builder == null) {
-                    builder = cond.getLeaf(RelationshipCond.class).
+                if (query == null) {
+                    query = cond.getLeaf(RelationshipCond.class).
                             filter(leaf -> AnyTypeKind.GROUP != kind).
-                            map(this::getQueryBuilder).
+                            map(this::getQuery).
                             orElse(null);
                 }
 
-                if (builder == null) {
-                    builder = cond.getLeaf(MembershipCond.class).
+                if (query == null) {
+                    query = cond.getLeaf(MembershipCond.class).
                             filter(leaf -> AnyTypeKind.GROUP != kind).
-                            map(this::getQueryBuilder).
+                            map(this::getQuery).
                             orElse(null);
                 }
 
-                if (builder == null) {
-                    builder = cond.getLeaf(MemberCond.class).
+                if (query == null) {
+                    query = cond.getLeaf(MemberCond.class).
                             filter(leaf -> AnyTypeKind.GROUP == kind).
-                            map(this::getQueryBuilder).
+                            map(this::getQuery).
                             orElse(null);
                 }
 
-                if (builder == null) {
-                    builder = cond.getLeaf(AssignableCond.class).
-                            map(this::getQueryBuilder).
+                if (query == null) {
+                    query = cond.getLeaf(AssignableCond.class).
+                            map(this::getQuery).
                             orElse(null);
                 }
 
-                if (builder == null) {
-                    builder = cond.getLeaf(RoleCond.class).
+                if (query == null) {
+                    query = cond.getLeaf(RoleCond.class).
                             filter(leaf -> AnyTypeKind.USER == kind).
-                            map(this::getQueryBuilder).
+                            map(this::getQuery).
                             orElse(null);
                 }
 
-                if (builder == null) {
-                    builder = cond.getLeaf(PrivilegeCond.class).
+                if (query == null) {
+                    query = cond.getLeaf(PrivilegeCond.class).
                             filter(leaf -> AnyTypeKind.USER == kind).
-                            map(this::getQueryBuilder).
+                            map(this::getQuery).
                             orElse(null);
                 }
 
-                if (builder == null) {
-                    builder = cond.getLeaf(DynRealmCond.class).
-                            map(this::getQueryBuilder).
+                if (query == null) {
+                    query = cond.getLeaf(DynRealmCond.class).
+                            map(this::getQuery).
                             orElse(null);
                 }
 
-                if (builder == null) {
-                    builder = cond.getLeaf(ResourceCond.class).
-                            map(this::getQueryBuilder).
+                if (query == null) {
+                    query = cond.getLeaf(ResourceCond.class).
+                            map(this::getQuery).
                             orElse(null);
                 }
 
-                if (builder == null) {
+                if (query == null) {
                     Optional<AnyCond> anyCond = cond.getLeaf(AnyCond.class);
                     if (anyCond.isPresent()) {
-                        builder = getQueryBuilder(anyCond.get(), kind);
+                        query = getQuery(anyCond.get(), kind);
                     } else {
-                        builder = cond.getLeaf(AttrCond.class).
-                                map(leaf -> getQueryBuilder(leaf, kind)).
+                        query = cond.getLeaf(AttrCond.class).
+                                map(leaf -> getQuery(leaf, kind)).
                                 orElse(null);
                     }
                 }
 
                 // allow for additional search conditions
-                if (builder == null) {
-                    builder = getQueryBuilderForCustomConds(cond, kind);
+                if (query == null) {
+                    query = getQueryForCustomConds(cond, kind);
                 }
 
-                if (builder == null) {
-                    builder = MATCH_NONE_QUERY_BUILDER;
+                if (query == null) {
+                    query = MATCH_NONE_QUERY;
                 }
 
                 if (cond.getType() == SearchCond.Type.NOT_LEAF) {
-                    builder = QueryBuilders.boolQuery().mustNot(builder);
+                    query = new Query.Builder().bool(QueryBuilders.bool().mustNot(query).build()).build();
                 }
                 break;
 
             case AND:
-                builder = QueryBuilders.boolQuery().
-                        must(getQueryBuilder(cond.getLeft(), kind)).
-                        must(getQueryBuilder(cond.getRight(), kind));
+                query = new Query.Builder().bool(QueryBuilders.bool().
+                        must(getQuery(cond.getLeft(), kind)).must(getQuery(cond.getRight(), kind)).build()).
+                        build();
                 break;
 
             case OR:
-                builder = QueryBuilders.disMaxQuery().
-                        add(getQueryBuilder(cond.getLeft(), kind)).
-                        add(getQueryBuilder(cond.getRight(), kind));
+                query = new Query.Builder().disMax(QueryBuilders.disMax().
+                        queries(getQuery(cond.getLeft(), kind), getQuery(cond.getRight(), kind)).build()).
+                        build();
                 break;
 
             default:
         }
 
-        return builder;
+        return query;
     }
 
-    protected QueryBuilder getQueryBuilder(final AnyTypeCond cond) {
-        return QueryBuilders.termQuery("anyType", cond.getAnyTypeKey());
+    protected Query getQuery(final AnyTypeCond cond) {
+        return new Query.Builder().term(QueryBuilders.term().
+                field("anyType").value(FieldValue.of(cond.getAnyTypeKey())).build()).
+                build();
     }
 
-    protected QueryBuilder getQueryBuilder(final RelationshipTypeCond cond) {
-        return QueryBuilders.termQuery("relationshipTypes", cond.getRelationshipTypeKey());
+    protected Query getQuery(final RelationshipTypeCond cond) {
+        return new Query.Builder().term(QueryBuilders.term().
+                field("relationshipTypes").value(FieldValue.of(cond.getRelationshipTypeKey())).build()).
+                build();
     }
 
-    protected QueryBuilder getQueryBuilder(final RelationshipCond cond) {
+    protected Query getQuery(final RelationshipCond cond) {
         String rightAnyObjectKey;
         try {
             rightAnyObjectKey = check(cond);
         } catch (IllegalArgumentException e) {
-            return MATCH_NONE_QUERY_BUILDER;
+            return MATCH_NONE_QUERY;
         }
 
-        return QueryBuilders.termQuery("relationships", rightAnyObjectKey);
+        return new Query.Builder().term(QueryBuilders.term().
+                field("relationships").value(FieldValue.of(rightAnyObjectKey)).build()).
+                build();
     }
 
-    protected QueryBuilder getQueryBuilder(final MembershipCond cond) {
+    protected Query getQuery(final MembershipCond cond) {
         List<String> groupKeys;
         try {
             groupKeys = check(cond);
         } catch (IllegalArgumentException e) {
-            return MATCH_NONE_QUERY_BUILDER;
+            return MATCH_NONE_QUERY;
         }
 
-        if (groupKeys.size() == 1) {
-            return QueryBuilders.termQuery("memberships", groupKeys.get(0));
+        List<Query> membershipQueries = groupKeys.stream().
+                map(key -> new Query.Builder().term(QueryBuilders.term().
+                field("memberships").value(FieldValue.of(key)).build()).
+                build()).collect(Collectors.toList());
+        if (membershipQueries.size() == 1) {
+            return membershipQueries.get(0);
         }
 
-        DisMaxQueryBuilder builder = QueryBuilders.disMaxQuery();
-        groupKeys.forEach(key -> builder.add(QueryBuilders.termQuery("memberships", key)));
-        return builder;
+        return new Query.Builder().disMax(QueryBuilders.disMax().queries(membershipQueries).build()).build();
     }
 
-    protected QueryBuilder getQueryBuilder(final AssignableCond cond) {
+    protected Query getQuery(final AssignableCond cond) {
         Realm realm;
         try {
             realm = check(cond);
         } catch (IllegalArgumentException e) {
-            return MATCH_NONE_QUERY_BUILDER;
+            return MATCH_NONE_QUERY;
         }
 
-        DisMaxQueryBuilder builder = QueryBuilders.disMaxQuery();
+        List<Query> queries = new ArrayList<>();
         if (cond.isFromGroup()) {
             realmDAO.findDescendants(realm).forEach(
-                    current -> builder.add(QueryBuilders.termQuery("realm", current.getFullPath())));
+                    current -> queries.add(new Query.Builder().term(QueryBuilders.term().
+                            field("realm").value(FieldValue.of(current.getFullPath())).build()).
+                            build()));
         } else {
             for (Realm current = realm; current.getParent() != null; current = current.getParent()) {
-                builder.add(QueryBuilders.termQuery("realm", current.getFullPath()));
+                queries.add(new Query.Builder().term(QueryBuilders.term().
+                        field("realm").value(FieldValue.of(current.getFullPath())).build()).
+                        build());
             }
-            builder.add(QueryBuilders.termQuery("realm", realmDAO.getRoot().getFullPath()));
+            queries.add(new Query.Builder().term(QueryBuilders.term().
+                    field("realm").value(FieldValue.of(realmDAO.getRoot().getFullPath())).build()).
+                    build());
         }
 
-        return builder;
+        return new Query.Builder().disMax(QueryBuilders.disMax().queries(queries).build()).build();
     }
 
-    protected QueryBuilder getQueryBuilder(final RoleCond cond) {
-        return QueryBuilders.termQuery("roles", cond.getRole());
+    protected Query getQuery(final RoleCond cond) {
+        return new Query.Builder().term(QueryBuilders.term().
+                field("roles").value(FieldValue.of(cond.getRole())).build()).
+                build();
     }
 
-    protected QueryBuilder getQueryBuilder(final PrivilegeCond cond) {
-        return QueryBuilders.termQuery("privileges", cond.getPrivilege());
+    protected Query getQuery(final PrivilegeCond cond) {
+        return new Query.Builder().term(QueryBuilders.term().
+                field("privileges").value(FieldValue.of(cond.getPrivilege())).build()).
+                build();
     }
 
-    protected QueryBuilder getQueryBuilder(final DynRealmCond cond) {
-        return QueryBuilders.termQuery("dynRealms", cond.getDynRealm());
+    protected Query getQuery(final DynRealmCond cond) {
+        return new Query.Builder().term(QueryBuilders.term().
+                field("dynRealms").value(FieldValue.of(cond.getDynRealm())).build()).
+                build();
     }
 
-    protected QueryBuilder getQueryBuilder(final MemberCond cond) {
+    protected Query getQuery(final MemberCond cond) {
         String memberKey;
         try {
             memberKey = check(cond);
         } catch (IllegalArgumentException e) {
-            return MATCH_NONE_QUERY_BUILDER;
+            return MATCH_NONE_QUERY;
         }
 
-        return QueryBuilders.termQuery("members", memberKey);
+        return new Query.Builder().term(QueryBuilders.term().
+                field("members").value(FieldValue.of(memberKey)).build()).
+                build();
     }
 
-    protected QueryBuilder getQueryBuilder(final ResourceCond cond) {
-        return QueryBuilders.termQuery("resources", cond.getResourceKey());
+    protected Query getQuery(final ResourceCond cond) {
+        return new Query.Builder().term(QueryBuilders.term().
+                field("resources").value(FieldValue.of(cond.getResourceKey())).build()).
+                build();
     }
 
-    protected QueryBuilder fillAttrQuery(
+    protected Query fillAttrQuery(
             final PlainSchema schema,
             final PlainAttrValue attrValue,
             final AttrCond cond) {
@@ -474,15 +513,17 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
                 ? attrValue.getDateValue().getTime()
                 : attrValue.getValue();
 
-        QueryBuilder builder = MATCH_NONE_QUERY_BUILDER;
+        Query query = MATCH_NONE_QUERY;
 
         switch (cond.getType()) {
             case ISNOTNULL:
-                builder = QueryBuilders.existsQuery(schema.getKey());
+                query = new Query.Builder().exists(QueryBuilders.exists().field(schema.getKey()).build()).build();
                 break;
 
             case ISNULL:
-                builder = QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery(schema.getKey()));
+                query = new Query.Builder().bool(QueryBuilders.bool().mustNot(
+                        new Query.Builder().exists(QueryBuilders.exists().field(schema.getKey()).build()).build()).
+                        build()).build();
                 break;
 
             case ILIKE:
@@ -499,62 +540,86 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
                         output.append(escapeForLikeRegex(c));
                     }
                 }
-                builder = QueryBuilders.regexpQuery(schema.getKey(), output.toString());
+                query = new Query.Builder().regexp(QueryBuilders.regexp().
+                        field(schema.getKey()).value(output.toString()).build()).build();
                 break;
 
             case LIKE:
-                builder = QueryBuilders.wildcardQuery(schema.getKey(), cond.getExpression().replace('%', '*'));
+                query = new Query.Builder().wildcard(QueryBuilders.wildcard().
+                        field(schema.getKey()).value(cond.getExpression().replace('%', '*')).build()).build();
                 break;
 
             case IEQ:
-                builder = QueryBuilders.matchQuery(schema.getKey(), cond.getExpression().toLowerCase());
+                query = new Query.Builder().match(QueryBuilders.match().
+                        field(schema.getKey()).query(FieldValue.of(cond.getExpression().toLowerCase())).build()).
+                        build();
                 break;
 
             case EQ:
-                builder = QueryBuilders.termQuery(schema.getKey(), value);
+                FieldValue fieldValue;
+                if (value instanceof Double) {
+                    fieldValue = FieldValue.of((Double) value);
+                } else if (value instanceof Long) {
+                    fieldValue = FieldValue.of((Long) value);
+                } else if (value instanceof Boolean) {
+                    fieldValue = FieldValue.of((Boolean) value);
+                } else {
+                    fieldValue = FieldValue.of(value.toString());
+                }
+                query = new Query.Builder().term(QueryBuilders.term().
+                        field(schema.getKey()).value(fieldValue).build()).
+                        build();
                 break;
 
             case GE:
-                builder = QueryBuilders.rangeQuery(schema.getKey()).gte(value);
+                query = new Query.Builder().range(QueryBuilders.range().
+                        field(schema.getKey()).gte(JsonData.of(value)).build()).
+                        build();
                 break;
 
             case GT:
-                builder = QueryBuilders.rangeQuery(schema.getKey()).gt(value);
+                query = new Query.Builder().range(QueryBuilders.range().
+                        field(schema.getKey()).gt(JsonData.of(value)).build()).
+                        build();
                 break;
 
             case LE:
-                builder = QueryBuilders.rangeQuery(schema.getKey()).lte(value);
+                query = new Query.Builder().range(QueryBuilders.range().
+                        field(schema.getKey()).lte(JsonData.of(value)).build()).
+                        build();
                 break;
 
             case LT:
-                builder = QueryBuilders.rangeQuery(schema.getKey()).lt(value);
+                query = new Query.Builder().range(QueryBuilders.range().
+                        field(schema.getKey()).lt(JsonData.of(value)).build()).
+                        build();
                 break;
 
             default:
         }
 
-        return builder;
+        return query;
     }
 
-    protected QueryBuilder getQueryBuilder(final AttrCond cond, final AnyTypeKind kind) {
+    protected Query getQuery(final AttrCond cond, final AnyTypeKind kind) {
         Pair<PlainSchema, PlainAttrValue> checked;
         try {
             checked = check(cond, kind);
         } catch (IllegalArgumentException e) {
-            return MATCH_NONE_QUERY_BUILDER;
+            return MATCH_NONE_QUERY;
         }
 
         return fillAttrQuery(checked.getLeft(), checked.getRight(), cond);
     }
 
-    protected QueryBuilder getQueryBuilder(final AnyCond cond, final AnyTypeKind kind) {
+    protected Query getQuery(final AnyCond cond, final AnyTypeKind kind) {
         if (JAXRSService.PARAM_REALM.equals(cond.getSchema())
                 && SyncopeConstants.UUID_PATTERN.matcher(cond.getExpression()).matches()) {
 
             Realm realm = realmDAO.find(cond.getExpression());
             if (realm == null) {
                 LOG.warn("Invalid Realm key: {}", cond.getExpression());
-                return MATCH_NONE_QUERY_BUILDER;
+                return MATCH_NONE_QUERY;
             }
             cond.setExpression(realm.getFullPath());
         }
@@ -563,13 +628,13 @@ public class ElasticsearchAnySearchDAO extends AbstractAnySearchDAO {
         try {
             checked = check(cond, kind);
         } catch (IllegalArgumentException e) {
-            return MATCH_NONE_QUERY_BUILDER;
+            return MATCH_NONE_QUERY;
         }
 
         return fillAttrQuery(checked.getLeft(), checked.getMiddle(), checked.getRight());
     }
 
-    protected QueryBuilder getQueryBuilderForCustomConds(final SearchCond cond, final AnyTypeKind kind) {
-        return MATCH_ALL_QUERY_BUILDER;
+    protected Query getQueryForCustomConds(final SearchCond cond, final AnyTypeKind kind) {
+        return MATCH_ALL_QUERY;
     }
 }
diff --git a/ext/elasticsearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java b/ext/elasticsearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java
index 2fa9a54..770cb76 100644
--- a/ext/elasticsearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java
+++ b/ext/elasticsearch/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/dao/ElasticsearchAnySearchDAOTest.java
@@ -18,11 +18,17 @@
  */
 package org.apache.syncope.core.persistence.jpa.dao;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
+import co.elastic.clients.elasticsearch._types.FieldValue;
+import co.elastic.clients.elasticsearch._types.SearchType;
+import co.elastic.clients.elasticsearch._types.query_dsl.Query;
+import co.elastic.clients.elasticsearch._types.query_dsl.QueryBuilders;
+import co.elastic.clients.elasticsearch.core.SearchRequest;
 import java.io.IOException;
 import java.util.List;
 import java.util.Optional;
@@ -48,11 +54,6 @@ import org.apache.syncope.core.persistence.jpa.entity.user.JPAUser;
 import org.apache.syncope.core.provisioning.api.utils.RealmUtils;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils;
-import org.elasticsearch.action.search.SearchRequest;
-import org.elasticsearch.action.search.SearchType;
-import org.elasticsearch.index.query.QueryBuilder;
-import org.elasticsearch.index.query.QueryBuilders;
-import org.elasticsearch.search.builder.SearchSourceBuilder;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.InjectMocks;
@@ -87,7 +88,7 @@ public class ElasticsearchAnySearchDAOTest {
     private ElasticsearchAnySearchDAO searchDAO;
 
     @Test
-    public void getAdminRealmsFilter_realm() {
+    public void getAdminRealmsFilter_realm() throws IOException {
         // 1. mock
         Realm root = mock(Realm.class);
         when(root.getFullPath()).thenReturn(SyncopeConstants.ROOT_REALM);
@@ -97,11 +98,15 @@ public class ElasticsearchAnySearchDAOTest {
 
         // 2. test
         Set<String> adminRealms = Set.of(SyncopeConstants.ROOT_REALM);
-        Triple<Optional<QueryBuilder>, Set<String>, Set<String>> filter =
+        Triple<Optional<Query>, Set<String>, Set<String>> filter =
                 searchDAO.getAdminRealmsFilter(AnyTypeKind.USER, adminRealms);
-        assertEquals(
-                QueryBuilders.disMaxQuery().add(QueryBuilders.termQuery("realm", SyncopeConstants.ROOT_REALM)),
-                filter.getLeft().get());
+
+        assertThat(
+                new Query.Builder().disMax(QueryBuilders.disMax().queries(
+                        new Query.Builder().term(QueryBuilders.term().field("realm").value(
+                                FieldValue.of(SyncopeConstants.ROOT_REALM)).build()).build()).build()).
+                        build()).
+                usingRecursiveComparison().isEqualTo(filter.getLeft().get());
         assertEquals(Set.of(), filter.getMiddle());
         assertEquals(Set.of(), filter.getRight());
     }
@@ -116,7 +121,7 @@ public class ElasticsearchAnySearchDAOTest {
 
         // 2. test
         Set<String> adminRealms = Set.of("dyn");
-        Triple<Optional<QueryBuilder>, Set<String>, Set<String>> filter =
+        Triple<Optional<Query>, Set<String>, Set<String>> filter =
                 searchDAO.getAdminRealmsFilter(AnyTypeKind.USER, adminRealms);
         assertFalse(filter.getLeft().isPresent());
         assertEquals(Set.of("dyn"), filter.getMiddle());
@@ -126,7 +131,7 @@ public class ElasticsearchAnySearchDAOTest {
     @Test
     public void getAdminRealmsFilter_groupOwner() {
         Set<String> adminRealms = Set.of(RealmUtils.getGroupOwnerRealm("/any", "groupKey"));
-        Triple<Optional<QueryBuilder>, Set<String>, Set<String>> filter =
+        Triple<Optional<Query>, Set<String>, Set<String>> filter =
                 searchDAO.getAdminRealmsFilter(AnyTypeKind.USER, adminRealms);
         assertFalse(filter.getLeft().isPresent());
         assertEquals(Set.of(), filter.getMiddle());
@@ -156,22 +161,21 @@ public class ElasticsearchAnySearchDAOTest {
             AnyCond anyCond = new AnyCond(AttrCond.Type.ISNOTNULL);
             anyCond.setSchema("id");
 
-            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder().
-                    query(searchDAO.getQueryBuilder(adminRealms, SearchCond.getLeaf(anyCond), AnyTypeKind.USER)).
+            SearchRequest request = new SearchRequest.Builder().
+                    index(ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), AnyTypeKind.USER)).
+                    searchType(SearchType.QueryThenFetch).
+                    query(searchDAO.getQuery(adminRealms, SearchCond.getLeaf(anyCond), AnyTypeKind.USER)).
                     from(1).
-                    size(10);
-            searchDAO.sortBuilders(AnyTypeKind.USER, List.of()).forEach(sourceBuilder::sort);
-
-            SearchRequest request = new SearchRequest(
-                    ElasticsearchUtils.getContextDomainName(AuthContextUtils.getDomain(), AnyTypeKind.USER)).
-                    searchType(SearchType.QUERY_THEN_FETCH).
-                    source(sourceBuilder);
-
-            assertEquals(
-                    QueryBuilders.boolQuery().
-                            must(QueryBuilders.existsQuery("id")).
-                            must(QueryBuilders.termQuery("memberships", "groupKey")),
-                    request.source().query());
+                    size(10).
+                    build();
+
+            assertThat(
+                    new Query.Builder().bool(QueryBuilders.bool().
+                            must(new Query.Builder().exists(QueryBuilders.exists().field("id").build()).build()).
+                            must(new Query.Builder().term(QueryBuilders.term().field("memberships").value(
+                                    FieldValue.of("groupKey")).build()).build()).
+                            build()).build()).
+                    usingRecursiveComparison().isEqualTo(request.query());
         }
     }
 }
diff --git a/ext/elasticsearch/provisioning-java/pom.xml b/ext/elasticsearch/provisioning-java/pom.xml
index ef60198..bd1d9c8 100644
--- a/ext/elasticsearch/provisioning-java/pom.xml
+++ b/ext/elasticsearch/provisioning-java/pom.xml
@@ -43,7 +43,7 @@ under the License.
       <artifactId>syncope-core-provisioning-java</artifactId>
       <version>${project.version}</version>
     </dependency>
-    
+
     <dependency>
       <groupId>org.apache.syncope.ext.elasticsearch</groupId>
       <artifactId>syncope-ext-elasticsearch-client</artifactId>
diff --git a/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java b/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java
index 6ae2d34..f7ef590 100644
--- a/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java
+++ b/ext/elasticsearch/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/ElasticsearchReindex.java
@@ -18,7 +18,13 @@
  */
 package org.apache.syncope.core.provisioning.java.job;
 
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
+import co.elastic.clients.elasticsearch.core.IndexRequest;
+import co.elastic.clients.elasticsearch.core.IndexResponse;
+import co.elastic.clients.elasticsearch.indices.IndexSettings;
 import java.io.IOException;
+import java.util.Map;
 import org.apache.syncope.common.lib.types.AnyTypeKind;
 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
@@ -28,11 +34,6 @@ import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
 import org.apache.syncope.core.spring.security.AuthContextUtils;
 import org.apache.syncope.ext.elasticsearch.client.ElasticsearchIndexManager;
 import org.apache.syncope.ext.elasticsearch.client.ElasticsearchUtils;
-import org.elasticsearch.action.index.IndexRequest;
-import org.elasticsearch.action.index.IndexResponse;
-import org.elasticsearch.client.RequestOptions;
-import org.elasticsearch.client.RestHighLevelClient;
-import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.quartz.JobExecutionContext;
 import org.quartz.JobExecutionException;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -43,7 +44,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate {
 
     @Autowired
-    protected RestHighLevelClient client;
+    protected ElasticsearchClient client;
 
     @Autowired
     protected ElasticsearchIndexManager indexManager;
@@ -60,27 +61,27 @@ public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate {
     @Autowired
     protected AnyObjectDAO anyObjectDAO;
 
-    protected XContentBuilder userSettings() throws IOException {
+    protected IndexSettings userSettings() throws IOException {
         return indexManager.defaultSettings();
     }
 
-    protected XContentBuilder groupSettings() throws IOException {
+    protected IndexSettings groupSettings() throws IOException {
         return indexManager.defaultSettings();
     }
 
-    protected XContentBuilder anyObjectSettings() throws IOException {
+    protected IndexSettings anyObjectSettings() throws IOException {
         return indexManager.defaultSettings();
     }
 
-    protected XContentBuilder userMapping() throws IOException {
+    protected TypeMapping userMapping() throws IOException {
         return indexManager.defaultMapping();
     }
 
-    protected XContentBuilder groupMapping() throws IOException {
+    protected TypeMapping groupMapping() throws IOException {
         return indexManager.defaultMapping();
     }
 
-    protected XContentBuilder anyObjectMapping() throws IOException {
+    protected TypeMapping anyObjectMapping() throws IOException {
         return indexManager.defaultMapping();
     }
 
@@ -104,13 +105,14 @@ public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate {
                 LOG.debug("Indexing users...");
                 for (int page = 1; page <= (userDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
                     for (String user : userDAO.findAllKeys(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
-                        IndexRequest request = new IndexRequest(
-                                ElasticsearchUtils.getContextDomainName(
+                        IndexRequest<Map<String, Object>> request = new IndexRequest.Builder<Map<String, Object>>().
+                                index(ElasticsearchUtils.getContextDomainName(
                                         AuthContextUtils.getDomain(), AnyTypeKind.USER)).
                                 id(user).
-                                source(utils.builder(userDAO.find(user), AuthContextUtils.getDomain()));
+                                document(utils.document(userDAO.find(user), AuthContextUtils.getDomain())).
+                                build();
                         try {
-                            IndexResponse response = client.index(request, RequestOptions.DEFAULT);
+                            IndexResponse response = client.index(request);
                             LOG.debug("Index successfully created for {}: {}", user, response);
                         } catch (Exception e) {
                             LOG.error("Could not create index for {} {}", AnyTypeKind.USER, user);
@@ -121,13 +123,14 @@ public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate {
                 LOG.debug("Indexing groups...");
                 for (int page = 1; page <= (groupDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
                     for (String group : groupDAO.findAllKeys(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
-                        IndexRequest request = new IndexRequest(
-                                ElasticsearchUtils.getContextDomainName(
+                        IndexRequest<Map<String, Object>> request = new IndexRequest.Builder<Map<String, Object>>().
+                                index(ElasticsearchUtils.getContextDomainName(
                                         AuthContextUtils.getDomain(), AnyTypeKind.GROUP)).
                                 id(group).
-                                source(utils.builder(groupDAO.find(group), AuthContextUtils.getDomain()));
+                                document(utils.document(groupDAO.find(group), AuthContextUtils.getDomain())).
+                                build();
                         try {
-                            IndexResponse response = client.index(request, RequestOptions.DEFAULT);
+                            IndexResponse response = client.index(request);
                             LOG.debug("Index successfully created for {}: {}", group, response);
                         } catch (Exception e) {
                             LOG.error("Could not create index for {} {}", AnyTypeKind.GROUP, group);
@@ -138,13 +141,14 @@ public class ElasticsearchReindex extends AbstractSchedTaskJobDelegate {
                 LOG.debug("Indexing any objects...");
                 for (int page = 1; page <= (anyObjectDAO.count() / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
                     for (String anyObject : anyObjectDAO.findAllKeys(page, AnyDAO.DEFAULT_PAGE_SIZE)) {
-                        IndexRequest request = new IndexRequest(
-                                ElasticsearchUtils.getContextDomainName(
+                        IndexRequest<Map<String, Object>> request = new IndexRequest.Builder<Map<String, Object>>().
+                                index(ElasticsearchUtils.getContextDomainName(
                                         AuthContextUtils.getDomain(), AnyTypeKind.ANY_OBJECT)).
                                 id(anyObject).
-                                source(utils.builder(anyObjectDAO.find(anyObject), AuthContextUtils.getDomain()));
+                                document(utils.document(anyObjectDAO.find(anyObject), AuthContextUtils.getDomain())).
+                                build();
                         try {
-                            IndexResponse response = client.index(request, RequestOptions.DEFAULT);
+                            IndexResponse response = client.index(request);
                             LOG.debug("Index successfully created for {}: {}", anyObject, response);
                         } catch (Exception e) {
                             LOG.error("Could not create index for {} {}", AnyTypeKind.ANY_OBJECT, anyObject);
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java
index 5c7b501..dde15d4 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/NotificationTaskITCase.java
@@ -167,12 +167,10 @@ public class NotificationTaskITCase extends AbstractNotificationTaskITCase {
             taskTO = taskService.read(TaskType.NOTIFICATION, taskTO.getKey(), true);
             assertNotNull(taskTO);
             assertTrue(taskTO.isExecuted());
-            assertEquals(1, taskTO.getExecutions().size());
+            assertFalse(taskTO.getExecutions().isEmpty());
         } finally {
             // Remove execution to make test re-runnable
-            if (!taskTO.getExecutions().isEmpty()) {
-                taskService.deleteExecution(taskTO.getExecutions().get(0).getKey());
-            }
+            taskTO.getExecutions().forEach(e -> taskService.deleteExecution(e.getKey()));
         }
     }
 
diff --git a/pom.xml b/pom.xml
index 6e364c4..00acfdc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -430,7 +430,7 @@ under the License.
 
     <slf4j.version>1.7.32</slf4j.version>
 
-    <elasticsearch.version>7.15.2</elasticsearch.version>
+    <elasticsearch.version>7.16.2</elasticsearch.version>
 
     <apacheds.version>2.0.0.AM26</apacheds.version>
     <apachedirapi.version>2.0.0</apachedirapi.version>
@@ -677,33 +677,19 @@ under the License.
       <!-- /Camel -->
 
       <dependency>
-        <groupId>org.elasticsearch.client</groupId>
-        <artifactId>elasticsearch-rest-high-level-client</artifactId>
-        <version>${elasticsearch.version}</version>
-        <exclusions>
-          <exclusion>
-            <groupId>com.fasterxml.jackson.datatype</groupId>
-            <artifactId>jackson-dataformat-smile</artifactId>
-          </exclusion>
-          <exclusion>
-            <groupId>com.fasterxml.jackson.datatype</groupId>
-            <artifactId>jackson-dataformat-yaml</artifactId>
-          </exclusion>
-          <exclusion>
-            <groupId>com.fasterxml.jackson.datatype</groupId>
-            <artifactId>jackson-dataformat-cbor</artifactId>
-          </exclusion>
-        </exclusions>
+        <groupId>co.elastic.clients</groupId>
+        <artifactId>elasticsearch-java</artifactId>
+	<version>${elasticsearch.version}</version>
       </dependency>
       <dependency>
         <groupId>org.elasticsearch.client</groupId>
         <artifactId>elasticsearch-rest-client</artifactId>
-        <version>${elasticsearch.version}</version>
+        <version>7.16.1</version>
       </dependency>
       <dependency>
-        <groupId>org.elasticsearch</groupId>
-        <artifactId>elasticsearch</artifactId>
-        <version>${elasticsearch.version}</version>
+        <groupId>jakarta.json</groupId>
+        <artifactId>jakarta.json-api</artifactId>
+        <version>2.0.1</version>
       </dependency>
 
       <dependency>