Skip to content

Commit ec77e35

Browse files
committed
Support for the reactive version of SQL queries.
Signed-off-by: Youssef Aouichaoui <[email protected]>
1 parent 63923d0 commit ec77e35

File tree

6 files changed

+186
-7
lines changed

6 files changed

+186
-7
lines changed

src/main/java/org/springframework/data/elasticsearch/client/elc/ReactiveElasticsearchTemplate.java

+15
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import static co.elastic.clients.util.ApiTypeHelper.*;
1919
import static org.springframework.data.elasticsearch.client.elc.TypeUtils.*;
20+
import static org.springframework.data.elasticsearch.core.sql.types.ResponseFormat.json;
2021

2122
import co.elastic.clients.elasticsearch._types.Result;
2223
import co.elastic.clients.elasticsearch.core.*;
@@ -25,6 +26,8 @@
2526
import co.elastic.clients.json.JsonpMapper;
2627
import co.elastic.clients.transport.Version;
2728
import co.elastic.clients.transport.endpoints.BooleanResponse;
29+
import org.springframework.data.elasticsearch.core.query.SqlQuery;
30+
import org.springframework.data.elasticsearch.core.sql.SqlResponse;
2831
import reactor.core.publisher.Flux;
2932
import reactor.core.publisher.Mono;
3033
import reactor.util.function.Tuple2;
@@ -88,6 +91,7 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
8891
private static final Log LOGGER = LogFactory.getLog(ReactiveElasticsearchTemplate.class);
8992

