1
0
mirror of synced 2026-05-23 12:43:17 +00:00

Compare commits

..

28 Commits

Author SHA1 Message Date
Mark Paluch 4729b9cfdc Release version 5.2.7 (2023.1.7).
See #2912
2024-06-14 10:04:22 +02:00
Mark Paluch aa16c0c5ab Prepare 5.2.7 (2023.1.7).
See #2912
2024-06-14 10:04:05 +02:00
Peter-Josef Meisch 5e507fa163 Fix max dim value for dense vector.
Closes #2911

(cherry picked from commit e997b39f68)
(cherry picked from commit ba9edf8ec8)
2024-05-18 18:27:59 +02:00
Mark Paluch a355537d78 After release cleanups.
See #2895
2024-05-17 11:24:38 +02:00
Mark Paluch 4fbf9f8b5a Prepare next development iteration.
See #2895
2024-05-17 11:24:37 +02:00
Mark Paluch 2cb27c26d7 Release version 5.2.6 (2023.1.6).
See #2895
2024-05-17 11:22:24 +02:00
Mark Paluch c322797eab Prepare 5.2.6 (2023.1.6).
See #2895
2024-05-17 11:22:00 +02:00
Peter-Josef Meisch c81958f62d Fix implementation of explicit refresh policy.
Original Pull Request #2908
Closes #2907

(cherry picked from commit 94a40a7a75)
2024-05-10 09:35:20 +02:00
Peter-Josef Meisch ed18be6f1c Fix parameter in Order constructor.
Closes #2897

(cherry picked from commit ad66510e9e)
2024-04-15 21:11:35 +02:00
Mark Paluch b3a21fbb7c After release cleanups.
See #2873
2024-04-12 10:17:29 +02:00
Mark Paluch d874b9d000 Prepare next development iteration.
See #2873
2024-04-12 10:17:28 +02:00
Mark Paluch 36e6a4b328 Release version 5.2.5 (2023.1.5).
See #2873
2024-04-12 10:15:11 +02:00
Mark Paluch 3710e47abd Prepare 5.2.5 (2023.1.5).
See #2873
2024-04-12 10:14:56 +02:00
Mark Paluch c40abc267b After release cleanups.
See #2850
2024-03-15 10:20:09 +01:00
Mark Paluch b479184d83 Prepare next development iteration.
See #2850
2024-03-15 10:20:08 +01:00
Mark Paluch f2370bc7df Release version 5.2.4 (2023.1.4).
See #2850
2024-03-15 10:17:49 +01:00
Mark Paluch 5f045d104e Prepare 5.2.4 (2023.1.4).
See #2850
2024-03-15 10:17:30 +01:00
Peter-Josef Meisch b52e8d1431 Fix setting setting id in bulkrequest.
Original Pull Request #2862
Closes #2861

(cherry picked from commit debf04b499)
2024-02-28 21:45:49 +01:00
Mark Paluch e86bb771be After release cleanups.
See #2825
2024-02-16 13:53:53 +01:00
Mark Paluch c9fbc17109 Prepare next development iteration.
See #2825
2024-02-16 13:53:51 +01:00
Mark Paluch 397e155d5e Release version 5.2.3 (2023.1.3).
See #2825
2024-02-16 13:50:30 +01:00
Mark Paluch 4e52594d12 Prepare 5.2.3 (2023.1.3).
See #2825
2024-02-16 13:50:02 +01:00
Peter-Josef Meisch 310f0ed567 Fix criteria filter in native query.
Original Pull Request #2846
Closes #2840

(cherry picked from commit e9ecebd9ef)
2024-02-06 21:23:50 +01:00
Peter-Josef Meisch dab0be04db Fix store null values implementation.
Original Pull Request #2842

