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

Compare commits

...

43 Commits

Author SHA1 Message Date
Mark Paluch 6faa70b0b2 Release version 5.5.7 (2025.0.7).
See #3196
2025-12-12 11:42:28 +01:00
Mark Paluch 0bcbc45373 Prepare 5.5.7 (2025.0.7).
See #3196
2025-12-12 11:42:06 +01:00
Mark Paluch 79321965f8 Update CI Properties.
See #3196
2025-12-10 08:34:38 +01:00
Mark Paluch e1f81f0c42 Add docker-java.properties.
See spring-projects/spring-data-build#2726
2025-12-04 09:00:50 +01:00
Mark Paluch 983e0e552c After release cleanups.
See #3185
2025-11-14 11:33:16 +01:00
Mark Paluch d9028609a8 Prepare next development iteration.
See #3185
2025-11-14 11:33:15 +01:00
Mark Paluch 3e2b114c5d Release version 5.5.6 (2025.0.6).
See #3185
2025-11-14 11:30:08 +01:00
Mark Paluch 6662e29aa5 Prepare 5.5.6 (2025.0.6).
See #3185
2025-11-14 11:29:46 +01:00
Mark Paluch 0b9cfca28f Update Update security documentation.
See #3185
2025-11-14 10:43:24 +01:00
Christoph Strobl f985e1efe5 After release cleanups.
See #3170
2025-10-17 11:39:06 +02:00
Christoph Strobl 8f6fca5b43 Prepare next development iteration.
See #3170
2025-10-17 11:39:05 +02:00
Christoph Strobl 0471b54d3b Release version 5.5.5 (2025.0.5).
See #3170
2025-10-17 11:34:13 +02:00
Christoph Strobl 1302c14920 Prepare 5.5.5 (2025.0.5).
See #3170
2025-10-17 11:33:06 +02:00
Peter-Josef Meisch 66894a5b0e Upgrade to Elasticsearch 8.18.8.
Original Pull Request #3183
Closes #3181
Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2025-10-17 00:09:34 +02:00
Peter-Josef Meisch 00c99852fa Fix string comparison in TypeUtils.
Original Pull Request #3177
Closes #3176
Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
(cherry picked from commit 0e1a97a27a)
2025-10-03 10:51:43 +02:00
Mark Paluch 4152378d32 Update GitHub Actions.
See #3170
2025-09-23 10:51:47 +02:00
Peter-Josef Meisch 3fca4e8be3 Adjust test to slower performance of build machines in Jenkins
Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
(cherry picked from commit 5529af8f76)
2025-09-20 20:42:54 +02:00
Peter-Josef Meisch 91f3393abc Speedup Criteria hashcode calculation
Original Pull Request #3172
Closes #3083
Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
(cherry picked from commit c0dd082f4e)
2025-09-20 19:52:33 +02:00
Christoph Strobl eee009fc18 After release cleanups.
See #3153
2025-09-12 11:45:34 +02:00
Christoph Strobl e394464724 Prepare next development iteration.
See #3153
2025-09-12 11:45:33 +02:00
Christoph Strobl 47815a69fb Release version 5.5.4 (2025.0.4).
See #3153
2025-09-12 11:41:39 +02:00
Christoph Strobl 01ca42fd2e Prepare 5.5.4 (2025.0.4).
See #3153
2025-09-12 11:41:09 +02:00
Mark Paluch 604c88b6a4 Polishing.
Update project metadata, add PJ as project lead (long overdue).

See #3154
2025-09-12 11:39:50 +02:00
Peter-Josef Meisch ed05313b17 Upgrade to Elasticsearch 8.18.6.
Original Pull Request #3164
Closes #3160

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2025-08-31 17:05:14 +02:00
Mark Paluch 822843a93a Refine version properties for documentation build.
See spring-projects/spring-data-build#2638
2025-08-18 09:16:40 +02:00
Mark Paluch 7f7731acdc After release cleanups.
See #3134
2025-08-15 10:04:17 +02:00
Mark Paluch a0ee571cc7 Prepare next development iteration.
See #3134
2025-08-15 10:04:16 +02:00
Mark Paluch 5bbccbf9d0 Release version 5.5.3 (2025.0.3).
See #3134
2025-08-15 10:01:35 +02:00
Mark Paluch 19af94f87e Prepare 5.5.3 (2025.0.3).
See #3134
2025-08-15 10:01:14 +02:00
Mark Paluch 71a3e24096 Polishing.
Use documentation variables for references, reorder antora keys.

