Skip to content

Commit 4a19d9d

Browse files
Merge pull request #18942 from sIvanovKonstantyn/master
BAEL-9121 - Using TermQueries in Elastic Search
2 parents 1ea7b49 + 616f8be commit 4a19d9d

File tree

5 files changed

+177
-0
lines changed

5 files changed

+177
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.baeldung.spring.data.es.termsqueries.config;
2+
3+
import co.elastic.clients.elasticsearch.ElasticsearchClient;
4+
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
5+
import co.elastic.clients.transport.ElasticsearchTransport;
6+
import co.elastic.clients.transport.rest_client.RestClientTransport;
7+
import org.apache.http.HttpHost;
8+
import org.elasticsearch.client.RestClient;
9+
import org.elasticsearch.client.RestHighLevelClient;
10+
import org.springframework.beans.factory.annotation.Value;
11+
import org.springframework.context.annotation.Bean;
12+
import org.springframework.context.annotation.Configuration;
13+
import org.springframework.data.elasticsearch.client.ClientConfiguration;
14+
import org.springframework.data.elasticsearch.client.erhlc.AbstractElasticsearchConfiguration;
15+
import org.springframework.data.elasticsearch.client.erhlc.RestClients;
16+
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
17+
18+
@Configuration
19+
@EnableElasticsearchRepositories(basePackages = "com.baeldung.spring.data.es.termsqueries.repository")
20+
public class ElasticsearchConfiguration extends AbstractElasticsearchConfiguration {
21+
@Value("${elasticsearch.hostAndPort}")
22+
private String hostAndPort;
23+
24+
@Bean
25+
@Override
26+
public RestHighLevelClient elasticsearchClient() {
27+
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
28+
.connectedTo(hostAndPort)
29+
.build();
30+
31+
return RestClients.create(clientConfiguration).rest();
32+
}
33+
34+
@Bean
35+
public ElasticsearchClient elasticsearchLowLevelClient() {
36+
RestClient restClient = RestClient.builder(HttpHost.create("http://" + hostAndPort))
37+
.build();
38+
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
39+
return new ElasticsearchClient(transport);
40+
}
41+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.baeldung.spring.data.es.termsqueries.model;
2+
3+
import lombok.Data;
4+
import org.springframework.data.annotation.Id;
5+
import org.springframework.data.elasticsearch.annotations.Document;
6+
import org.springframework.data.elasticsearch.annotations.Field;
7+
import org.springframework.data.elasticsearch.annotations.FieldType;
8+
9+
@Data
10+
@Document(indexName = "users")
11+
public class User {
12+
@Id
13+
private String id;
14+
@Field(type = FieldType.Keyword, name = "role")
15+
private String role;
16+
@Field(type = FieldType.Text, name = "name")
17+
private String name;
18+
@Field(type = FieldType.Boolean, name = "is_active")
19+
private Boolean isActive;
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.baeldung.spring.data.es.termsqueries.repository;
2+
3+
import com.baeldung.spring.data.es.termsqueries.model.User;
4+
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
5+
6+
import java.util.List;
7+
8+
public interface UserRepository extends ElasticsearchRepository<User, String> {
9+
List<User> findByRoleAndIsActive(String role, boolean isActive);
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package com.baeldung.spring.data.es.termsqueries;
2+
3+
import co.elastic.clients.elasticsearch.ElasticsearchClient;
4+
import co.elastic.clients.elasticsearch._types.aggregations.Aggregation;
5+
import co.elastic.clients.elasticsearch._types.aggregations.MultiBucketBase;
6+
import co.elastic.clients.elasticsearch._types.aggregations.StringTermsAggregate;
7+
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
8+
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
9+
import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;
10+
import co.elastic.clients.elasticsearch.core.SearchRequest;
11+
import co.elastic.clients.elasticsearch.core.SearchResponse;
12+
import co.elastic.clients.elasticsearch.core.search.Hit;
13+
import com.baeldung.spring.data.es.termsqueries.config.ElasticsearchConfiguration;
14+
import com.baeldung.spring.data.es.termsqueries.model.User;
15+
import com.baeldung.spring.data.es.termsqueries.repository.UserRepository;
16+
import lombok.extern.slf4j.Slf4j;
17+
import org.junit.jupiter.api.Test;
18+
import org.springframework.beans.factory.annotation.Autowired;
19+
import org.springframework.boot.test.context.SpringBootTest;
20+
import org.springframework.test.context.ContextConfiguration;
21+
22+
import java.util.List;
23+
import java.util.Map;
24+
25+
import static org.assertj.core.api.Assertions.assertThat;
26+
27+
/**
28+
* This Manual test requires: Elasticsearch instance running on localhost:9200.
29+
* <p>
30+
* The following docker command can be used:
31+
* docker run -d --name elastic-test -p 9200:9200 -e "discovery.type=single-node" -e "xpack.security.enabled=false" docker.elastic.co/elasticsearch/elasticsearch:8.9.0
32+
*/
33+
@Slf4j
34+
@SpringBootTest
35+
@ContextConfiguration(classes = ElasticsearchConfiguration.class)
36+
public class ElasticSearchTermsQueriesManualTest {
37+
38+
@Autowired
39+
private ElasticsearchClient elasticsearchLowLevelClient;
40+
41+
@Autowired
42+
private UserRepository userRepository;
43+
44+
45+
@Test
46+
void givenAdminRoleAndActiveStatusFilter_whenSearch_thenReturnsOnlyActiveAdmins() throws Exception {
47+
Query roleQuery = TermQuery.of(t -> t.field("role").value("admin"))._toQuery();
48+
Query activeQuery = TermQuery.of(t -> t.field("is_active").value(true))._toQuery();
49+
Query boolQuery = BoolQuery.of(b -> b.filter(roleQuery).filter(activeQuery))._toQuery();
50+
SearchRequest request = SearchRequest.of(s -> s.index("users").query(boolQuery));
51+
52+
SearchResponse<Map> response = elasticsearchLowLevelClient.search(request, Map.class);
53+
assertThat(response.hits().hits())
54+
.hasSize(1)
55+
.first()
56+
.extracting(Hit::source)
57+
.satisfies(source -> {
58+
assertThat(source)
59+
.isNotNull()
60+
.values()
61+
.containsExactly("1", "Alice", "admin", true);
62+
});
63+
}
64+
65+
@Test
66+
void givenActiveUsers_whenAggregateByRole_thenReturnsRoleCounts() throws Exception {
67+
Query activeQuery = TermQuery.of(t -> t.field("is_active").value(true))._toQuery();
68+
69+
Aggregation aggregation = Aggregation.of(a -> a
70+
.terms(t -> t.field("role.keyword")));
71+
72+
SearchRequest request = SearchRequest.of(s -> s
73+
.index("users")
74+
.query(activeQuery)
75+
.aggregations("by_role", aggregation));
76+
77+
SearchResponse<Void> response = elasticsearchLowLevelClient.search(request, Void.class);
78+
79+
StringTermsAggregate rolesAggregate = response.aggregations().get("by_role").sterms();
80+
81+
assertThat(rolesAggregate.buckets().array())
82+
.extracting(b -> b.key().stringValue())
83+
.containsExactlyInAnyOrder("admin", "user", "manager");
84+
85+
assertThat(rolesAggregate.buckets().array())
86+
.extracting(MultiBucketBase::docCount)
87+
.contains(1L, 1L, 1L);
88+
}
89+
90+
@Test
91+
void givenAdminRoleAndActiveStatusFilter_whenSearchUsingRepository_thenReturnsOnlyActiveAdmins() throws Exception {
92+
List<User> users = userRepository.findByRoleAndIsActive("admin", true);
93+
94+
assertThat(users)
95+
.hasSize(1)
96+
.first()
97+
.satisfies(user -> {
98+
assertThat(user.getId()).isEqualTo("1");
99+
assertThat(user.getName()).isEqualTo("Alice");
100+
assertThat(user.getRole()).isEqualTo("admin");
101+
assertThat(user.getIsActive()).isTrue();
102+
});
103+
}
104+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
elasticsearch:
2+
hostAndPort: localhost:9200

0 commit comments

Comments
 (0)