9093
private final ReactiveElasticsearchClient client;
94+
private final ReactiveElasticsearchSqlClient sqlClient;
9195
private final RequestConverter requestConverter;
9296
private final ResponseConverter responseConverter;
9397
private final JsonpMapper jsonpMapper;
@@ -99,6 +103,7 @@ public ReactiveElasticsearchTemplate(ReactiveElasticsearchClient client, Elastic
99103
Assert.notNull(client, "client must not be null");
100104

101105
this.client = client;
106+
this.sqlClient = client.sql();
102107
this.jsonpMapper = client._transport().jsonpMapper();
103108
requestConverter = new RequestConverter(converter, jsonpMapper);
104109
responseConverter = new ResponseConverter(jsonpMapper);
@@ -646,6 +651,16 @@ public BaseQueryBuilder queryBuilderWithIds(List<String> ids) {
646651
return NativeQuery.builder().withIds(ids);
647652
}
648653

654+
@Override
655+
public Mono<SqlResponse> search(SqlQuery query) {
656+
Assert.notNull(query, "Query must not be null.");
657+
Assert.isTrue(query.getFormat() == null || json.equals(query.getFormat()),
658+
"The Elasticsearch Java Client only supports JSON format.");
659+
660+
co.elastic.clients.elasticsearch.sql.QueryRequest request = requestConverter.sqlQueryRequest(query);
661+
return sqlClient.query(request).onErrorMap(this::translateException).map(responseConverter::sqlResponse);
662+
}
663+
649664
/**
650665
* Callback interface to be used with {@link #execute(ReactiveElasticsearchTemplate.ClientCallback<>)} for operating
651666
* directly on {@link ReactiveElasticsearchClient}.

src/main/java/org/springframework/data/elasticsearch/core/ReactiveElasticsearchOperations.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
2222
import org.springframework.data.elasticsearch.core.routing.RoutingResolver;
2323
import org.springframework.data.elasticsearch.core.script.ReactiveScriptOperations;
24+
import org.springframework.data.elasticsearch.core.sql.ReactiveSqlOperations;
2425
import org.springframework.lang.Nullable;
2526

2627
/**
@@ -31,7 +32,7 @@
3132
* @since 3.2
3233
*/
3334
public interface ReactiveElasticsearchOperations
34-
extends ReactiveDocumentOperations, ReactiveSearchOperations, ReactiveScriptOperations {
35+
extends ReactiveDocumentOperations, ReactiveSearchOperations, ReactiveScriptOperations, ReactiveSqlOperations {
3536

3637
/**
3738
* Get the {@link ElasticsearchConverter} used.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.elasticsearch.core.sql;
17+
18+
import org.springframework.data.elasticsearch.core.query.SqlQuery;
19+
20+
import reactor.core.publisher.Mono;
21+
22+
/**
23+
* The reactive version of operations for the
24+
* <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/sql-search-api.html">SQL search API</a>.
25+
*
26+
* @author Aouichaoui Youssef
27+
* @since 5.4
28+
*/
29+
public interface ReactiveSqlOperations {
30+
/**
31+
* Execute the criteria {@code query} against elasticsearch and return result as {@link SqlResponse}
32+
*
33+
* @param query the query to execute
34+
* @return {@link SqlResponse} containing the list of found objects
35+
*/
36+
Mono<SqlResponse> search(SqlQuery query);
37+
}

src/main/java/org/springframework/data/elasticsearch/core/sql/SqlOperations.java

+7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717

1818
import org.springframework.data.elasticsearch.core.query.SqlQuery;
1919

20+
/**
21+
* The operations for the
22+
* <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/sql-search-api.html">SQL search API</a>.
23+
*
24+
* @author Aouichaoui Youssef
25+
* @since 5.4
26+
*/
2027
public interface SqlOperations {
2128
/**
2229
* Execute the criteria {@code query} against elasticsearch and return result as {@link SqlResponse}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.elasticsearch.core.sql;
17+
18+
import static org.springframework.data.elasticsearch.core.IndexOperationsAdapter.blocking;
19+
20+
import java.util.List;
21+
22+
import org.junit.jupiter.api.AfterEach;
23+
import org.junit.jupiter.api.BeforeEach;
24+
import org.junit.jupiter.api.DisplayName;
25+
import org.junit.jupiter.api.Test;
26+
import org.springframework.beans.factory.annotation.Autowired;
27+
import org.springframework.context.annotation.Configuration;
28+
import org.springframework.context.annotation.Import;
29+
import org.springframework.data.annotation.Id;
30+
import org.springframework.data.elasticsearch.annotations.Document;
31+
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
32+
import org.springframework.data.elasticsearch.core.query.SqlQuery;
33+
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchTemplateConfiguration;
34+
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
35+
import org.springframework.lang.Nullable;
36+
import org.springframework.test.context.ContextConfiguration;
37+
38+
import reactor.test.StepVerifier;
39+
40+
/**
41+
* Testing the reactive querying using SQL syntax.
42+
*
43+
* @author Youssef Aouichaoui
44+
*/
45+
@SpringIntegrationTest
46+
@ContextConfiguration(classes = { ReactiveSqlOperationsIntegrationTests.Config.class })
47+
@DisplayName("Using Elasticsearch SQL Reactive Client")
48+
public class ReactiveSqlOperationsIntegrationTests {
49+
@Autowired ReactiveElasticsearchOperations operations;
50+
51+
@BeforeEach
52+
void setUp() {
53+
// create index
54+
blocking(operations.indexOps(EntityForSQL.class)).createWithMapping();
55+
56+
// add data
57+
operations
58+
.saveAll(List.of(EntityForSQL.builder().withViews(3).build(), EntityForSQL.builder().withViews(0).build()),
59+
EntityForSQL.class)
60+
.blockLast();
61+
}
62+
63+
@AfterEach
64+
void tearDown() {
65+
// delete index
66+
blocking(operations.indexOps(EntityForSQL.class)).delete();
67+
}
68+
69+
// begin configuration region
70+
@Configuration
71+
@Import({ ReactiveElasticsearchTemplateConfiguration.class })
72+
static class Config {}
73+
// end region
74+
75+
@Test
76+
void when_search_with_an_sql_query() {
77+
// Given
78+
SqlQuery query = SqlQuery.builder("SELECT * FROM entity_for_sql WHERE views = 0").build();
79+
80+
// When
81+
82+
// Then
83+
operations.search(query).as(StepVerifier::create).expectNextCount(1).verifyComplete();
84+
}
85+
86+
// begin region
87+
@Document(indexName = "entity_for_sql")
88+
static class EntityForSQL {
89+
@Id private String id;
90+
private final Integer views;
91+
92+
public EntityForSQL(EntityForSQL.Builder builder) {
93+
this.views = builder.views;
94+
}
95+
96+
@Nullable
97+
public String getId() {
98+
return id;
99+
}
100+
101+
public Integer getViews() {
102+
return views;
103+
}
104+
105+
public static EntityForSQL.Builder builder() {
106+
return new EntityForSQL.Builder();
107+
}
108+
109+
static class Builder {
110+
private Integer views = 0;
111+
112+
public EntityForSQL.Builder withViews(Integer views) {
113+
this.views = views;
114+
115+
return this;
116+
}
117+
118+
public EntityForSQL build() {
119+
return new EntityForSQL(this);
120+
}
121+
}
122+
}
123+
// end region
124+
}

src/test/java/org/springframework/data/elasticsearch/core/sql/SqlOperationsIntegrationTests.java

+1-6
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ void when_search_with_an_sql_query_and_txt_format() {
103103
// begin region
104104
@Document(indexName = "entity_for_sql")
105105
static class EntityForSQL {
106-
@Nullable
107106
@Id private String id;
108107
private final Integer views;
109108

@@ -116,10 +115,6 @@ public String getId() {
116115
return id;
117116
}
118117

119-
public void setId(@Nullable String id) {
120-
this.id = id;
121-
}
122-
123118
public Integer getViews() {
124119
return views;
125120
}
@@ -129,7 +124,7 @@ public static Builder builder() {
129124
}
130125

131126
static class Builder {
132-
private Integer views;
127+
private Integer views = 0;
133128

134129
public Builder withViews(Integer views) {
135130
this.views = views;

0 commit comments

Comments
 (0)