See #3135
2025-08-14 17:28:01 +02:00
Peter-Josef Meisch 23eacd4d4b Upgrade to Elasticsearch 8.18.5.
Original Pull Request #3151
Closes #3149
Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
2025-08-12 18:39:04 +02:00
Mark Paluch 56bda34666 After release cleanups.
See #3120
2025-07-18 10:30:34 +02:00
Mark Paluch 76ba2324a2 Prepare next development iteration.
See #3120
2025-07-18 10:30:33 +02:00
Mark Paluch 4c217fa9c4 Release version 5.5.2 (2025.0.2).
See #3120
2025-07-18 10:27:59 +02:00
Mark Paluch 003213d4b0 Prepare 5.5.2 (2025.0.2).
See #3120
2025-07-18 10:27:38 +02:00
Mark Paluch 85d52014dc Upgrade to Maven Wrapper 3.9.11.
See #3131
2025-07-17 14:00:55 +02:00
Peter-Josef Meisch 786e0928ed Fix the calculation of the requested number of documents.
Original Pull Request #3128
Closes #3127

Signed-off-by: Peter-Josef Meisch <pj.meisch@sothawo.com>
(cherry picked from commit 12ddb74fae)
2025-07-15 18:41:17 +02:00
Mark Paluch ef6f091d6b After release cleanups.
See #3114
2025-06-13 13:42:19 +02:00
Mark Paluch 9ff829a829 Prepare next development iteration.
See #3114
2025-06-13 13:42:18 +02:00
Mark Paluch 30f32b6bbe Release version 5.5.1 (2025.0.1).
See #3114
2025-06-13 13:39:35 +02:00
Mark Paluch 8ce113a083 Prepare 5.5.1 (2025.0.1).
See #3114
2025-06-13 13:39:14 +02:00
Mark Paluch 262781c0a0 After release cleanups.
See #3096
2025-05-16 11:31:36 +02:00
Mark Paluch ffe8293365 Prepare next development iteration.
See #3096
2025-05-16 11:31:35 +02:00
22 changed files with 414 additions and 95 deletions
+21
View File
@@ -0,0 +1,21 @@
# GitHub Actions for CodeQL Scanning
name: "CodeQL Advanced"
on:
push:
pull_request:
workflow_dispatch:
schedule:
# https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule
- cron: '0 5 * * *'
permissions: read-all
jobs:
codeql-analysis-call:
permissions:
actions: read
contents: read
security-events: write
uses: spring-io/github-actions/.github/workflows/codeql-analysis.yml@1
+45
View File
@@ -0,0 +1,45 @@
# GitHub Actions to automate GitHub issues for Spring Data Project Management
name: Spring Data GitHub Issues
on:
issues:
types: [opened, edited, reopened]
issue_comment:
types: [created]
pull_request_target:
types: [opened, edited, reopened]
permissions:
contents: read
issues: write
pull-requests: write
jobs:
Inbox:
runs-on: ubuntu-latest
if: github.repository_owner == 'spring-projects' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request == null && !contains(join(github.event.issue.labels.*.name, ', '), 'dependency-upgrade') && !contains(github.event.issue.title, 'Release ')
steps:
- name: Create or Update Issue Card
uses: actions/add-to-project@v1.0.2
with:
project-url: https://github.com/orgs/spring-projects/projects/25
github-token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }}
Pull-Request:
runs-on: ubuntu-latest
if: github.repository_owner == 'spring-projects' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request != null
steps:
- name: Create or Update Pull Request Card
uses: actions/add-to-project@v1.0.2
with:
project-url: https://github.com/orgs/spring-projects/projects/25
github-token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }}
Feedback-Provided:
runs-on: ubuntu-latest
if: github.repository_owner == 'spring-projects' && github.event_name == 'issue_comment' && github.event.action == 'created' && github.actor != 'spring-projects-issues' && github.event.pull_request == null && github.event.issue.state == 'open' && contains(toJSON(github.event.issue.labels), 'waiting-for-feedback')
steps:
- name: Update Project Card
uses: actions/add-to-project@v1.0.2
with:
project-url: https://github.com/orgs/spring-projects/projects/25
github-token: ${{ secrets.GH_ISSUES_TOKEN_SPRING_DATA }}
+2 -2
View File
@@ -1,3 +1,3 @@
#Thu Nov 07 09:47:28 CET 2024
#Thu Jul 17 14:00:55 CEST 2025
wrapperUrl=https\://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
distributionUrl=https\://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip
Vendored
+1 -1
View File
@@ -9,7 +9,7 @@ pipeline {
triggers {
pollSCM 'H/10 * * * *'
upstream(upstreamProjects: "spring-data-commons/main", threshold: hudson.model.Result.SUCCESS)
upstream(upstreamProjects: "spring-data-commons/3.5.x", threshold: hudson.model.Result.SUCCESS)
}
options {
+11 -5
View File
@@ -1,9 +1,15 @@
# Security Policy
= Security Policy
## Supported Versions
== Reporting a Vulnerability
Please see the https://spring.io/projects/spring-data-elasticsearch[Spring Data Elasticsearch] project page for supported versions.
Please, https://github.com/spring-projects/security-advisories/security/advisories/new[open a draft security advisory] if you need to disclose and discuss a security issue in private with the Spring Data team.
Note that we only accept reports against https://spring.io/projects/spring-data#support[supported versions].
## Reporting a Vulnerability
For more details, check out our https://spring.io/security-policy[security policy].
Please don't raise security vulnerabilities here. Head over to https://pivotal.io/security to learn how to disclose them responsibly.
== JAR signing
Spring Data JARs released on Maven Central are signed.
You'll find more information about the key here: https://spring.io/GPG-KEY-spring.txt
Versions released prior to 2023 may be signed with a different key.
+2 -2
View File
@@ -17,8 +17,8 @@ docker.redis.7.version=7.2.4
docker.valkey.8.version=8.1.1
# Docker environment settings
docker.java.inside.basic=-v $HOME:/tmp/jenkins-home
docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home
docker.java.inside.basic=-v $HOME:/tmp/jenkins-home --ulimit nofile=32000:32000
docker.java.inside.docker=-u root -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker -v $HOME:/tmp/jenkins-home --ulimit nofile=32000:32000
# Credentials
docker.registry=
+14 -10
View File
@@ -1,16 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>5.5.0</version>
<version>5.5.7</version>
<parent>
<groupId>org.springframework.data.build</groupId>
<artifactId>spring-data-parent</artifactId>
<version>3.5.0</version>
<version>3.5.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.5.0</springdata.commons>
<springdata.commons>3.5.7</springdata.commons>
<!-- version of the ElasticsearchClient -->
<elasticsearch-java>8.18.1</elasticsearch-java>
<elasticsearch-java>8.18.8</elasticsearch-java>
<hoverfly>0.19.0</hoverfly>
<log4j>2.23.1</log4j>
@@ -41,6 +41,15 @@
</properties>
<developers>
<developer>
<id>sothawo</id>
<name>Peter-Josef Meisch</name>
<email>pj.meisch at sothawo.com</email>
<roles>
<role>Project Lead</role>
</roles>
<timezone>+1</timezone>
</developer>
<developer>
<id>biomedcentral</id>
<name>BioMed Central Development Team</name>
@@ -77,11 +86,6 @@
</developerConnection>
</scm>
<ciManagement>
<system>Bamboo</system>
<url>https://build.spring.io/browse/SPRINGDATAES</url>
</ciManagement>
<issueManagement>
<system>GitHub</system>
<url>https://github.com/spring-projects/spring-data-elasticsearch/issues</url>
@@ -376,7 +376,7 @@ So calling the method with a `List` of `["id1", "id2", "id3"]` would produce the
.Declare query on the method using the `@Query` annotation with SpEL expression.
====
https://docs.spring.io/spring-framework/reference/core/expressions.html[SpEL expression] is also supported when defining query in `@Query`.
{spring-framework-docs}/core/expressions.html[SpEL expression] is also supported when defining query in `@Query`.
[source,java]
----
@@ -453,7 +453,7 @@ We can pass `new QueryParameter("John")` as the parameter now, and it will produ
.accessing bean property.
====
https://docs.spring.io/spring-framework/reference/core/expressions/language-ref/bean-references.html[Bean property] is also supported to access.
{spring-framework-docs}/core/expressions/language-ref/bean-references.html[Bean property] is also supported to access.
Given that there is a bean named `queryParameter` of type `QueryParameter`, we can access the bean with symbol `@` rather than `#`, and there is no need to declare a parameter of type `QueryParameter` in the query method:
[source,java]
@@ -523,7 +523,7 @@ A collection of `names` like `List.of("name1", "name2")` will produce the follow
.access property in the `Collection` param.
====
https://docs.spring.io/spring-framework/reference/core/expressions/language-ref/collection-projection.html[SpEL Collection Projection] is convenient to use when values in the `Collection` parameter is not plain `String`:
{spring-framework-docs}/core/expressions/language-ref/collection-projection.html[SpEL Collection Projection] is convenient to use when values in the `Collection` parameter is not plain `String`:
[source,java]
----
@@ -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
| 2025.0 | 5.5.x | 8.18.1 | 6.2.x
| 2025.0 | 5.5.x | 8.18.8 | 6.2.x
| 2024.1 | 5.4.x | 8.15.5 | 6.1.x
| 2024.0 | 5.3.xfootnote:oom[Out of maintenance] | 8.13.4 | 6.1.x
| 2023.1 (Vaughan) | 5.2.xfootnote:oom[] | 8.11.1 | 6.1.x
@@ -3,19 +3,20 @@ prerelease: ${antora-component.prerelease}
asciidoc:
attributes:
copyright-year: ${current.year}
version: ${project.version}
springversionshort: ${spring.short}
springversion: ${spring}
attribute-missing: 'warn'
commons: ${springdata.commons.docs}
chomp: 'all'
version: '${project.version}'
copyright-year: '${current.year}'
springversionshort: '${spring.short}'
springversion: '${spring}'
commons: '${springdata.commons.docs}'
include-xml-namespaces: false
spring-data-commons-docs-url: https://docs.spring.io/spring-data/commons/reference
spring-data-commons-javadoc-base: https://docs.spring.io/spring-data/commons/docs/${springdata.commons}/api/
springdocsurl: https://docs.spring.io/spring-framework/reference/{springversionshort}
springjavadocurl: https://docs.spring.io/spring-framework/docs/${spring}/javadoc-api
spring-data-commons-docs-url: '${documentation.baseurl}/spring-data/commons/reference/${springdata.commons.short}'
spring-data-commons-javadoc-base: '{spring-data-commons-docs-url}/api/java'
springdocsurl: '${documentation.baseurl}/spring-framework/reference/{springversionshort}'
spring-framework-docs: '{springdocsurl}'
springjavadocurl: '${documentation.spring-javadoc-url}'
spring-framework-javadoc: '{springjavadocurl}'
springhateoasversion: ${spring-hateoas}
releasetrainversion: ${releasetrain}
springhateoasversion: '${spring-hateoas}'
releasetrainversion: '${releasetrain}'
store: Elasticsearch
@@ -120,9 +120,6 @@ class RequestConverter extends AbstractQueryProcessor {
private static final Log LOGGER = LogFactory.getLog(RequestConverter.class);
// the default max result window size of Elasticsearch
public static final Integer INDEX_MAX_RESULT_WINDOW = 10_000;
protected final JsonpMapper jsonpMapper;
protected final ElasticsearchConverter elasticsearchConverter;
@@ -1039,18 +1036,7 @@ class RequestConverter extends AbstractQueryProcessor {
List<SortOptions> sortOptions = getSortOptions(query.getSort(), persistentEntity);
if (!sortOptions.isEmpty()) {
dqb.sort(
sortOptions.stream()
.map(sortOption -> {
String order = "asc";
var sortField = sortOption.field();
if (sortField.order() != null) {
order = sortField.order().jsonValue();
}
return sortField.field() + ':' + order;
})
.collect(Collectors.toList()));
dqb.sort(sortOptions);
}
}
if (query.getRefresh() != null) {
@@ -1295,15 +1281,8 @@ class RequestConverter extends AbstractQueryProcessor {
.timeout(timeStringMs(query.getTimeout())) //
;
var offset = query.getPageable().isPaged() ? query.getPageable().getOffset() : 0;
var pageSize = query.getPageable().isPaged() ? query.getPageable().getPageSize()
: INDEX_MAX_RESULT_WINDOW;
// if we have both a page size and a max results, we take the min, this is necessary for
// searchForStream to work correctly (#3098) as there the page size defines what is
// returned in a single request, and the max result determines the total number of
// documents returned
var size = query.isLimiting() ? Math.min(pageSize, query.getMaxResults()) : pageSize;
bb.from((int) offset).size(size);
bb.from((int) (query.getPageable().isPaged() ? query.getPageable().getOffset() : 0))
.size(query.getRequestSize());
if (!isEmpty(query.getFields())) {
bb.fields(fb -> {
@@ -1473,14 +1452,8 @@ class RequestConverter extends AbstractQueryProcessor {
builder.seqNoPrimaryTerm(true);
}
var offset = query.getPageable().isPaged() ? query.getPageable().getOffset() : 0;
var pageSize = query.getPageable().isPaged() ? query.getPageable().getPageSize() : INDEX_MAX_RESULT_WINDOW;
// if we have both a page size and a max results, we take the min, this is necessary for
// searchForStream to work correctly (#3098) as there the page size defines what is
// returned in a single request, and the max result determines the total number of
// documents returned
var size = query.isLimiting() ? Math.min(pageSize, query.getMaxResults()) : pageSize;
builder.from((int) offset).size(size);
builder.from((int) (query.getPageable().isPaged() ? query.getPageable().getOffset() : 0))
.size(query.getRequestSize());
if (!isEmpty(query.getFields())) {
var fieldAndFormats = query.getFields().stream().map(field -> FieldAndFormat.of(b -> b.field(field))).toList();
@@ -436,7 +436,7 @@ final class TypeUtils {
// values taken from the RHLC implementation
if (value == null) {
return -2;
} else if ("all".equals(value.toUpperCase())) {
} else if ("all".equals(value.toLowerCase())) {
return -1;
} else {
try {
@@ -27,6 +27,7 @@ import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.lang.Nullable;
@@ -47,10 +48,15 @@ import org.springframework.util.Assert;
*/
public class BaseQuery implements Query {
public static final int INDEX_MAX_RESULT_WINDOW = 10_000;
private static final int DEFAULT_REACTIVE_BATCH_SIZE = 500;
// the instance to mark the query pageable initial status, needed to distinguish between the initial
// value and a user-set unpaged value; values don't matter, the RequestConverter compares to the isntance.
private static final Pageable UNSET_PAGE = PageRequest.of(0, 1);
@Nullable protected Sort sort;
protected Pageable pageable = DEFAULT_PAGE;
protected Pageable pageable = UNSET_PAGE;
protected List<String> fields = new ArrayList<>();
@Nullable protected List<String> storedFields;
@Nullable protected SourceFilter sourceFilter;
@@ -78,7 +84,7 @@ public class BaseQuery implements Query {
private boolean queryIsUpdatedByConverter = false;
@Nullable private Integer reactiveBatchSize = null;
@Nullable private Boolean allowNoIndices = null;
private EnumSet<IndicesOptions.WildcardStates> expandWildcards;
private EnumSet<IndicesOptions.WildcardStates> expandWildcards = EnumSet.noneOf(IndicesOptions.WildcardStates.class);
private List<DocValueField> docValueFields = new ArrayList<>();
private List<ScriptedField> scriptedFields = new ArrayList<>();
@@ -87,7 +93,7 @@ public class BaseQuery implements Query {
public <Q extends BaseQuery, B extends BaseQueryBuilder<Q, B>> BaseQuery(BaseQueryBuilder<Q, B> builder) {
this.sort = builder.getSort();
// do a setPageable after setting the sort, because the pageable may contain an additional sort
this.setPageable(builder.getPageable() != null ? builder.getPageable() : DEFAULT_PAGE);
this.setPageable(builder.getPageable() != null ? builder.getPageable() : UNSET_PAGE);
this.fields = builder.getFields();
this.storedFields = builder.getStoredFields();
this.sourceFilter = builder.getSourceFilter();
@@ -203,7 +209,7 @@ public class BaseQuery implements Query {
@Override
@SuppressWarnings("unchecked")
public final <T extends Query> T addSort(@Nullable Sort sort) {
if (sort == null) {
if (sort == null || sort.isUnsorted()) {
return (T) this;
}
@@ -561,4 +567,52 @@ public class BaseQuery implements Query {
public List<ScriptedField> getScriptedFields() {
return scriptedFields;
}
@Override
public Integer getRequestSize() {
var pageable = getPageable();
Integer requestSize = null;
if (pageable.isPaged() && pageable != UNSET_PAGE) {
// pagesize defined by the user
if (!isLimiting()) {
// no maxResults
requestSize = pageable.getPageSize();
} else {
// if we have both a page size and a max results, we take the min, this is necessary for
// searchForStream to work correctly (#3098) as there the page size defines what is
// returned in a single request, and the max result determines the total number of
// documents returned.
requestSize = Math.min(pageable.getPageSize(), getMaxResults());
}
} else if (pageable == UNSET_PAGE) {
// no user defined pageable
if (isLimiting()) {
// maxResults
requestSize = getMaxResults();
} else {
requestSize = DEFAULT_PAGE_SIZE;
}
} else {
// explicitly set unpaged
if (!isLimiting()) {
// no maxResults
requestSize = INDEX_MAX_RESULT_WINDOW;
} else {
// if we have both a implicit page size and a max results, we take the min, this is necessary for
// searchForStream to work correctly (#3098) as there the page size defines what is
// returned in a single request, and the max result determines the total number of
// documents returned.
requestSize = Math.min(INDEX_MAX_RESULT_WINDOW, getMaxResults());
}
}
if (requestSize == null) {
// this should not happen
requestSize = DEFAULT_PAGE_SIZE;
}
return requestSize;
}
}
@@ -61,12 +61,16 @@ public class Criteria {
private float boost = Float.NaN;
private boolean negating = false;
private final CriteriaChain criteriaChain = new CriteriaChain();
private final Set<CriteriaEntry> queryCriteriaEntries = new LinkedHashSet<>();
private final Set<CriteriaEntry> filterCriteriaEntries = new LinkedHashSet<>();
private final Set<Criteria> subCriteria = new LinkedHashSet<>();
// we cash this and recalculate when properties used in equals change
// see https://github.com/spring-projects/spring-data-elasticsearch/issues/3083
private int hashCode;
// region criteria creation
private final CriteriaChain criteriaChain = new CriteriaChain();
private final Set<CriteriaEntry> queryCriteriaEntries = new LinkedHashSet<>();
private final Set<CriteriaEntry> filterCriteriaEntries = new LinkedHashSet<>();
private final Set<Criteria> subCriteria = new LinkedHashSet<>();
// region criteria creation
/**
* @return factory method to create an and-Criteria that is not bound to a field
@@ -84,7 +88,9 @@ public class Criteria {
return new OrCriteria();
}
public Criteria() {}
public Criteria() {
recalculateHashCode();
}
/**
* Creates a new Criteria with provided field name
@@ -107,6 +113,7 @@ public class Criteria {
this.field = field;
this.criteriaChain.add(this);
recalculateHashCode();
}
/**
@@ -136,6 +143,7 @@ public class Criteria {
this.field = field;
this.criteriaChain.addAll(criteriaChain);
this.criteriaChain.add(this);
recalculateHashCode();
}
/**
@@ -189,6 +197,7 @@ public class Criteria {
*/
public Criteria not() {
this.negating = true;
recalculateHashCode();
return this;
}
@@ -207,6 +216,7 @@ public class Criteria {
Assert.isTrue(boost >= 0, "boost must not be negative");
this.boost = boost;
recalculateHashCode();
return this;
}
@@ -223,7 +233,7 @@ public class Criteria {
}
/**
* @return the set ob subCriteria
* @return the set of subCriteria
* @since 4.1
*/
public Set<Criteria> getSubCriteria() {
@@ -264,6 +274,7 @@ public class Criteria {
Assert.notNull(criteria, "Cannot chain 'null' criteria.");
this.criteriaChain.add(criteria);
recalculateHashCode();
return this;
}
@@ -278,6 +289,7 @@ public class Criteria {
Assert.notNull(criterias, "Cannot chain 'null' criterias.");
this.criteriaChain.addAll(Arrays.asList(criterias));
recalculateHashCode();
return this;
}
@@ -320,6 +332,7 @@ public class Criteria {
orCriteria.subCriteria.addAll(criteria.subCriteria);
orCriteria.boost = criteria.boost;
orCriteria.negating = criteria.isNegating();
orCriteria.recalculateHashCode();
return orCriteria;
}
@@ -335,6 +348,7 @@ public class Criteria {
Assert.notNull(criteria, "criteria must not be null");
subCriteria.add(criteria);
recalculateHashCode();
return this;
}
@@ -349,6 +363,7 @@ public class Criteria {
*/
public Criteria is(Object o) {
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.EQUALS, o));
recalculateHashCode();
return this;
}
@@ -360,6 +375,7 @@ public class Criteria {
*/
public Criteria exists() {
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.EXISTS));
recalculateHashCode();
return this;
}
@@ -378,6 +394,7 @@ public class Criteria {
}
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.BETWEEN, new Object[] { lowerBound, upperBound }));
recalculateHashCode();
return this;
}
@@ -393,6 +410,7 @@ public class Criteria {
assertNoBlankInWildcardQuery(s, false, true);
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.STARTS_WITH, s));
recalculateHashCode();
return this;
}
@@ -409,6 +427,7 @@ public class Criteria {
assertNoBlankInWildcardQuery(s, true, true);
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.CONTAINS, s));
recalculateHashCode();
return this;
}
@@ -425,6 +444,7 @@ public class Criteria {
assertNoBlankInWildcardQuery(s, true, false);
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.ENDS_WITH, s));
recalculateHashCode();
return this;
}
@@ -452,6 +472,7 @@ public class Criteria {
Assert.notNull(values, "Collection of 'in' values must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.IN, values));
recalculateHashCode();
return this;
}
@@ -478,6 +499,7 @@ public class Criteria {
Assert.notNull(values, "Collection of 'NotIn' values must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.NOT_IN, values));
recalculateHashCode();
return this;
}
@@ -490,6 +512,7 @@ public class Criteria {
*/
public Criteria expression(String s) {
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.EXPRESSION, s));
recalculateHashCode();
return this;
}
@@ -501,6 +524,7 @@ public class Criteria {
*/
public Criteria fuzzy(String s) {
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.FUZZY, s));
recalculateHashCode();
return this;
}
@@ -515,6 +539,7 @@ public class Criteria {
Assert.notNull(upperBound, "upperBound must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.LESS_EQUAL, upperBound));
recalculateHashCode();
return this;
}
@@ -529,6 +554,7 @@ public class Criteria {
Assert.notNull(upperBound, "upperBound must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.LESS, upperBound));
recalculateHashCode();
return this;
}
@@ -543,6 +569,7 @@ public class Criteria {
Assert.notNull(lowerBound, "lowerBound must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.GREATER_EQUAL, lowerBound));
recalculateHashCode();
return this;
}
@@ -557,6 +584,7 @@ public class Criteria {
Assert.notNull(lowerBound, "lowerBound must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.GREATER, lowerBound));
recalculateHashCode();
return this;
}
@@ -573,6 +601,7 @@ public class Criteria {
Assert.notNull(value, "value must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.MATCHES, value));
recalculateHashCode();
return this;
}
@@ -589,6 +618,7 @@ public class Criteria {
Assert.notNull(value, "value must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.MATCHES_ALL, value));
recalculateHashCode();
return this;
}
@@ -601,6 +631,7 @@ public class Criteria {
public Criteria empty() {
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.EMPTY));
recalculateHashCode();
return this;
}
@@ -613,6 +644,7 @@ public class Criteria {
public Criteria notEmpty() {
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.NOT_EMPTY));
recalculateHashCode();
return this;
}
@@ -628,6 +660,7 @@ public class Criteria {
Assert.notNull(value, "value must not be null");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.REGEXP, value));
recalculateHashCode();
return this;
}
@@ -646,6 +679,7 @@ public class Criteria {
Assert.notNull(boundingBox, "boundingBox value for boundedBy criteria must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.BBOX, new Object[] { boundingBox }));
recalculateHashCode();
return this;
}
@@ -662,6 +696,7 @@ public class Criteria {
filterCriteriaEntries
.add(new CriteriaEntry(OperationKey.BBOX, new Object[] { boundingBox.getFirst(), boundingBox.getSecond() }));
recalculateHashCode();
return this;
}
@@ -679,6 +714,7 @@ public class Criteria {
filterCriteriaEntries
.add(new CriteriaEntry(OperationKey.BBOX, new Object[] { topLeftGeohash, bottomRightGeohash }));
recalculateHashCode();
return this;
}
@@ -695,6 +731,7 @@ public class Criteria {
Assert.notNull(bottomRightPoint, "bottomRightPoint must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.BBOX, new Object[] { topLeftPoint, bottomRightPoint }));
recalculateHashCode();
return this;
}
@@ -712,6 +749,7 @@ public class Criteria {
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.BBOX,
new Object[] { GeoPoint.fromPoint(topLeftPoint), GeoPoint.fromPoint(bottomRightPoint) }));
recalculateHashCode();
return this;
}
@@ -729,6 +767,7 @@ public class Criteria {
Assert.notNull(location, "Distance value for near criteria must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.WITHIN, new Object[] { location, distance }));
recalculateHashCode();
return this;
}
@@ -745,6 +784,7 @@ public class Criteria {
Assert.notNull(location, "Distance value for near criteria must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.WITHIN, new Object[] { location, distance }));
recalculateHashCode();
return this;
}
@@ -761,6 +801,7 @@ public class Criteria {
Assert.isTrue(StringUtils.hasLength(geoLocation), "geoLocation value must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.WITHIN, new Object[] { geoLocation, distance }));
recalculateHashCode();
return this;
}
@@ -775,6 +816,7 @@ public class Criteria {
Assert.notNull(geoShape, "geoShape must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.GEO_INTERSECTS, geoShape));
recalculateHashCode();
return this;
}
@@ -789,6 +831,7 @@ public class Criteria {
Assert.notNull(geoShape, "geoShape must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.GEO_IS_DISJOINT, geoShape));
recalculateHashCode();
return this;
}
@@ -802,6 +845,7 @@ public class Criteria {
Assert.notNull(geoShape, "geoShape must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.GEO_WITHIN, geoShape));
recalculateHashCode();
return this;
}
@@ -815,6 +859,7 @@ public class Criteria {
Assert.notNull(geoShape, "geoShape must not be null");
filterCriteriaEntries.add(new CriteriaEntry(OperationKey.GEO_CONTAINS, geoShape));
recalculateHashCode();
return this;
}
@@ -828,6 +873,7 @@ public class Criteria {
Assert.notNull(query, "has_child query must not be null.");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.HAS_CHILD, query));
recalculateHashCode();
return this;
}
@@ -841,6 +887,7 @@ public class Criteria {
Assert.notNull(query, "has_parent query must not be null.");
queryCriteriaEntries.add(new CriteriaEntry(OperationKey.HAS_PARENT, query));
recalculateHashCode();
return this;
}
// endregion
@@ -887,18 +934,22 @@ public class Criteria {
@Override
public int hashCode() {
int result = field != null ? field.hashCode() : 0;
result = 31 * result + (boost != +0.0f ? Float.floatToIntBits(boost) : 0);
result = 31 * result + (negating ? 1 : 0);
// the criteriaChain contains "this" object, so we need to filter it out
// to avoid a stackoverflow here, because the hashcode implementation
// uses the element's hashcodes
result = 31 * result + criteriaChain.filter(this).hashCode();
result = 31 * result + queryCriteriaEntries.hashCode();
result = 31 * result + filterCriteriaEntries.hashCode();
result = 31 * result + subCriteria.hashCode();
return result;
return hashCode;
}
private void recalculateHashCode() {
int result = field != null ? field.hashCode() : 0;
result = 31 * result + (boost != +0.0f ? Float.floatToIntBits(boost) : 0);
result = 31 * result + (negating ? 1 : 0);
// the criteriaChain contains "this" object, so we need to filter it out
// to avoid a stackoverflow here, because the hashcode implementation
// uses the element's hashcodes
result = 31 * result + criteriaChain.filter(this).hashCode();
result = 31 * result + queryCriteriaEntries.hashCode();
result = 31 * result + filterCriteriaEntries.hashCode();
result = 31 * result + subCriteria.hashCode();
this.hashCode = result;
}
// endregion
@Override
@@ -484,6 +484,13 @@ public interface Query {
*/
List<ScriptedField> getScriptedFields();
/**
* @return the number of documents that should be requested from Elasticsearch in this query. Depends wether a
* Pageable and/or maxResult size is set on the query.
* @since 5.4.8 5.5.2
*/
public Integer getRequestSize();
/**
* @since 4.3
*/
+8 -1
View File
@@ -1,4 +1,4 @@
Spring Data Elasticsearch 5.5 GA (2025.0.0)
Spring Data Elasticsearch 5.5.7 (2025.0.7)
Copyright (c) [2013-2022] Pivotal Software, Inc.
This product is licensed to you under the Apache License, Version 2.0 (the "License").
@@ -21,6 +21,13 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
@@ -448,7 +448,7 @@ public class CriteriaQueryMappingUnitTests {
}
// the following test failed because of a wrong implementation in Criteria
// equals and hscode methods.
// equals and hashcode methods.
@Test // #3083
@DisplayName("should map correct subcriteria")
void shouldMapCorrectSubcriteria() throws JSONException {
@@ -105,8 +105,6 @@ import org.springframework.lang.Nullable;
@SpringIntegrationTest
public abstract class ElasticsearchIntegrationTests {
static final Integer INDEX_MAX_RESULT_WINDOW = 10_000;
private static final String MULTI_INDEX_PREFIX = "test-index";
private static final String MULTI_INDEX_ALL = MULTI_INDEX_PREFIX + "*";
private static final String MULTI_INDEX_1_NAME = MULTI_INDEX_PREFIX + "-1";
@@ -0,0 +1,106 @@
/*
* Copyright 2025 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.core.query;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.elasticsearch.core.query.BaseQuery.*;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Pageable;
class BaseQueryTests {
private static final String MATCH_ALL_QUERY = "{\"match_all\":{}}";
@Test // #3127
@DisplayName("query with no Pageable and no maxResults requests 10 docs from 0")
void queryWithNoPageableAndNoMaxResultsRequests10DocsFrom0() {
var query = StringQuery.builder(MATCH_ALL_QUERY)
.build();
var requestSize = query.getRequestSize();
assertThat(requestSize).isEqualTo(10);
}
@Test // #3127
@DisplayName("query with a Pageable and no MaxResults request with values from Pageable")
void queryWithAPageableAndNoMaxResultsRequestWithValuesFromPageable() {
var query = StringQuery.builder(MATCH_ALL_QUERY)
.withPageable(Pageable.ofSize(42))
.build();
var requestSize = query.getRequestSize();
assertThat(requestSize).isEqualTo(42);
}
@Test // #3127
@DisplayName("query with no Pageable and maxResults requests maxResults")
void queryWithNoPageableAndMaxResultsRequestsMaxResults() {
var query = StringQuery.builder(MATCH_ALL_QUERY)
.withMaxResults(12_345)
.build();
var requestSize = query.getRequestSize();
assertThat(requestSize).isEqualTo(12_345);
}
@Test // #3127
@DisplayName("query with Pageable and maxResults requests with values from Pageable if Pageable is less than maxResults")
void queryWithPageableAndMaxResultsRequestsWithValuesFromPageableIfPageableIsLessThanMaxResults() {
var query = StringQuery.builder(MATCH_ALL_QUERY)
.withPageable(Pageable.ofSize(42))
.withMaxResults(123)
.build();
var requestSize = query.getRequestSize();
assertThat(requestSize).isEqualTo(42);
}
@Test // #3127
@DisplayName("query with Pageable and maxResults requests with values from maxResults if Pageable is more than maxResults")
void queryWithPageableAndMaxResultsRequestsWithValuesFromMaxResultsIfPageableIsMoreThanMaxResults() {
var query = StringQuery.builder(MATCH_ALL_QUERY)
.withPageable(Pageable.ofSize(420))
.withMaxResults(123)
.build();
var requestSize = query.getRequestSize();
assertThat(requestSize).isEqualTo(123);
}
@Test // #3127
@DisplayName("query with explicit unpaged request and no maxResults requests max request window size")
void queryWithExplicitUnpagedRequestAndNoMaxResultsRequestsMaxRequestWindowSize() {
var query = StringQuery.builder(MATCH_ALL_QUERY)
.withPageable(Pageable.unpaged())
.build();
var requestSize = query.getRequestSize();
assertThat(requestSize).isEqualTo(INDEX_MAX_RESULT_WINDOW);
}
}
@@ -0,0 +1,45 @@
/*
* Copyright 2019-2025 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.core.query;
import static org.junit.jupiter.api.Assertions.*;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
/**
* @author Peter-Josef Meisch
*/
class CriteriaTest {
@Test // #3159
@DisplayName("should not slow down on calculating hashcode for long criteria chains")
void shouldNotSlowDownOnCalculatingHashcodeForLongCriteriaChains() {
assertTimeoutPreemptively(Duration.of(1, ChronoUnit.SECONDS), () -> {
var criteria = new Criteria();
var size = 1000;
for (int i = 1; i <= size; i++) {
criteria = criteria.or("field-" + i).contains("value-" + i);
}
final var criteriaChain = criteria.getCriteriaChain();
assertEquals(size, criteriaChain.size());
final var hashCode = Integer.valueOf(criteria.hashCode());
});
}
}
@@ -0,0 +1 @@
api.version=1.44
@@ -15,7 +15,7 @@
#
#
sde.testcontainers.image-name=docker.elastic.co/elasticsearch/elasticsearch
sde.testcontainers.image-version=8.18.1
sde.testcontainers.image-version=8.18.8
#
#
# 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