1
0
mirror of synced 2026-05-24 13:13:17 +00:00

Compare commits

...

12 Commits

Author SHA1 Message Date
Mark Paluch 3a876901c0 Release version 4.4.6 (2021.2.6).
See #2335
2022-11-18 10:45:33 +01:00
Mark Paluch f59d9c6eae Prepare 4.4.6 (2021.2.6).
See #2335
2022-11-18 10:44:45 +01:00
Tiny a1094527a9 Update Dynamic.java
Original Pull Request #2357
Closes #2359

(cherry picked from commit f8ddf16c0c)
2022-11-10 20:18:24 +01:00
Peter-Josef Meisch cd8fabe499 Upgrade to Elasticsearch 7.17.7.
Original Pull Request #2347
Closes #2346
2022-11-01 20:29:29 +01:00
Mark Paluch b61f44d872 Update CI properties.
See #2335
2022-10-31 13:18:18 +01:00
Peter-Josef Meisch 933ebab0e0 Fix repository methods value converting.
Original Pull Request #2339
Closes #2338

(cherry picked from commit e67150a55b)
2022-10-19 22:42:06 +02:00
Spring Builds edac49c470 After release cleanups.
See #2334
2022-10-13 13:46:55 +00:00
Spring Builds cb65bc5ead Prepare next development iteration.
See #2334
2022-10-13 13:46:42 +00:00
Spring Builds c99d633ac6 Release version 4.4.5 (2021.2.5).
See #2334
2022-10-13 13:24:06 +00:00
Spring Builds 7372f9120d Prepare 4.4.5 (2021.2.5).
See #2334
2022-10-13 13:21:37 +00:00
Spring Builds c09138f985 After release cleanups.
See #2296
2022-10-13 09:23:59 +00:00
Spring Builds 5ac167290c Prepare next development iteration.
See #2296
2022-10-13 09:23:46 +00:00
23 changed files with 690 additions and 193 deletions
+6 -6
View File
@@ -1,7 +1,7 @@
# Java versions
java.main.tag=8u332-b09-jdk
java.next.tag=11.0.15_10-jdk
java.lts.tag=17.0.3_7-jdk
java.main.tag=8u345-b01-jdk-focal
java.next.tag=11.0.16.1_1-jdk-focal
java.lts.tag=17.0.4.1_1-jdk-focal
# Docker container images - standard
docker.java.main.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclipse-temurin:${java.main.tag}
@@ -10,14 +10,14 @@ docker.java.lts.image=harbor-repo.vmware.com/dockerhub-proxy-cache/library/eclip
# Supported versions of MongoDB
docker.mongodb.4.0.version=4.0.28
docker.mongodb.4.4.version=4.4.12
docker.mongodb.5.0.version=5.0.6
docker.mongodb.4.4.version=4.4.17
docker.mongodb.5.0.version=5.0.13
# Supported versions of Redis
docker.redis.6.version=6.2.6
# Supported versions of Cassandra
docker.cassandra.3.version=3.11.12
docker.cassandra.3.version=3.11.14
# Docker environment settings
docker.java.inside.basic=-v $HOME:/tmp/jenkins-home
+5 -5
View File
@@ -5,12 +5,12 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>4.4.4</version>
<version>4.4.6</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>2.7.4</version>
<version>2.7.6</version>
</parent>
<name>Spring Data Elasticsearch</name>
@@ -19,12 +19,12 @@
<properties>
<!-- version of the RestHighLevelClient -->
<elasticsearch-rhlc>7.17.6</elasticsearch-rhlc>
<elasticsearch-rhlc>7.17.7</elasticsearch-rhlc>
<!-- version of the new ElasticsearchClient -->
<elasticsearch-java>7.17.6</elasticsearch-java>
<elasticsearch-java>7.17.7</elasticsearch-java>
<log4j>2.17.1</log4j>
<netty>4.1.65.Final</netty>
<springdata.commons>2.7.4</springdata.commons>
<springdata.commons>2.7.6</springdata.commons>
<testcontainers>1.16.2</testcontainers>
<blockhound-junit>1.0.6.RELEASE</blockhound-junit>
<java-module-name>spring.data.elasticsearch</java-module-name>
+1 -1
View File
@@ -37,7 +37,7 @@ built and tested.
[cols="^,^,^,^,^",options="header"]
|===
| Spring Data Release Train | Spring Data Elasticsearch | Elasticsearch | Spring Framework | Spring Boot
| 2021.2 (Raj) | 4.4.x | 7.17.6 | 5.3.x | 2.7.x
| 2021.2 (Raj) | 4.4.x | 7.17.7 | 5.3.x | 2.7.x
| 2021.1 (Q) | 4.3.x | 7.15.2 | 5.3.x | 2.6.x
| 2021.0 (Pascal) | 4.2.xfootnote:oom[Out of maintenance] | 7.12.0 | 5.3.x | 2.5.x
| 2020.0 (Ockham)footnote:oom[] | 4.1.xfootnote:oom[] | 7.9.3 | 5.3.2 | 2.4.x
@@ -82,7 +82,7 @@ The dependencies for the new Elasticsearch client are still optional in Spring D
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>7.17.6</version>
<version>7.17.7</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
@@ -93,7 +93,7 @@ The dependencies for the new Elasticsearch client are still optional in Spring D
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId> <!-- is Apache 2-->
<version>7.17.6</version>
<version>7.17.7</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
@@ -46,7 +46,7 @@ public enum Dynamic {
/**
* Inherit the dynamic setting from their parent object or from the mapping type.
*/
INHERIT("nherit");
INHERIT("inherit");
private final String mappedName;
@@ -40,6 +40,7 @@ import org.springframework.data.elasticsearch.core.document.SearchDocument;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
import org.springframework.data.elasticsearch.core.query.BaseQuery;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
@@ -1156,6 +1157,13 @@ public class MappingElasticsearchConverter
Assert.notNull(query, "query must not be null");
if (query instanceof BaseQuery) {
if (((BaseQuery) query).queryIsUpdatedByConverter()) {
return;
}
}
if (domainClass == null) {
return;
}
@@ -1165,6 +1173,10 @@ public class MappingElasticsearchConverter
if (query instanceof CriteriaQuery) {
updatePropertiesInCriteriaQuery((CriteriaQuery) query, domainClass);
}
if (query instanceof BaseQuery) {
((BaseQuery) query).setQueryIsUpdatedByConverter(true);
}
}
private void updatePropertiesInFieldsAndSourceFilter(Query query, Class<?> domainClass) {
@@ -73,6 +73,8 @@ public class BaseQuery implements Query {
protected final List<RuntimeField> runtimeFields = new ArrayList<>();
@Nullable protected List<IndexBoost> indicesBoost;
private boolean queryIsUpdatedByConverter = false;
public BaseQuery() {}
public <Q extends BaseQuery, B extends BaseQueryBuilder<Q, B>> BaseQuery(BaseQueryBuilder<Q, B> builder) {
@@ -440,4 +442,23 @@ public class BaseQuery implements Query {
public List<IndexBoost> getIndicesBoost() {
return indicesBoost;
}
/**
* used internally. Not considered part of the API.
*
* @since 5.0
*/
public boolean queryIsUpdatedByConverter() {
return queryIsUpdatedByConverter;
}
/**
* used internally. Not considered part of the API.
*
* @since 5.0
*/
public void setQueryIsUpdatedByConverter(boolean queryIsUpdatedByConverter) {
this.queryIsUpdatedByConverter = queryIsUpdatedByConverter;
}
}
@@ -15,9 +15,24 @@
*/
package org.springframework.data.elasticsearch.repository.query;
import java.util.Collections;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHitSupport;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchHitsImpl;
import org.springframework.data.elasticsearch.core.TotalHitsRelation;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.util.StreamUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* AbstractElasticsearchRepositoryQuery
@@ -32,11 +47,13 @@ public abstract class AbstractElasticsearchRepositoryQuery implements Repository
protected static final int DEFAULT_STREAM_BATCH_SIZE = 500;
protected ElasticsearchQueryMethod queryMethod;
protected ElasticsearchOperations elasticsearchOperations;
protected final ElasticsearchConverter elasticsearchConverter;
public AbstractElasticsearchRepositoryQuery(ElasticsearchQueryMethod queryMethod,
ElasticsearchOperations elasticsearchOperations) {
this.queryMethod = queryMethod;
this.elasticsearchOperations = elasticsearchOperations;
this.elasticsearchConverter = elasticsearchOperations.getElasticsearchConverter();
}
@Override
@@ -49,4 +66,117 @@ public abstract class AbstractElasticsearchRepositoryQuery implements Repository
* @since 4.2
*/
public abstract boolean isCountQuery();
protected abstract boolean isDeleteQuery();
protected abstract boolean isExistsQuery();
@Override
public Object execute(Object[] parameters) {
ParametersParameterAccessor parameterAccessor = getParameterAccessor(parameters);
Class<?> clazz = getResultClass();
Query query = createQuery(parameters);
IndexCoordinates index = elasticsearchOperations.getIndexCoordinatesFor(clazz);
Object result = null;
if (isDeleteQuery()) {
result = countOrGetDocumentsForDelete(query, parameterAccessor);
elasticsearchOperations.delete(query, clazz, index);
elasticsearchOperations.indexOps(index).refresh();
} else if (isCountQuery()) {
result = elasticsearchOperations.count(query, clazz, index);
} else if (isExistsQuery()) {
result = elasticsearchOperations.count(query, clazz, index) > 0;
} else if (queryMethod.isPageQuery()) {
query.setPageable(parameterAccessor.getPageable());
SearchHits<?> searchHits = elasticsearchOperations.search(query, clazz, index);
if (queryMethod.isSearchPageMethod()) {
result = SearchHitSupport.searchPageFor(searchHits, query.getPageable());
} else {
result = SearchHitSupport.unwrapSearchHits(SearchHitSupport.searchPageFor(searchHits, query.getPageable()));
}
} else if (queryMethod.isStreamQuery()) {
query.setPageable(parameterAccessor.getPageable().isPaged() ? parameterAccessor.getPageable()
: PageRequest.of(0, DEFAULT_STREAM_BATCH_SIZE));
result = StreamUtils.createStreamFromIterator(elasticsearchOperations.searchForStream(query, clazz, index));
} else if (queryMethod.isCollectionQuery()) {
if (parameterAccessor.getPageable().isUnpaged()) {
int itemCount = (int) elasticsearchOperations.count(query, clazz, index);
if (itemCount == 0) {
result = new SearchHitsImpl<>(0, TotalHitsRelation.EQUAL_TO, Float.NaN, null, Collections.emptyList(), null,
null);
} else {
query.setPageable(PageRequest.of(0, Math.max(1, itemCount)));
}
} else {
query.setPageable(parameterAccessor.getPageable());
}
if (result == null) {
result = elasticsearchOperations.search(query, clazz, index);
}
} else {
result = elasticsearchOperations.searchOne(query, clazz, index);
}
return (queryMethod.isNotSearchHitMethod() && queryMethod.isNotSearchPageMethod())
? SearchHitSupport.unwrapSearchHits(result)
: result;
}
public Query createQuery(Object[] parameters) {
Class<?> clazz = getResultClass();
ParametersParameterAccessor parameterAccessor = getParameterAccessor(parameters);
Query query = createQuery(parameterAccessor);
Assert.notNull(query, "unsupported query");
if (queryMethod.hasAnnotatedHighlight()) {
query.setHighlightQuery(queryMethod.getAnnotatedHighlightQuery());
}
return query;
}
private Class<?> getResultClass() {
return queryMethod.getResultProcessor().getReturnedType().getDomainType();
}
private ParametersParameterAccessor getParameterAccessor(Object[] parameters) {
return new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
}
@Nullable
private Object countOrGetDocumentsForDelete(Query query, ParametersParameterAccessor accessor) {
Object result = null;
Class<?> entityClass = queryMethod.getEntityInformation().getJavaType();
IndexCoordinates index = elasticsearchOperations.getIndexCoordinatesFor(entityClass);
if (queryMethod.isCollectionQuery()) {
if (accessor.getPageable().isUnpaged()) {
int itemCount = (int) elasticsearchOperations.count(query, entityClass, index);
query.setPageable(PageRequest.of(0, Math.max(1, itemCount)));
} else {
query.setPageable(accessor.getPageable());
}
result = elasticsearchOperations.search(query, entityClass, index);
}
if (ClassUtils.isAssignable(Number.class, queryMethod.getReturnedObjectType())) {
result = elasticsearchOperations.count(query, entityClass, index);
}
return result;
}
protected abstract Query createQuery(ParametersParameterAccessor accessor);
}
@@ -15,26 +15,18 @@
*/
package org.springframework.data.elasticsearch.repository.query;
import java.util.Collections;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHitSupport;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchHitsImpl;
import org.springframework.data.elasticsearch.core.TotalHitsRelation;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.BaseQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.repository.query.parser.ElasticsearchQueryCreator;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.data.util.StreamUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* ElasticsearchPartQuery
@@ -49,13 +41,11 @@ import org.springframework.util.ClassUtils;
public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery {
private final PartTree tree;
private final ElasticsearchConverter elasticsearchConverter;
private final MappingContext<?, ElasticsearchPersistentProperty> mappingContext;
public ElasticsearchPartQuery(ElasticsearchQueryMethod method, ElasticsearchOperations elasticsearchOperations) {
super(method, elasticsearchOperations);
this.tree = new PartTree(queryMethod.getName(), queryMethod.getResultProcessor().getReturnedType().getDomainType());
this.elasticsearchConverter = elasticsearchOperations.getElasticsearchConverter();
this.mappingContext = elasticsearchConverter.getMappingContext();
}
@@ -65,106 +55,24 @@ public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery
}
@Override
public Object execute(Object[] parameters) {
Class<?> clazz = queryMethod.getResultProcessor().getReturnedType().getDomainType();
ParametersParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
protected boolean isDeleteQuery() {
return tree.isDelete();
}
CriteriaQuery query = createQuery(accessor);
@Override
protected boolean isExistsQuery() {
return tree.isExistsProjection();
}
Assert.notNull(query, "unsupported query");
protected Query createQuery(ParametersParameterAccessor accessor) {
elasticsearchConverter.updateQuery(query, clazz);
if (queryMethod.hasAnnotatedHighlight()) {
query.setHighlightQuery(queryMethod.getAnnotatedHighlightQuery());
}
IndexCoordinates index = elasticsearchOperations.getIndexCoordinatesFor(clazz);
Object result = null;
BaseQuery query = new ElasticsearchQueryCreator(tree, accessor, mappingContext).createQuery();
if (tree.isLimiting()) {
// noinspection ConstantConditions
query.setMaxResults(tree.getMaxResults());
}
if (tree.isDelete()) {
result = countOrGetDocumentsForDelete(query, accessor);
elasticsearchOperations.delete(query, clazz, index);
elasticsearchOperations.indexOps(index).refresh();
} else if (queryMethod.isPageQuery()) {
query.setPageable(accessor.getPageable());
SearchHits<?> searchHits = elasticsearchOperations.search(query, clazz, index);
if (queryMethod.isSearchPageMethod()) {
result = SearchHitSupport.searchPageFor(searchHits, query.getPageable());
} else {
result = SearchHitSupport.unwrapSearchHits(SearchHitSupport.searchPageFor(searchHits, query.getPageable()));
}
} else if (queryMethod.isStreamQuery()) {
if (accessor.getPageable().isUnpaged()) {
query.setPageable(PageRequest.of(0, DEFAULT_STREAM_BATCH_SIZE));
} else {
query.setPageable(accessor.getPageable());
}
result = StreamUtils.createStreamFromIterator(elasticsearchOperations.searchForStream(query, clazz, index));
} else if (queryMethod.isCollectionQuery()) {
if (accessor.getPageable().isUnpaged()) {
int itemCount = (int) elasticsearchOperations.count(query, clazz, index);
if (itemCount == 0) {
result = new SearchHitsImpl<>(0, TotalHitsRelation.EQUAL_TO, Float.NaN, null, Collections.emptyList(), null,
null);
} else {
query.setPageable(PageRequest.of(0, Math.max(1, itemCount)));
}
} else {
query.setPageable(accessor.getPageable());
}
if (result == null) {
result = elasticsearchOperations.search(query, clazz, index);
}
} else if (tree.isCountProjection()) {
result = elasticsearchOperations.count(query, clazz, index);
} else if (tree.isExistsProjection()) {
long count = elasticsearchOperations.count(query, clazz, index);
result = count > 0;
} else {
result = elasticsearchOperations.searchOne(query, clazz, index);
}
return (queryMethod.isNotSearchHitMethod() && queryMethod.isNotSearchPageMethod())
? SearchHitSupport.unwrapSearchHits(result)
: result;
}
@Nullable
private Object countOrGetDocumentsForDelete(CriteriaQuery query, ParametersParameterAccessor accessor) {
Object result = null;
Class<?> clazz = queryMethod.getEntityInformation().getJavaType();
IndexCoordinates index = elasticsearchOperations.getIndexCoordinatesFor(clazz);
if (queryMethod.isCollectionQuery()) {
if (accessor.getPageable().isUnpaged()) {
int itemCount = (int) elasticsearchOperations.count(query, clazz, index);
query.setPageable(PageRequest.of(0, Math.max(1, itemCount)));
} else {
query.setPageable(accessor.getPageable());
}
result = elasticsearchOperations.search(query, clazz, index);
}
if (ClassUtils.isAssignable(Number.class, queryMethod.getReturnedObjectType())) {
result = elasticsearchOperations.count(query, clazz, index);
}
return result;
}
public CriteriaQuery createQuery(ParametersParameterAccessor accessor) {
return new ElasticsearchQueryCreator(tree, accessor, mappingContext).createQuery();
return query;
}
}
@@ -15,16 +15,11 @@
*/
package org.springframework.data.elasticsearch.repository.query;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHitSupport;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.StringQuery;
import org.springframework.data.elasticsearch.repository.support.StringQueryUtil;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.util.StreamUtils;
import org.springframework.util.Assert;
/**
@@ -38,13 +33,13 @@ import org.springframework.util.Assert;
*/
public class ElasticsearchStringQuery extends AbstractElasticsearchRepositoryQuery {
private String query;
private final String queryString;
public ElasticsearchStringQuery(ElasticsearchQueryMethod queryMethod, ElasticsearchOperations elasticsearchOperations,
String query) {
String queryString) {
super(queryMethod, elasticsearchOperations);
Assert.notNull(query, "Query cannot be empty");
this.query = query;
Assert.notNull(queryString, "Query cannot be empty");
this.queryString = queryString;
}
@Override
@@ -53,54 +48,20 @@ public class ElasticsearchStringQuery extends AbstractElasticsearchRepositoryQue
}
@Override
public Object execute(Object[] parameters) {
Class<?> clazz = queryMethod.getResultProcessor().getReturnedType().getDomainType();
ParametersParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
StringQuery stringQuery = createQuery(accessor);
Assert.notNull(stringQuery, "unsupported query");
if (queryMethod.hasAnnotatedHighlight()) {
stringQuery.setHighlightQuery(queryMethod.getAnnotatedHighlightQuery());
}
IndexCoordinates index = elasticsearchOperations.getIndexCoordinatesFor(clazz);
Object result = null;
if (isCountQuery()) {
result = elasticsearchOperations.count(stringQuery, clazz, index);
} else if (queryMethod.isPageQuery()) {
stringQuery.setPageable(accessor.getPageable());
SearchHits<?> searchHits = elasticsearchOperations.search(stringQuery, clazz, index);
if (queryMethod.isSearchPageMethod()) {
result = SearchHitSupport.searchPageFor(searchHits, stringQuery.getPageable());
} else {
result = SearchHitSupport
.unwrapSearchHits(SearchHitSupport.searchPageFor(searchHits, stringQuery.getPageable()));
}
} else if (queryMethod.isStreamQuery()) {
stringQuery.setPageable(
accessor.getPageable().isPaged() ? accessor.getPageable() : PageRequest.of(0, DEFAULT_STREAM_BATCH_SIZE));
result = StreamUtils.createStreamFromIterator(elasticsearchOperations.searchForStream(stringQuery, clazz, index));
} else if (queryMethod.isCollectionQuery()) {
stringQuery.setPageable(accessor.getPageable().isPaged() ? accessor.getPageable() : Pageable.unpaged());
result = elasticsearchOperations.search(stringQuery, clazz, index);
} else {
result = elasticsearchOperations.searchOne(stringQuery, clazz, index);
}
return (queryMethod.isNotSearchHitMethod() && queryMethod.isNotSearchPageMethod())
? SearchHitSupport.unwrapSearchHits(result)
: result;
protected boolean isDeleteQuery() {
return false;
}
protected StringQuery createQuery(ParametersParameterAccessor parameterAccessor) {
@Override
protected boolean isExistsQuery() {
return false;
}
protected Query createQuery(ParametersParameterAccessor parameterAccessor) {
String queryString = new StringQueryUtil(elasticsearchOperations.getElasticsearchConverter().getConversionService())
.replacePlaceholders(this.query, parameterAccessor);
.replacePlaceholders(this.queryString, parameterAccessor);
return new StringQuery(queryString);
}
}
@@ -61,7 +61,6 @@ final public class StringQueryUtil {
String parameterValue = "null";
if (parameter != null) {
parameterValue = convert(parameter);
}
+3 -1
View File
@@ -1,4 +1,4 @@
Spring Data Elasticsearch 4.4.4 (2021.2.4)
Spring Data Elasticsearch 4.4.6 (2021.2.6)
Copyright (c) [2013-2021] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
@@ -39,6 +39,8 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
@@ -21,8 +21,8 @@ import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.ElasticsearchPartQueryIntegrationTests;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration;
/**
@@ -36,12 +36,11 @@ public class ElasticsearchPartQueryELCIntegrationTests extends ElasticsearchPart
static class Config {}
@Override
protected String buildQueryString(CriteriaQuery criteriaQuery, Class<?> clazz) {
protected String buildQueryString(Query query, Class<?> clazz) {
JacksonJsonpMapper jsonpMapper = new JacksonJsonpMapper();
RequestConverter requestConverter = new RequestConverter(operations.getElasticsearchConverter(), jsonpMapper);
SearchRequest request = requestConverter.searchRequest(criteriaQuery, clazz, IndexCoordinates.of("dummy"), false,
false);
SearchRequest request = requestConverter.searchRequest(query, clazz, IndexCoordinates.of("dummy"), false, false);
return JsonUtils.toJson(request, jsonpMapper);
// return "{\"query\":" + JsonUtils.toJson(request.query(), jsonpMapper) + "}";
@@ -19,8 +19,8 @@ import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.ElasticsearchPartQueryIntegrationTests;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.test.context.ContextConfiguration;
@@ -38,9 +38,9 @@ public class ElasticsearchPartQueryERHLCIntegrationTests extends ElasticsearchPa
@Import({ ElasticsearchRestTemplateConfiguration.class })
static class Config {}
protected String buildQueryString(CriteriaQuery criteriaQuery, Class<?> clazz) {
protected String buildQueryString(Query query, Class<?> clazz) {
SearchSourceBuilder source = new RequestFactory(operations.getElasticsearchConverter())
.searchRequest(criteriaQuery, clazz, IndexCoordinates.of("dummy")).source();
.searchRequest(query, clazz, IndexCoordinates.of("dummy")).source();
// remove defaultboost values
return source.toString().replaceAll("(\\^\\d+\\.\\d+)", "");
}
@@ -103,15 +103,15 @@ public class ReactiveElasticsearchELCIntegrationTests extends ReactiveElasticsea
assertThat(bucketList.size()).isEqualTo(3);
AtomicInteger count = new AtomicInteger();
bucketList.forEach(stringTermsBucket -> {
if ("message".equals(stringTermsBucket.key())) {
if ("message".equals(stringTermsBucket.key().stringValue())) {
count.getAndIncrement();
assertThat(stringTermsBucket.docCount()).isEqualTo(3);
}
if ("some".equals(stringTermsBucket.key())) {
if ("some".equals(stringTermsBucket.key().stringValue())) {
count.getAndIncrement();
assertThat(stringTermsBucket.docCount()).isEqualTo(2);
}
if ("other".equals(stringTermsBucket.key())) {
if ("other".equals(stringTermsBucket.key().stringValue())) {
count.getAndIncrement();
assertThat(stringTermsBucket.docCount()).isEqualTo(1);
}
@@ -36,7 +36,6 @@ import org.springframework.data.elasticsearch.repository.query.ElasticsearchPart
import org.springframework.data.elasticsearch.repository.query.ElasticsearchQueryMethod;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.lang.Nullable;
/**
@@ -637,19 +636,18 @@ public abstract class ElasticsearchPartQueryIntegrationTests {
new DefaultRepositoryMetadata(SampleRepository.class), new SpelAwareProxyProjectionFactory(),
operations.getElasticsearchConverter().getMappingContext());
ElasticsearchPartQuery partQuery = new ElasticsearchPartQuery(queryMethod, operations);
CriteriaQuery criteriaQuery = partQuery
.createQuery(new ParametersParameterAccessor(queryMethod.getParameters(), parameters));
return buildQueryString(criteriaQuery, Book.class);
Query query = partQuery.createQuery(parameters);
return buildQueryString(query, Book.class);
}
/**
* builds the query String that would be sent to Elasticsearch
*
* @param criteriaQuery the {@link CriteriaQuery}
* @param query the {@link Query}
* @param clazz the entity class
* @return the created query string
*/
abstract protected String buildQueryString(CriteriaQuery criteriaQuery, Class<?> clazz);
abstract protected String buildQueryString(Query query, Class<?> clazz);
@FunctionalInterface
interface AssertFunction {
@@ -0,0 +1,43 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.repository.query.valueconverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchTemplateConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
* @since 5.0
*/
@ContextConfiguration(classes = { ReactiveValueConverterELCIntegrationTests.Config.class })
public class ReactiveValueConverterELCIntegrationTests extends ReactiveValueConverterIntegrationTests {
@Configuration
@Import({ ReactiveElasticsearchTemplateConfiguration.class })
@EnableReactiveElasticsearchRepositories(considerNestedRepositories = true)
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("reactive-valueconverter");
}
}
}
@@ -0,0 +1,43 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.repository.query.valueconverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ReactiveElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* @author Peter-Josef Meisch
* @since 5.0
*/
@ContextConfiguration(classes = { ReactiveValueConverterERHLCIntegrationTests.Config.class })
public class ReactiveValueConverterERHLCIntegrationTests extends ReactiveValueConverterIntegrationTests {
@Configuration
@Import({ ReactiveElasticsearchRestTemplateConfiguration.class })
@EnableReactiveElasticsearchRepositories(considerNestedRepositories = true)
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("reactive-valueconverter-es7");
}
}
}
@@ -0,0 +1,153 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.repository.query.valueconverter;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.elasticsearch.annotations.FieldType.*;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.annotations.ValueConverter;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.lang.Boolean;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.lang.Nullable;
/**
* Integration tests to check that {@link org.springframework.data.elasticsearch.annotations.ValueConverter} annotated
* properties are handle correctly (method name derived queries, for
*
* @{@link org.springframework.data.elasticsearch.core.query.Query} methods we don't know which parameters map to which
* property.
* @author Peter-Josef Meisch
*/
@SpringIntegrationTest
public abstract class ReactiveValueConverterIntegrationTests {
@Autowired private IndexNameProvider indexNameProvider;
@Autowired private ReactiveElasticsearchOperations operations;
@Autowired private EntityRepository repository;
@BeforeEach
void setUp() {
indexNameProvider.increment();
operations.indexOps(Entity.class).createWithMapping().block();
}
@Test
@Order(java.lang.Integer.MAX_VALUE)
void cleanup() {
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + '*')).delete().block();
}
@Test // #2338
@DisplayName("should apply ValueConverter")
void shouldApplyValueConverter() {
ValueConverterIntegrationTests.Entity entity = new ValueConverterIntegrationTests.Entity();
entity.setId("42");
entity.setText("answer");
operations.save(entity).block();
repository.queryByText("text-answer") //
.as(StepVerifier::create) //
.expectNextCount(1) //
.verifyComplete();
repository.findByText("answer") //
.as(StepVerifier::create) //
.expectNextCount(1) //
.verifyComplete();
}
interface EntityRepository extends ReactiveElasticsearchRepository<Entity, String> {
Flux<SearchHit<Entity>> findByText(String text);
@Query("{ \"term\": { \"text\": \"?0\" } }")
Flux<SearchHit<Entity>> queryByText(String text);
}
@Document(indexName = "#{@indexNameProvider.indexName()}")
static class Entity {
@Id
@Nullable private String id;
@Field(type = FieldType.Keyword)
@ValueConverter(ValueConverterIntegrationTests.TextConverter.class)
@Nullable private String text;
@Nullable
public String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
@Nullable
public String getText() {
return text;
}
public void setText(@Nullable String text) {
this.text = text;
}
}
static class TextConverter implements PropertyValueConverter {
public static final String PREFIX = "text-";
@Override
public Object write(Object value) {
return PREFIX + value.toString();
}
@Override
public Object read(Object value) {
String valueString = value.toString();
if (valueString.startsWith(PREFIX)) {
return valueString.substring(PREFIX.length());
} else {
return value;
}
}
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.repository.query.valueconverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* {@link ValueConverterIntegrationTests} using a Repository backed by an ElasticsearchTemplate.
*
* @author Peter-Josef Meisch
* @since 5.0
*/
@ContextConfiguration(classes = { ValueConverterELCIntegrationTests.Config.class })
public class ValueConverterELCIntegrationTests extends ValueConverterIntegrationTests {
@Configuration
@Import({ ElasticsearchTemplateConfiguration.class })
@EnableElasticsearchRepositories(considerNestedRepositories = true)
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("valueconverter");
}
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.repository.query.valueconverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.test.context.ContextConfiguration;
/**
* {@link ValueConverterIntegrationTests} using a Repository backed by an ElasticsearchTemplate.
*
* @author Peter-Josef Meisch
* @since 5.0
*/
@ContextConfiguration(classes = { ValueConverterERHLCIntegrationTests.Config.class })
public class ValueConverterERHLCIntegrationTests extends ValueConverterIntegrationTests {
@Configuration
@Import({ ElasticsearchRestTemplateConfiguration.class })
@EnableElasticsearchRepositories(considerNestedRepositories = true)
static class Config {
@Bean
IndexNameProvider indexNameProvider() {
return new IndexNameProvider("valueconverter-es7");
}
}
}
@@ -0,0 +1,140 @@
/*
* Copyright 2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.elasticsearch.repository.query.valueconverter;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.annotations.ValueConverter;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.mapping.PropertyValueConverter;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
import org.springframework.lang.Nullable;
/**
* Integration tests to check that {@link org.springframework.data.elasticsearch.annotations.ValueConverter} annotated
* properties are handle correctly (method name derived queries, for
*
* @{@link org.springframework.data.elasticsearch.core.query.Query} methods we don't know which parameters map to which
* property.
* @author Peter-Josef Meisch
*/
@SpringIntegrationTest
abstract class ValueConverterIntegrationTests {
@Autowired private EntityRepository repository;
@Autowired ElasticsearchOperations operations;
@Autowired IndexNameProvider indexNameProvider;
@BeforeEach
public void before() {
indexNameProvider.increment();
operations.indexOps(Entity.class).createWithMapping();
}
@Test
@Order(Integer.MAX_VALUE)
void cleanup() {
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + '*')).delete();
}
@Test // #2338
@DisplayName("should apply ValueConverter")
void shouldApplyValueConverter() {
Entity entity = new Entity();
entity.setId("42");
entity.setText("answer");
operations.save(entity);
SearchHits<Entity> searchHits = repository.queryByText("text-answer");
assertThat(searchHits.getTotalHits()).isEqualTo(1);
searchHits = repository.findByText("answer");
assertThat(searchHits.getTotalHits()).isEqualTo(1);
}
interface EntityRepository extends ElasticsearchRepository<Entity, String> {
SearchHits<Entity> findByText(String text);
@Query("{ \"term\": { \"text\": \"?0\" } }")
SearchHits<Entity> queryByText(String text);
}
@Document(indexName = "#{@indexNameProvider.indexName()}")
static class Entity {
@Id
@Nullable private String id;
@Field(type = FieldType.Keyword)
@ValueConverter(TextConverter.class)
@Nullable private String text;
@Nullable
public String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
@Nullable
public String getText() {
return text;
}
public void setText(@Nullable String text) {
this.text = text;
}
}
static class TextConverter implements PropertyValueConverter {
public static final String PREFIX = "text-";
@Override
public Object write(Object value) {
return PREFIX + value.toString();
}
@Override
public Object read(Object value) {
String valueString = value.toString();
if (valueString.startsWith(PREFIX)) {
return valueString.substring(PREFIX.length());
} else {
return value;
}
}
}
}
@@ -15,7 +15,7 @@
#
#
sde.testcontainers.image-name=docker.elastic.co/elasticsearch/elasticsearch
sde.testcontainers.image-version=7.17.6
sde.testcontainers.image-version=7.17.7
#
#
# needed as we do a DELETE /* at the end of the tests, will be required from 8.0 on, produces a warning since 7.13