(cherry picked from commit 0a1e20579e)
2024-01-31 17:32:41 +01:00
Mark Paluch e8f269d9ec Refine Artifactory build name.
See #2772
2024-01-31 15:13:58 +01:00
Peter-Josef Meisch 1e9824ae2d Upgrade to Elasticsearch 8.11.4.
Original Pull Request #2829
Closes #2827
2024-01-19 07:18:40 +01:00
Mark Paluch c9b8b3f33d After release cleanups.
See #2804
2024-01-12 10:54:33 +01:00
Mark Paluch a806553169 Prepare next development iteration.
See #2804
2024-01-12 10:54:32 +01:00
15 changed files with 258 additions and 48 deletions
Vendored
+1 -1
View File
@@ -108,7 +108,7 @@ pipeline {
"-Dartifactory.password=${ARTIFACTORY_PSW} " +
"-Dartifactory.staging-repository=${p['artifactory.repository.snapshot']} " +
"-Dartifactory.build-name=spring-data-elasticsearch " +
"-Dartifactory.build-number=${BUILD_NUMBER} " +
"-Dartifactory.build-number=spring-data-elasticsearch-${BRANCH_NAME}-build-${BUILD_NUMBER} " +
"-Dmaven.test.skip=true clean deploy -U -B"
}
}
+4 -4
View File
@@ -5,12 +5,12 @@
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>5.2.2</version>
<version>5.2.7</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>3.2.2</version>
<version>3.2.7</version>
</parent>
<name>Spring Data Elasticsearch</name>
@@ -18,10 +18,10 @@
<url>https://github.com/spring-projects/spring-data-elasticsearch</url>
<properties>
<springdata.commons>3.2.2</springdata.commons>
<springdata.commons>3.2.7</springdata.commons>
<!-- version of the ElasticsearchClient -->
<elasticsearch-java>8.11.3</elasticsearch-java>
<elasticsearch-java>8.11.4</elasticsearch-java>
<blockhound-junit>1.0.8.RELEASE</blockhound-junit>
<hoverfly>0.14.4</hoverfly>
@@ -1,6 +1,10 @@
[[new-features]]
= What's new
[[new-features.5-2-3]]
== New in Spring Data Elasticsearch 5.2.2
* Upgrade to Elasticsearch 8.11.4
[[new-features.5-2-2]]
== New in Spring Data Elasticsearch 5.2.2
* Upgrade to Elasticsearch 8.11.3
@@ -6,7 +6,7 @@ The following table shows the Elasticsearch and Spring versions that are used by
[cols="^,^,^,^",options="header"]
|===
| Spring Data Release Train | Spring Data Elasticsearch | Elasticsearch | Spring Framework
| 2023.1 (Vaughan) | 5.2.x | 8.11.3 | 6.1.x
| 2023.1 (Vaughan) | 5.2.x | 8.11.4 | 6.1.x
| 2023.0 (Ullmann) | 5.1.x | 8.7.1 | 6.0.x
| 2022.0 (Turing) | 5.0.xfootnote:oom[Out of maintenance] | 8.5.3 | 6.0.x
| 2021.2 (Raj) | 4.4.xfootnote:oom[] | 7.17.3 | 5.3.x
@@ -31,6 +31,10 @@ import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
import org.springframework.util.Assert;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
/**
* Base class for a @{@link org.springframework.context.annotation.Configuration} class to set up the Elasticsearch
* connection using the Elasticsearch Client. This class exposes different parts of the setup as Spring beans. Deriving
@@ -118,7 +122,13 @@ public abstract class ElasticsearchConfiguration extends ElasticsearchConfigurat
*/
@Bean
public JsonpMapper jsonpMapper() {
return new JacksonJsonpMapper();
// we need to create our own objectMapper that keeps null values in order to provide the storeNullValue
// functionality. The one Elasticsearch would provide removes the nulls. We remove unwanted nulls before they get
// into this mapper, so we can safely keep them here.
var objectMapper = (new ObjectMapper())
.configure(SerializationFeature.INDENT_OUTPUT, false)
.setSerializationInclusion(JsonInclude.Include.ALWAYS);
return new JacksonJsonpMapper(objectMapper);
}
/**
@@ -545,13 +545,12 @@ class RequestConverter {
Object queryObject = query.getObject();
if (queryObject != null) {
String id = StringUtils.hasText(query.getId()) ? query.getId() : getPersistentEntityId(queryObject);
builder //
.id(id) //
builder
.id(StringUtils.hasText(query.getId()) ? query.getId() : getPersistentEntityId(queryObject))
.document(elasticsearchConverter.mapObject(queryObject));
} else if (query.getSource() != null) {
builder //
.id(query.getId()) //
builder
.id(query.getId())
.document(new DefaultStringObjectMap<>().fromJson(query.getSource()));
} else {
throw new InvalidDataAccessApiUsageException(
@@ -597,12 +596,13 @@ class RequestConverter {
Object queryObject = query.getObject();
if (queryObject != null) {
String id = StringUtils.hasText(query.getId()) ? query.getId() : getPersistentEntityId(queryObject);
builder //
.id(id) //
builder
.id(StringUtils.hasText(query.getId()) ? query.getId() : getPersistentEntityId(queryObject))
.document(elasticsearchConverter.mapObject(queryObject));
} else if (query.getSource() != null) {
builder.document(new DefaultStringObjectMap<>().fromJson(query.getSource()));
builder
.id(query.getId())
.document(new DefaultStringObjectMap<>().fromJson(query.getSource()));
} else {
throw new InvalidDataAccessApiUsageException(
"object or source is null, failed to index the document [id: " + query.getId() + ']');
@@ -638,12 +638,13 @@ class RequestConverter {
Object queryObject = query.getObject();
if (queryObject != null) {
String id = StringUtils.hasText(query.getId()) ? query.getId() : getPersistentEntityId(queryObject);
builder //
.id(id) //
builder
.id(StringUtils.hasText(query.getId()) ? query.getId() : getPersistentEntityId(queryObject))
.document(elasticsearchConverter.mapObject(queryObject));
} else if (query.getSource() != null) {
builder.document(new DefaultStringObjectMap<>().fromJson(query.getSource()));
builder
.id(query.getId())
.document(new DefaultStringObjectMap<>().fromJson(query.getSource()));
} else {
throw new InvalidDataAccessApiUsageException(
"object or source is null, failed to index the document [id: " + query.getId() + ']');
@@ -1682,8 +1683,13 @@ class RequestConverter {
} else // noinspection StatementWithEmptyBody
if (query instanceof StringQuery) {
// no filter for StringQuery
} else if (query instanceof NativeQuery) {
builder.postFilter(((NativeQuery) query).getFilter());
} else if (query instanceof NativeQuery nativeQuery) {
if (nativeQuery.getFilter() != null) {
builder.postFilter(nativeQuery.getFilter());
} else if (nativeQuery.getSpringDataQuery() != null) {
addFilter(nativeQuery.getSpringDataQuery(), builder);
}
} else {
throw new IllegalArgumentException("unhandled Query implementation " + query.getClass().getName());
}
@@ -169,8 +169,8 @@ public final class MappingParameters {
positiveScoreImpact = field.positiveScoreImpact();
dims = field.dims();
if (type == FieldType.Dense_Vector) {
Assert.isTrue(dims >= 1 && dims <= 2048,
"Invalid required parameter! Dense_Vector value \"dims\" must be between 1 and 2048.");
Assert.isTrue(dims >= 1 && dims <= 4096,
"Invalid required parameter! Dense_Vector value \"dims\" must be between 1 and 4096.");
}
Assert.isTrue(field.enabled() || type == FieldType.Object, "enabled false is only allowed for field type object");
enabled = field.enabled();
@@ -212,8 +212,8 @@ public final class MappingParameters {
positiveScoreImpact = field.positiveScoreImpact();
dims = field.dims();
if (type == FieldType.Dense_Vector) {
Assert.isTrue(dims >= 1 && dims <= 2048,
"Invalid required parameter! Dense_Vector value \"dims\" must be between 1 and 2048.");
Assert.isTrue(dims >= 1 && dims <= 4096,
"Invalid required parameter! Dense_Vector value \"dims\" must be between 1 and 4096.");
}
enabled = true;
eagerGlobalOrdinals = field.eagerGlobalOrdinals();
@@ -70,7 +70,7 @@ public class Order extends Sort.Order {
public Order(Sort.Direction direction, String property, Sort.NullHandling nullHandlingHint, @Nullable Mode mode,
@Nullable String unmappedType) {
this(direction, property, nullHandlingHint, null, unmappedType, null);
this(direction, property, nullHandlingHint, mode, unmappedType, null);
}
public Order(Sort.Direction direction, String property, Sort.NullHandling nullHandlingHint, @Nullable Mode mode,
@@ -453,9 +453,7 @@ public class SimpleElasticsearchRepository<T, ID> implements ElasticsearchReposi
@Nullable
public <R> R executeAndRefresh(OperationsCallback<R> callback, @Nullable RefreshPolicy refreshPolicy) {
R result = callback.doWithOperations(operations.withRefreshPolicy(refreshPolicy));
doRefresh();
return result;
return callback.doWithOperations(operations.withRefreshPolicy(refreshPolicy));
}
// endregion
}
+6 -1
View File
@@ -1,4 +1,4 @@
Spring Data Elasticsearch 5.2.2 (2023.1.2)
Spring Data Elasticsearch 5.2.7 (2023.1.7)
Copyright (c) [2013-2022] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
@@ -20,3 +20,8 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
@@ -17,9 +17,9 @@ package org.springframework.data.elasticsearch.client;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
import static io.specto.hoverfly.junit.dsl.HoverflyDsl.service;
import static io.specto.hoverfly.junit.verification.HoverflyVerifications.atLeast;
import static org.assertj.core.api.Assertions.assertThat;
import static io.specto.hoverfly.junit.dsl.HoverflyDsl.*;
import static io.specto.hoverfly.junit.verification.HoverflyVerifications.*;
import static org.assertj.core.api.Assertions.*;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.transport.endpoints.BooleanResponse;
@@ -51,6 +51,10 @@ import com.github.tomakehurst.wiremock.stubbing.StubMapping;
/**
* We need hoverfly for testing the reactive code to use a proxy. Wiremock cannot intercept the proxy calls as WebClient
* uses HTTP CONNECT on proxy requests which wiremock does not support.
* <br/>
* Note: since 5.0 we do not use the WebClient for
* the reactive code anymore, so this might be handled with two wiremocks, but there is no real need to change this test
* setup.
*
* @author Peter-Josef Meisch
*/
@@ -0,0 +1,140 @@
/*
* Copyright 2024 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.client.elc;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.*;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
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.client.ClientConfiguration;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.lang.Nullable;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
/**
* Tests that need to check the data produced by the Elasticsearch client
* @author Peter-Josef Meisch
*/
@ExtendWith(SpringExtension.class)
public class ELCWiremockTests {
@RegisterExtension static WireMockExtension wireMock = WireMockExtension.newInstance()
.options(wireMockConfig()
.dynamicPort()
// needed, otherwise Wiremock goes to test/resources/mappings
.usingFilesUnderDirectory("src/test/resources/wiremock-mappings"))
.build();
@Configuration
static class Config extends ElasticsearchConfiguration {
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder()
.connectedTo("localhost:" + wireMock.getPort())
.build();
}
}
@Autowired ElasticsearchOperations operations;
@Test // #2839
@DisplayName("should store null values if configured")
void shouldStoreNullValuesIfConfigured() {
wireMock.stubFor(put(urlPathEqualTo("/null-fields/_doc/42"))
.withRequestBody(equalToJson("""
{
"_class": "org.springframework.data.elasticsearch.client.elc.ELCWiremockTests$EntityWithNullFields",
"id": "42",
"field1": null
}
"""))
.willReturn(
aResponse()
.withStatus(200)
.withHeader("X-elastic-product", "Elasticsearch")
.withHeader("content-type", "application/vnd.elasticsearch+json;compatible-with=8")
.withBody("""
{
"_index": "null-fields",
"_id": "42",
"_version": 1,
"result": "created",
"forced_refresh": true,
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 1,
"_primary_term": 1
}
""")));
var entity = new EntityWithNullFields();
entity.setId("42");
operations.save(entity);
// no need to assert anything, if the field1:null is not sent, we run into a 404 error
}
@Document(indexName = "null-fields")
static class EntityWithNullFields {
@Nullable
@Id private String id;
@Nullable
@Field(storeNullValue = true) private String field1;
@Nullable
@Field private String field2;
@Nullable
public String getId() {
return id;
}
public void setId(@Nullable String id) {
this.id = id;
}
@Nullable
public String getField1() {
return field1;
}
public void setField1(@Nullable String field1) {
this.field1 = field1;
}
@Nullable
public String getField2() {
return field2;
}
public void setField2(@Nullable String field2) {
this.field2 = field2;
}
}
}
@@ -70,8 +70,8 @@ public class MappingParametersTest extends MappingContextBaseTests {
}
@Test // #1700
@DisplayName("should not allow dims length greater than 2048 for dense_vector type")
void shouldNotAllowDimsLengthGreaterThan2048ForDenseVectorType() {
@DisplayName("should not allow dims length greater than 4096 for dense_vector type")
void shouldNotAllowDimsLengthGreaterThan4096ForDenseVectorType() {
ElasticsearchPersistentEntity<?> failEntity = elasticsearchConverter.get().getMappingContext()
.getRequiredPersistentEntity(DenseVectorInvalidDimsClass.class);
Annotation annotation = failEntity.getRequiredPersistentProperty("dense_vector").findAnnotation(Field.class);
@@ -90,21 +90,28 @@ public class MappingParametersTest extends MappingContextBaseTests {
}
static class AnnotatedClass {
@Nullable @Field private String field;
@Nullable @MultiField(mainField = @Field,
@Nullable
@Field private String field;
@Nullable
@MultiField(mainField = @Field,
otherFields = { @InnerField(suffix = "test", type = FieldType.Text) }) private String mainField;
@Nullable @Field(type = FieldType.Text, docValues = false) private String docValuesText;
@Nullable @Field(type = FieldType.Nested, docValues = false) private String docValuesNested;
@Nullable @Field(type = Object, enabled = true) private String enabledObject;
@Nullable @Field(type = Object, enabled = false) private String disabledObject;
@Nullable
@Field(type = FieldType.Text, docValues = false) private String docValuesText;
@Nullable
@Field(type = FieldType.Nested, docValues = false) private String docValuesNested;
@Nullable
@Field(type = Object, enabled = true) private String enabledObject;
@Nullable
@Field(type = Object, enabled = false) private String disabledObject;
}
static class InvalidEnabledFieldClass {
@Nullable @Field(type = FieldType.Text, enabled = false) private String disabledObject;
@Nullable
@Field(type = FieldType.Text, enabled = false) private String disabledObject;
}
static class DenseVectorInvalidDimsClass {
@Field(type = Dense_Vector, dims = 2049) private float[] dense_vector;
@Field(type = Dense_Vector, dims = 4097) private float[] dense_vector;
}
static class DenseVectorMissingDimsClass {
@@ -28,6 +28,7 @@ import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
import org.springframework.data.elasticsearch.utils.IndexNameProvider;
@@ -50,7 +51,7 @@ public abstract class NativeQueryIntegrationTests {
@Test
@Order(java.lang.Integer.MAX_VALUE)
void cleanup() {
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete();
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + '*')).delete();
}
@Test // #2391
@@ -75,6 +76,28 @@ public abstract class NativeQueryIntegrationTests {
assertThat(searchHits.getSearchHit(0).getId()).isEqualTo(entity.getId());
}
@Test // #2840
@DisplayName("should be able to use CriteriaQuery with filter arguments in a NativeQuery")
void shouldBeAbleToUseCriteriaQueryWithFilterArgumentsInANativeQuery() {
var entity1 = new SampleEntity();
entity1.setId("60");
var location1 = new GeoPoint(60.0, 60.0);
entity1.setLocation(location1);
var entity2 = new SampleEntity();
entity2.setId("70");
var location70 = new GeoPoint(70.0, 70.0);
entity2.setLocation(location70);
operations.save(entity1, entity2);
var criteriaQuery = new CriteriaQuery(Criteria.where("location").within(location1, "10km"));
var nativeQuery = NativeQuery.builder().withQuery(criteriaQuery).build();
var searchHits = operations.search(nativeQuery, SampleEntity.class);
assertThat(searchHits.getTotalHits()).isEqualTo(1);
assertThat(searchHits.getSearchHit(0).getId()).isEqualTo(entity1.getId());
}
@Test // #2391
@DisplayName("should be able to use StringQuery in a NativeQuery")
void shouldBeAbleToUseStringQueryInANativeQuery() {
@@ -112,6 +135,14 @@ public abstract class NativeQueryIntegrationTests {
@Document(indexName = "#{@indexNameProvider.indexName()}")
static class SampleEntity {
@Nullable
@Id private String id;
@Nullable
@Field(type = FieldType.Text) private String text;
@Nullable private GeoPoint location;
@Nullable
public String getId() {
return id;
@@ -121,6 +152,7 @@ public abstract class NativeQueryIntegrationTests {
this.id = id;
}
@Nullable
public String getText() {
return text;
}
@@ -130,8 +162,12 @@ public abstract class NativeQueryIntegrationTests {
}
@Nullable
@Id private String id;
public GeoPoint getLocation() {
return location;
}
@Field(type = FieldType.Text) private String text;
public void setLocation(GeoPoint location) {
this.location = location;
}
}
}
@@ -15,7 +15,7 @@
#
#
sde.testcontainers.image-name=docker.elastic.co/elasticsearch/elasticsearch
sde.testcontainers.image-version=8.11.3
sde.testcontainers.image-version=8.11.4
#
#
# 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