Compare commits
79 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f7e44adae | |||
| 5c6b167c77 | |||
| 7c3ea61fa6 | |||
| 0e7845f0e7 | |||
| c9a15acb07 | |||
| c8c0a32f2d | |||
| 98d4cc0d74 | |||
| 1515f33047 | |||
| e0f30b819b | |||
| 2825046949 | |||
| a4defdc5fd | |||
| 039f78e311 | |||
| d93f3c6810 | |||
| b93ed840db | |||
| de956dd81c | |||
| 21306745b3 | |||
| 0f7dc4f54d | |||
| 48855e75df | |||
| 430527504e | |||
| 40cd3f127f | |||
| 2ccf157a58 | |||
| 274df0e9d8 | |||
| b66146dadd | |||
| 930c04e355 | |||
| 4f475575e6 | |||
| 268f73e692 | |||
| 37c20975a9 | |||
| e3afc2b704 | |||
| e634d7ac05 | |||
| 5f1f46acd4 | |||
| 7a5c8b4546 | |||
| ac6dd91b57 | |||
| 89c20c3b21 | |||
| 6fdd786b03 | |||
| c739e023b6 | |||
| c7a8c0bf60 | |||
| 57b4af14c2 | |||
| 5175f34418 | |||
| f5f73dc67a | |||
| 6700580755 | |||
| 7ff25485de | |||
| 394e56d3a9 | |||
| 474e433926 | |||
| 7e64d85efc | |||
| ea51ce062e | |||
| 4767a8215e | |||
| 6c4fc59a11 | |||
| 0e8401d3b1 | |||
| 2c20671379 | |||
| 65b0709b73 | |||
| 47110a212a | |||
| ed59fc85ca | |||
| 46fdc8a84b | |||
| 394fb7a831 | |||
| c23da6ed5e | |||
| cc56e27633 | |||
| ce64d2d6d1 | |||
| e02599d177 | |||
| 8558c44714 | |||
| 88e2e9da2a | |||
| c15c1d08d7 | |||
| c653e6a4ec | |||
| 7318c31692 | |||
| 6812684122 | |||
| cd5d3da110 | |||
| f87f7469f1 | |||
| 817967a282 | |||
| 60b71dcb11 | |||
| 33a149ae2d | |||
| 29158f9097 | |||
| 5d52918a5c | |||
| 457c9b71b3 | |||
| 18b91d5da2 | |||
| 62091be997 | |||
| fdc7893817 | |||
| 535d76faf0 | |||
| 26bd770b8c | |||
| aec03a3529 | |||
| e3b26b2268 |
@@ -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
|
||||
@@ -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 }}
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#Thu Jul 17 14:00:55 CEST 2025
|
||||
#Thu Jul 17 14:04:44 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.11/apache-maven-3.9.11-bin.zip
|
||||
|
||||
Vendored
+1
-1
@@ -9,7 +9,7 @@ pipeline {
|
||||
|
||||
triggers {
|
||||
pollSCM 'H/10 * * * *'
|
||||
upstream(upstreamProjects: "spring-data-commons/3.5.x", threshold: hudson.model.Result.SUCCESS)
|
||||
upstream(upstreamProjects: "spring-data-commons/3.4.x", threshold: hudson.model.Result.SUCCESS)
|
||||
}
|
||||
|
||||
options {
|
||||
|
||||
+2
-2
@@ -62,7 +62,7 @@ public class MyService {
|
||||
|
||||
=== Using the RestClient
|
||||
|
||||
Please check the https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.clients.configuration[official documentation].
|
||||
Please check the [official documentation](https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.clients.configuration).
|
||||
|
||||
=== Maven configuration
|
||||
|
||||
@@ -168,7 +168,7 @@ Building the documentation builds also the project without running tests.
|
||||
$ ./mvnw clean install -Pantora
|
||||
----
|
||||
|
||||
The generated documentation is available from `target/site/index.html`.
|
||||
The generated documentation is available from `target/antora/site/index.html`.
|
||||
|
||||
== Examples
|
||||
|
||||
|
||||
+11
-5
@@ -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.
|
||||
|
||||
+10
-6
@@ -7,18 +7,22 @@ docker.java.main.image=library/eclipse-temurin:${java.main.tag}
|
||||
docker.java.next.image=library/eclipse-temurin:${java.next.tag}
|
||||
|
||||
# Supported versions of MongoDB
|
||||
docker.mongodb.6.0.version=6.0.23
|
||||
docker.mongodb.7.0.version=7.0.20
|
||||
docker.mongodb.8.0.version=8.0.9
|
||||
docker.mongodb.4.4.version=4.4.25
|
||||
docker.mongodb.5.0.version=5.0.21
|
||||
docker.mongodb.6.0.version=6.0.10
|
||||
docker.mongodb.7.0.version=7.0.2
|
||||
docker.mongodb.8.0.version=8.0.0
|
||||
|
||||
# Supported versions of Redis
|
||||
docker.redis.6.version=6.2.13
|
||||
docker.redis.7.version=7.2.4
|
||||
docker.valkey.8.version=8.1.1
|
||||
|
||||
# Supported versions of Cassandra
|
||||
docker.cassandra.3.version=3.11.16
|
||||
|
||||
# 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=
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-elasticsearch</artifactId>
|
||||
<version>5.5.3</version>
|
||||
<version>5.4.14-SNAPSHOT</version>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.build</groupId>
|
||||
<artifactId>spring-data-parent</artifactId>
|
||||
<version>3.5.3</version>
|
||||
<version>3.4.14-SNAPSHOT</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.3</springdata.commons>
|
||||
<springdata.commons>3.4.14-SNAPSHOT</springdata.commons>
|
||||
|
||||
<!-- version of the ElasticsearchClient -->
|
||||
<elasticsearch-java>8.18.5</elasticsearch-java>
|
||||
<elasticsearch-java>8.15.5</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>
|
||||
@@ -132,18 +136,6 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch.client</groupId>
|
||||
<artifactId>elasticsearch-rest-client</artifactId>
|
||||
<version>${elasticsearch-java}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson JSON Mapper -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
@@ -462,8 +454,20 @@
|
||||
</profiles>
|
||||
|
||||
<repositories>
|
||||
|
||||
|
||||
<repository>
|
||||
<id>spring-snapshot</id>
|
||||
<url>https://repo.spring.io/snapshot</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spring-milestone</id>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -17,7 +17,7 @@ content:
|
||||
- url: https://github.com/spring-projects/spring-data-commons
|
||||
# Refname matching:
|
||||
# https://docs.antora.org/antora/latest/playbook/content-refname-matching/
|
||||
branches: [ main, 3.4.x, 3.3.x ]
|
||||
branches: [ main, 3.2.x ]
|
||||
start_path: src/main/antora
|
||||
asciidoc:
|
||||
attributes:
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
*** xref:migration-guides/migration-guide-5.1-5.2.adoc[]
|
||||
*** xref:migration-guides/migration-guide-5.2-5.3.adoc[]
|
||||
*** xref:migration-guides/migration-guide-5.3-5.4.adoc[]
|
||||
*** xref:migration-guides/migration-guide-5.4-5.5.adoc[]
|
||||
|
||||
|
||||
* xref:elasticsearch.adoc[]
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
[[new-features]]
|
||||
= What's new
|
||||
|
||||
[[new-features.5-5-0]]
|
||||
== New in Spring Data Elasticsearch 5.5
|
||||
|
||||
* Upgrade to Elasticsearch 8.18.1.
|
||||
* Add support for the `@SearchTemplateQuery` annotation on repository methods.
|
||||
* Scripted field properties of type collection can be populated from scripts returning arrays.
|
||||
[[new-features.5-4-1]]
|
||||
== New in Spring Data Elasticsearch 5.4.1
|
||||
* Upgrade to Elasticsearch 8.15.5.
|
||||
|
||||
[[new-features.5-4-0]]
|
||||
== New in Spring Data Elasticsearch 5.4
|
||||
|
||||
@@ -365,8 +365,6 @@ operations.putScript( <.>
|
||||
|
||||
To use a search template in a search query, Spring Data Elasticsearch provides the `SearchTemplateQuery`, an implementation of the `org.springframework.data.elasticsearch.core.query.Query` interface.
|
||||
|
||||
NOTE: Although `SearchTemplateQuery` is an implementation of the `Query` interface, not all of the functionality provided by the base class is available for a `SearchTemplateQuery` like setting a `Pageable` or a `Sort`. Values for this functionality must be added to the stored script like shown in the following example for paging parameters. If these values are set on the `Query` object, they will be ignored.
|
||||
|
||||
In the following code, we will add a call using a search template query to a custom repository implementation (see
|
||||
xref:repositories/custom-implementations.adoc[]) as an example how this can be integrated into a repository call.
|
||||
|
||||
@@ -451,3 +449,4 @@ var query = Query.findAll().addSort(Sort.by(order));
|
||||
About the filter query: It is not possible to use a `CriteriaQuery` here, as this query would be converted into a Elasticsearch nested query which does not work in the filter context. So only `StringQuery` or `NativeQuery` can be used here. When using one of these, like the term query above, the Elasticsearch field names must be used, so take care, when these are redefined with the `@Field(name="...")` definition.
|
||||
|
||||
For the definition of the order path and the nested paths, the Java entity property names should be used.
|
||||
|
||||
|
||||
+6
-36
@@ -10,9 +10,7 @@ The Elasticsearch module supports all basic query building feature as string que
|
||||
=== Declared queries
|
||||
|
||||
Deriving the query from the method name is not always sufficient and/or may result in unreadable method names.
|
||||
In this case one might make use of the `@Query` annotation (see xref:elasticsearch/repositories/elasticsearch-repository-queries.adoc#elasticsearch.query-methods.at-query[Using the @Query Annotation] ).
|
||||
|
||||
Another possibility is the use of a search-template, (see xref:elasticsearch/repositories/elasticsearch-repository-queries.adoc#elasticsearch.query-methods.at-searchtemplate-query[Using the @SearchTemplateQuery Annotation] ).
|
||||
In this case one might make use of the `@Query` annotation (see xref:elasticsearch/repositories/elasticsearch-repository-queries.adoc#elasticsearch.query-methods.at-query[Using @Query Annotation] ).
|
||||
|
||||
[[elasticsearch.query-methods.criterions]]
|
||||
== Query creation
|
||||
@@ -314,13 +312,11 @@ Repository methods can be defined to have the following return types for returni
|
||||
* `SearchPage<T>`
|
||||
|
||||
[[elasticsearch.query-methods.at-query]]
|
||||
== Using the @Query Annotation
|
||||
== Using @Query Annotation
|
||||
|
||||
.Declare query on the method using the `@Query` annotation.
|
||||
====
|
||||
The arguments passed to the method can be inserted into placeholders in the query string.
|
||||
The placeholders are of the form `?0`, `?1`, `?2` etc. for the first, second, third parameter and so on.
|
||||
|
||||
The arguments passed to the method can be inserted into placeholders in the query string. The placeholders are of the form `?0`, `?1`, `?2` etc. for the first, second, third parameter and so on.
|
||||
[source,java]
|
||||
----
|
||||
interface BookRepository extends ElasticsearchRepository<Book, String> {
|
||||
@@ -345,20 +341,15 @@ It will be sent to Easticsearch as value of the query element; if for example th
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
.`@Query` annotation on a method taking a Collection argument
|
||||
====
|
||||
A repository method such as
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Query("{\"ids\": {\"values\": ?0 }}")
|
||||
List<SampleEntity> getByIds(Collection<String> ids);
|
||||
----
|
||||
|
||||
would make an https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-ids-query.html[IDs query] to return all the matching documents.
|
||||
So calling the method with a `List` of `["id1", "id2", "id3"]` would produce the query body
|
||||
|
||||
would make an https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-ids-query.html[IDs query] to return all the matching documents. So calling the method with a `List` of `["id1", "id2", "id3"]` would produce the query body
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
@@ -378,6 +369,7 @@ So calling the method with a `List` of `["id1", "id2", "id3"]` would produce the
|
||||
====
|
||||
{spring-framework-docs}/core/expressions.html[SpEL expression] is also supported when defining query in `@Query`.
|
||||
|
||||
|
||||
[source,java]
|
||||
----
|
||||
interface BookRepository extends ElasticsearchRepository<Book, String> {
|
||||
@@ -419,7 +411,6 @@ If for example the function is called with the parameter _John_, it would produc
|
||||
.accessing parameter property.
|
||||
====
|
||||
Supposing that we have the following class as query parameter type:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
public record QueryParameter(String value) {
|
||||
@@ -453,9 +444,7 @@ We can pass `new QueryParameter("John")` as the parameter now, and it will produ
|
||||
|
||||
.accessing bean property.
|
||||
====
|
||||
{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:
|
||||
|
||||
{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]
|
||||
----
|
||||
interface BookRepository extends ElasticsearchRepository<Book, String> {
|
||||
@@ -504,7 +493,6 @@ interface BookRepository extends ElasticsearchRepository<Book, String> {
|
||||
NOTE: collection values should not be quoted when declaring the elasticsearch json query.
|
||||
|
||||
A collection of `names` like `List.of("name1", "name2")` will produce the following terms query:
|
||||
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
@@ -544,7 +532,6 @@ interface BookRepository extends ElasticsearchRepository<Book, String> {
|
||||
Page<Book> findByName(Collection<QueryParameter> parameters, Pageable pageable);
|
||||
}
|
||||
----
|
||||
|
||||
This will extract all the `value` property values as a new `Collection` from `QueryParameter` collection, thus takes the same effect as above.
|
||||
====
|
||||
|
||||
@@ -573,20 +560,3 @@ interface BookRepository extends ElasticsearchRepository<Book, String> {
|
||||
----
|
||||
|
||||
====
|
||||
|
||||
[[elasticsearch.query-methods.at-searchtemplate-query]]
|
||||
== Using the @SearchTemplateQuery Annotation
|
||||
|
||||
When using Elasticsearch search templates - (see xref:elasticsearch/misc.adoc#elasticsearch.misc.searchtemplates [Search Template support]) it is possible to specify that a repository method should use a template by adding the `@SearchTemplateQuery` annotation to that method.
|
||||
|
||||
Let's assume that there is a search template stored with the name "book-by-title" and this template need a parameter named "title", then a repository method using that search template can be defined like this:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
interface BookRepository extends ElasticsearchRepository<Book, String> {
|
||||
@SearchTemplateQuery(id = "book-by-title")
|
||||
SearchHits<Book> findByTitle(String title);
|
||||
}
|
||||
----
|
||||
|
||||
The parameters of the repository method are sent to the seacrh template as key/value pairs where the key is the parameter name and the value is taken from the actual value when the method is invoked.
|
||||
|
||||
@@ -6,10 +6,9 @@ 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.5 | 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
|
||||
| 2024.1 | 5.4.x | 8.15.5 | 6.2.x
|
||||
| 2024.0 | 5.3.1 | 8.13.4 | 6.1.x
|
||||
| 2023.1 (Vaughan) | 5.2.xfootnote:oom[Out of maintenance] | 8.11.1 | 6.1.x
|
||||
| 2023.0 (Ullmann) | 5.1.xfootnote:oom[] | 8.7.1 | 6.0.x
|
||||
| 2022.0 (Turing) | 5.0.xfootnote:oom[] | 8.5.3 | 6.0.x
|
||||
| 2021.2 (Raj) | 4.4.xfootnote:oom[] | 7.17.3 | 5.3.x
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
[[elasticsearch-migration-guide-5.4-5.5]]
|
||||
= Upgrading from 5.4.x to 5.5.x
|
||||
|
||||
This section describes breaking changes from version 5.4.x to 5.5.x and how removed features can be replaced by new introduced features.
|
||||
|
||||
[[elasticsearch-migration-guide-5.4-5.5.breaking-changes]]
|
||||
== Breaking Changes
|
||||
|
||||
[[elasticsearch-migration-guide-5.4-5.5.deprecations]]
|
||||
== Deprecations
|
||||
|
||||
Some classes that probably are not used by a library user have been renamed, the classes with the old names are still there, but are deprecated:
|
||||
|
||||
|===
|
||||
|old name|new name
|
||||
|
||||
|ElasticsearchPartQuery|RepositoryPartQuery
|
||||
|ElasticsearchStringQuery|RepositoryStringQuery
|
||||
|ReactiveElasticsearchStringQuery|ReactiveRepositoryStringQuery
|
||||
|===
|
||||
|
||||
=== Removals
|
||||
|
||||
The following methods that had been deprecated since release 5.3 have been removed:
|
||||
```
|
||||
DocumentOperations.delete(Query, Class<?>)
|
||||
DocumentOperations.delete(Query, Class<?>, IndexCoordinates)
|
||||
ReactiveDocumentOperations.delete(Query, Class<?>)
|
||||
ReactiveDocumentOperations.delete(Query, Class<?>, IndexCoordinates)
|
||||
```
|
||||
@@ -5,18 +5,18 @@ asciidoc:
|
||||
attributes:
|
||||
attribute-missing: 'warn'
|
||||
chomp: 'all'
|
||||
version: ${project.version}
|
||||
copyright-year: ${current.year}
|
||||
springversionshort: ${spring.short}
|
||||
springversion: ${spring}
|
||||
commons: ${springdata.commons.docs}
|
||||
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/{commons}
|
||||
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: https://docs.spring.io/spring-framework/reference/{springversionshort}
|
||||
springdocsurl: '${documentation.baseurl}/spring-framework/reference/{springversionshort}'
|
||||
spring-framework-docs: '{springdocsurl}'
|
||||
springjavadocurl: https://docs.spring.io/spring-framework/docs/${spring}/javadoc-api
|
||||
springjavadocurl: '${documentation.spring-javadoc-url}'
|
||||
spring-framework-javadoc: '{springjavadocurl}'
|
||||
springhateoasversion: ${spring-hateoas}
|
||||
releasetrainversion: ${releasetrain}
|
||||
springhateoasversion: '${spring-hateoas}'
|
||||
releasetrainversion: '${releasetrain}'
|
||||
store: Elasticsearch
|
||||
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* 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.annotations;
|
||||
|
||||
import org.springframework.data.annotation.QueryAnnotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Annotation to mark a repository method as a search template method. The annotation defines the search template id,
|
||||
* the parameters for the search template are taken from the method's arguments.
|
||||
*
|
||||
* @author P.J. Meisch (pj.meisch@sothawo.com)
|
||||
* @since 5.5
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
|
||||
@Documented
|
||||
@QueryAnnotation
|
||||
public @interface SearchTemplateQuery {
|
||||
/**
|
||||
* The id of the search template. Must not be empt or null.
|
||||
*/
|
||||
String id();
|
||||
}
|
||||
+2
-7
@@ -18,8 +18,6 @@ package org.springframework.data.elasticsearch.client.elc;
|
||||
import co.elastic.clients.elasticsearch.ElasticsearchClient;
|
||||
import co.elastic.clients.transport.ElasticsearchTransport;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -38,10 +36,7 @@ public class AutoCloseableElasticsearchClient extends ElasticsearchClient implem
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
// since Elasticsearch 8.16 the ElasticsearchClient implements (through ApiClient) the Closeable interface and
|
||||
// handles closing of the underlying transport. We now just call the base class, but keep this as we
|
||||
// have been implementing AutoCloseable since 4.4 and won't change that to a mere Closeable
|
||||
super.close();
|
||||
public void close() throws Exception {
|
||||
transport.close();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -329,7 +329,7 @@ public final class ElasticsearchClients {
|
||||
Assert.notNull(jsonpMapper, "jsonpMapper must not be null");
|
||||
|
||||
TransportOptions.Builder transportOptionsBuilder = transportOptions != null ? transportOptions.toBuilder()
|
||||
: new RestClientOptions(RequestOptions.DEFAULT, false).toBuilder();
|
||||
: new RestClientOptions(RequestOptions.DEFAULT).toBuilder();
|
||||
|
||||
RestClientOptions.Builder restClientOptionsBuilder = getRestClientOptionsBuilder(transportOptions);
|
||||
|
||||
|
||||
+1
-1
@@ -135,6 +135,6 @@ public abstract class ElasticsearchConfiguration extends ElasticsearchConfigurat
|
||||
* @return the options that should be added to every request. Must not be {@literal null}
|
||||
*/
|
||||
public TransportOptions transportOptions() {
|
||||
return new RestClientOptions(RequestOptions.DEFAULT, false);
|
||||
return new RestClientOptions(RequestOptions.DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -181,6 +181,19 @@ public class ElasticsearchTemplate extends AbstractElasticsearchTemplate {
|
||||
return delete(query, clazz, getIndexCoordinatesFor(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByQueryResponse delete(Query query, Class<?> clazz, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, routingResolver.getRouting(),
|
||||
clazz, index, getRefreshPolicy());
|
||||
|
||||
DeleteByQueryResponse response = execute(client -> client.deleteByQuery(request));
|
||||
|
||||
return responseConverter.byQueryResponse(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByQueryResponse delete(DeleteQuery query, Class<?> clazz, IndexCoordinates index) {
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
+4
-10
@@ -27,7 +27,6 @@ import co.elastic.clients.transport.endpoints.EndpointWithResponseMapperAttr;
|
||||
import co.elastic.clients.util.ObjectBuilder;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -57,11 +56,8 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
// since Elasticsearch 8.16 the ElasticsearchClient implements (through ApiClient) the Closeable interface and
|
||||
// handles closing of the underlying transport. We now just call the base class, but keep this as we
|
||||
// have been implementing AutoCloseable since 4.4 and won't change that to a mere Closeable
|
||||
super.close();
|
||||
public void close() throws Exception {
|
||||
transport.close();
|
||||
}
|
||||
|
||||
// region child clients
|
||||
@@ -131,8 +127,7 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
|
||||
// java.lang.Class<TDocument>)
|
||||
// noinspection unchecked
|
||||
JsonEndpoint<GetRequest, GetResponse<T>, ErrorResponse> endpoint = (JsonEndpoint<GetRequest, GetResponse<T>, ErrorResponse>) GetRequest._ENDPOINT;
|
||||
endpoint = new EndpointWithResponseMapperAttr<>(endpoint,
|
||||
"co.elastic.clients:Deserializer:_global.get.Response.TDocument",
|
||||
endpoint = new EndpointWithResponseMapperAttr<>(endpoint, "co.elastic.clients:Deserializer:_global.get.Response.TDocument",
|
||||
getDeserializer(tClass));
|
||||
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, endpoint, transportOptions));
|
||||
@@ -177,8 +172,7 @@ public class ReactiveElasticsearchClient extends ApiClient<ElasticsearchTranspor
|
||||
|
||||
// noinspection unchecked
|
||||
JsonEndpoint<MgetRequest, MgetResponse<T>, ErrorResponse> endpoint = (JsonEndpoint<MgetRequest, MgetResponse<T>, ErrorResponse>) MgetRequest._ENDPOINT;
|
||||
endpoint = new EndpointWithResponseMapperAttr<>(endpoint,
|
||||
"co.elastic.clients:Deserializer:_global.mget.Response.TDocument",
|
||||
endpoint = new EndpointWithResponseMapperAttr<>(endpoint, "co.elastic.clients:Deserializer:_global.mget.Response.TDocument",
|
||||
this.getDeserializer(clazz));
|
||||
|
||||
return Mono.fromFuture(transport.performRequestAsync(request, endpoint, transportOptions));
|
||||
|
||||
+1
-1
@@ -125,6 +125,6 @@ public abstract class ReactiveElasticsearchConfiguration extends ElasticsearchCo
|
||||
* @return the options that should be added to every request. Must not be {@literal null}
|
||||
*/
|
||||
public TransportOptions transportOptions() {
|
||||
return new RestClientOptions(RequestOptions.DEFAULT, false).toBuilder().build();
|
||||
return new RestClientOptions(RequestOptions.DEFAULT).toBuilder().build();
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -167,6 +167,16 @@ public class ReactiveElasticsearchTemplate extends AbstractReactiveElasticsearch
|
||||
.onErrorReturn(NoSuchIndexException.class, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ByQueryResponse> delete(Query query, Class<?> entityType, IndexCoordinates index) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
DeleteByQueryRequest request = requestConverter.documentDeleteByQueryRequest(query, routingResolver.getRouting(),
|
||||
entityType, index, getRefreshPolicy());
|
||||
return Mono.from(execute(client -> client.deleteByQuery(request))).map(responseConverter::byQueryResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ByQueryResponse> delete(DeleteQuery query, Class<?> entityType, IndexCoordinates index) {
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
+21
-45
@@ -51,11 +51,9 @@ import co.elastic.clients.elasticsearch.indices.*;
|
||||
import co.elastic.clients.elasticsearch.indices.ExistsIndexTemplateRequest;
|
||||
import co.elastic.clients.elasticsearch.indices.ExistsRequest;
|
||||
import co.elastic.clients.elasticsearch.indices.update_aliases.Action;
|
||||
import co.elastic.clients.elasticsearch.sql.query.SqlFormat;
|
||||
import co.elastic.clients.json.JsonData;
|
||||
import co.elastic.clients.json.JsonpDeserializer;
|
||||
import co.elastic.clients.json.JsonpMapper;
|
||||
import co.elastic.clients.util.NamedValue;
|
||||
import co.elastic.clients.util.ObjectBuilder;
|
||||
import jakarta.json.stream.JsonParser;
|
||||
|
||||
@@ -73,7 +71,6 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -533,22 +530,17 @@ class RequestConverter extends AbstractQueryProcessor {
|
||||
public co.elastic.clients.elasticsearch.sql.QueryRequest sqlQueryRequest(SqlQuery query) {
|
||||
Assert.notNull(query, "Query must not be null.");
|
||||
|
||||
return co.elastic.clients.elasticsearch.sql.QueryRequest.of(sqb -> sqb
|
||||
.query(query.getQuery())
|
||||
.catalog(query.getCatalog())
|
||||
.columnar(query.getColumnar())
|
||||
.cursor(query.getCursor())
|
||||
.fetchSize(query.getFetchSize())
|
||||
.fieldMultiValueLeniency(query.getFieldMultiValueLeniency())
|
||||
.indexUsingFrozen(query.getIndexIncludeFrozen())
|
||||
.keepAlive(time(query.getKeepAlive()))
|
||||
.keepOnCompletion(query.getKeepOnCompletion())
|
||||
.pageTimeout(time(query.getPageTimeout()))
|
||||
.requestTimeout(time(query.getRequestTimeout()))
|
||||
.waitForCompletionTimeout(time(query.getWaitForCompletionTimeout()))
|
||||
.filter(getQuery(query.getFilter(), null))
|
||||
.timeZone(Objects.toString(query.getTimeZone(), null))
|
||||
.format(SqlFormat.Json));
|
||||
return co.elastic.clients.elasticsearch.sql.QueryRequest.of(sqb -> {
|
||||
sqb.query(query.getQuery()).catalog(query.getCatalog()).columnar(query.getColumnar()).cursor(query.getCursor())
|
||||
.fetchSize(query.getFetchSize()).fieldMultiValueLeniency(query.getFieldMultiValueLeniency())
|
||||
.indexUsingFrozen(query.getIndexIncludeFrozen()).keepAlive(time(query.getKeepAlive()))
|
||||
.keepOnCompletion(query.getKeepOnCompletion()).pageTimeout(time(query.getPageTimeout()))
|
||||
.requestTimeout(time(query.getRequestTimeout()))
|
||||
.waitForCompletionTimeout(time(query.getWaitForCompletionTimeout())).filter(getQuery(query.getFilter(), null))
|
||||
.timeZone(Objects.toString(query.getTimeZone(), null)).format("json");
|
||||
|
||||
return sqb;
|
||||
});
|
||||
}
|
||||
|
||||
// endregion
|
||||
@@ -1357,14 +1349,9 @@ class RequestConverter extends AbstractQueryProcessor {
|
||||
}
|
||||
|
||||
if (!isEmpty(query.getIndicesBoost())) {
|
||||
Stream<NamedValue<Double>> namedValueStream = query.getIndicesBoost().stream()
|
||||
.map(indexBoost -> {
|
||||
var namedValue = new NamedValue(indexBoost.getIndexName(),
|
||||
Float.valueOf(indexBoost.getBoost()).doubleValue());
|
||||
return namedValue;
|
||||
});
|
||||
List<NamedValue<Double>> namedValueList = namedValueStream.collect(Collectors.toList());
|
||||
bb.indicesBoost(namedValueList);
|
||||
bb.indicesBoost(query.getIndicesBoost().stream()
|
||||
.map(indexBoost -> Map.of(indexBoost.getIndexName(), (double) indexBoost.getBoost()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
query.getScriptedFields().forEach(scriptedField -> bb.scriptFields(scriptedField.getFieldName(),
|
||||
@@ -1564,14 +1551,9 @@ class RequestConverter extends AbstractQueryProcessor {
|
||||
}
|
||||
|
||||
if (!isEmpty(query.getIndicesBoost())) {
|
||||
Stream<NamedValue<Double>> namedValueStream = query.getIndicesBoost().stream()
|
||||
.map(indexBoost -> {
|
||||
var namedValue = new NamedValue(indexBoost.getIndexName(),
|
||||
Float.valueOf(indexBoost.getBoost()).doubleValue());
|
||||
return namedValue;
|
||||
});
|
||||
List<NamedValue<Double>> namedValueList = namedValueStream.collect(Collectors.toList());
|
||||
builder.indicesBoost(namedValueList);
|
||||
builder.indicesBoost(query.getIndicesBoost().stream()
|
||||
.map(indexBoost -> Map.of(indexBoost.getIndexName(), (double) indexBoost.getBoost()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
if (!isEmpty(query.getDocValueFields())) {
|
||||
@@ -2013,12 +1995,9 @@ class RequestConverter extends AbstractQueryProcessor {
|
||||
private SourceConfig getSourceConfig(Query query) {
|
||||
|
||||
if (query.getSourceFilter() != null) {
|
||||
return SourceConfig.of(s -> {
|
||||
SourceFilter sourceFilter = query.getSourceFilter();
|
||||
if (sourceFilter.fetchSource() != null) {
|
||||
s.fetch(sourceFilter.fetchSource());
|
||||
} else {
|
||||
s.filter(sfb -> {
|
||||
return SourceConfig.of(s -> s //
|
||||
.filter(sfb -> {
|
||||
SourceFilter sourceFilter = query.getSourceFilter();
|
||||
String[] includes = sourceFilter.getIncludes();
|
||||
String[] excludes = sourceFilter.getExcludes();
|
||||
|
||||
@@ -2031,10 +2010,7 @@ class RequestConverter extends AbstractQueryProcessor {
|
||||
}
|
||||
|
||||
return sfb;
|
||||
});
|
||||
}
|
||||
return s;
|
||||
});
|
||||
}));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
+3
-1
@@ -92,7 +92,7 @@ class ResponseConverter {
|
||||
return ClusterHealth.builder() //
|
||||
.withActivePrimaryShards(healthResponse.activePrimaryShards()) //
|
||||
.withActiveShards(healthResponse.activeShards()) //
|
||||
.withActiveShardsPercent(healthResponse.activeShardsPercentAsNumber())//
|
||||
.withActiveShardsPercent(Double.parseDouble(healthResponse.activeShardsPercentAsNumber()))//
|
||||
.withClusterName(healthResponse.clusterName()) //
|
||||
.withDelayedUnassignedShards(healthResponse.delayedUnassignedShards()) //
|
||||
.withInitializingShards(healthResponse.initializingShards()) //
|
||||
@@ -400,6 +400,7 @@ class ResponseConverter {
|
||||
private ReindexResponse.Failure reindexResponseFailureOf(BulkIndexByScrollFailure failure) {
|
||||
return ReindexResponse.Failure.builder() //
|
||||
.withIndex(failure.index()) //
|
||||
.withType(failure.type()) //
|
||||
.withId(failure.id()) //
|
||||
.withStatus(failure.status())//
|
||||
.withErrorCause(toErrorCause(failure.cause())) //
|
||||
@@ -410,6 +411,7 @@ class ResponseConverter {
|
||||
private ByQueryResponse.Failure byQueryResponseFailureOf(BulkIndexByScrollFailure failure) {
|
||||
return ByQueryResponse.Failure.builder() //
|
||||
.withIndex(failure.index()) //
|
||||
.withType(failure.type()) //
|
||||
.withId(failure.id()) //
|
||||
.withStatus(failure.status())//
|
||||
.withErrorCause(toErrorCause(failure.cause())).build();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+6
@@ -298,6 +298,12 @@ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOper
|
||||
return this.delete(id, getIndexCoordinatesFor(entityType));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public ByQueryResponse delete(Query query, Class<?> clazz) {
|
||||
return delete(query, clazz, getIndexCoordinatesFor(clazz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String delete(Object entity) {
|
||||
return delete(entity, getIndexCoordinatesFor(entity.getClass()));
|
||||
|
||||
+6
@@ -414,6 +414,12 @@ abstract public class AbstractReactiveElasticsearchTemplate
|
||||
|
||||
abstract protected Mono<String> doDeleteById(String id, @Nullable String routing, IndexCoordinates index);
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Mono<ByQueryResponse> delete(Query query, Class<?> entityType) {
|
||||
return delete(query, entityType, getIndexCoordinatesFor(entityType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<ByQueryResponse> delete(DeleteQuery query, Class<?> entityType) {
|
||||
return delete(query, entityType, getIndexCoordinatesFor(entityType));
|
||||
|
||||
@@ -272,6 +272,19 @@ public interface DocumentOperations {
|
||||
*/
|
||||
String delete(Object entity, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Delete all records matching the query.
|
||||
*
|
||||
* @param query query defining the objects
|
||||
* @param clazz The entity class, must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @return response with detailed information
|
||||
* @since 4.1
|
||||
* @deprecated since 5.3.0, use {@link #delete(DeleteQuery, Class)}
|
||||
*/
|
||||
@Deprecated
|
||||
ByQueryResponse delete(Query query, Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Delete all records matching the query.
|
||||
*
|
||||
@@ -283,6 +296,19 @@ public interface DocumentOperations {
|
||||
*/
|
||||
ByQueryResponse delete(DeleteQuery query, Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Delete all records matching the query.
|
||||
*
|
||||
* @param query query defining the objects
|
||||
* @param clazz The entity class, must be annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.Document}
|
||||
* @param index the index from which to delete
|
||||
* @return response with detailed information
|
||||
* @deprecated since 5.3.0, use {@link #delete(DeleteQuery, Class, IndexCoordinates)}
|
||||
*/
|
||||
@Deprecated
|
||||
ByQueryResponse delete(Query query, Class<?> clazz, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Delete all records matching the query.
|
||||
*
|
||||
|
||||
+23
@@ -326,6 +326,17 @@ public interface ReactiveDocumentOperations {
|
||||
*/
|
||||
Mono<String> delete(String id, Class<?> entityType);
|
||||
|
||||
/**
|
||||
* Delete the documents matching the given {@link Query} extracting index from entity metadata.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @return a {@link Mono} emitting the number of the removed documents.
|
||||
* @deprecated since 5.3.0, use {@link #delete(DeleteQuery, Class)}
|
||||
*/
|
||||
@Deprecated
|
||||
Mono<ByQueryResponse> delete(Query query, Class<?> entityType);
|
||||
|
||||
/**
|
||||
* Delete the documents matching the given {@link Query} extracting index from entity metadata.
|
||||
*
|
||||
@@ -336,6 +347,18 @@ public interface ReactiveDocumentOperations {
|
||||
*/
|
||||
Mono<ByQueryResponse> delete(DeleteQuery query, Class<?> entityType);
|
||||
|
||||
/**
|
||||
* Delete the documents matching the given {@link Query} extracting index from entity metadata.
|
||||
*
|
||||
* @param query must not be {@literal null}.
|
||||
* @param entityType must not be {@literal null}.
|
||||
* @param index the target index, must not be {@literal null}
|
||||
* @return a {@link Mono} emitting the number of the removed documents.
|
||||
* @deprecated since 5.3.0, use {@link #delete(DeleteQuery, Class, IndexCoordinates)}
|
||||
*/
|
||||
@Deprecated
|
||||
Mono<ByQueryResponse> delete(Query query, Class<?> entityType, IndexCoordinates index);
|
||||
|
||||
/**
|
||||
* Delete the documents matching the given {@link Query} extracting index from entity metadata.
|
||||
*
|
||||
|
||||
+3
-3
@@ -19,14 +19,14 @@ import java.time.temporal.TemporalAccessor;
|
||||
|
||||
/**
|
||||
* Interface to convert from and to {@link TemporalAccessor}s.
|
||||
*
|
||||
*
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.2
|
||||
*/
|
||||
public interface DateFormatter {
|
||||
/**
|
||||
* Formats a {@link TemporalAccessor} into a String.
|
||||
*
|
||||
*
|
||||
* @param accessor must not be {@literal null}
|
||||
* @return the formatted String
|
||||
*/
|
||||
@@ -34,7 +34,7 @@ public interface DateFormatter {
|
||||
|
||||
/**
|
||||
* Parses a String into a {@link TemporalAccessor}.
|
||||
*
|
||||
*
|
||||
* @param input the String to parse, must not be {@literal null}
|
||||
* @param type the class of T
|
||||
* @param <T> the {@link TemporalAccessor} implementation
|
||||
|
||||
+5
-19
@@ -394,7 +394,7 @@ public class MappingElasticsearchConverter
|
||||
}
|
||||
|
||||
if (source instanceof SearchDocument searchDocument) {
|
||||
populateScriptedFields(targetEntity, result, searchDocument);
|
||||
populateScriptFields(targetEntity, result, searchDocument);
|
||||
}
|
||||
return result;
|
||||
} catch (ConversionException e) {
|
||||
@@ -652,16 +652,7 @@ public class MappingElasticsearchConverter
|
||||
return conversionService.convert(value, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any of the properties of the entity is annotated with
|
||||
*
|
||||
* @{@link ScriptedField}. If so, the value of this property is set from the returned fields in the document.
|
||||
* @param entity the entity to defining the persistent property
|
||||
* @param result the rsult to populate
|
||||
* @param searchDocument the search result caontaining the fields
|
||||
* @param <T> the result type
|
||||
*/
|
||||
private <T> void populateScriptedFields(ElasticsearchPersistentEntity<?> entity, T result,
|
||||
private <T> void populateScriptFields(ElasticsearchPersistentEntity<?> entity, T result,
|
||||
SearchDocument searchDocument) {
|
||||
Map<String, List<Object>> fields = searchDocument.getFields();
|
||||
entity.doWithProperties((SimplePropertyHandler) property -> {
|
||||
@@ -670,13 +661,8 @@ public class MappingElasticsearchConverter
|
||||
// noinspection ConstantConditions
|
||||
String name = scriptedField.name().isEmpty() ? property.getName() : scriptedField.name();
|
||||
if (fields.containsKey(name)) {
|
||||
if (property.isCollectionLike()) {
|
||||
List<Object> values = searchDocument.getFieldValues(name);
|
||||
entity.getPropertyAccessor(result).setProperty(property, values);
|
||||
} else {
|
||||
Object value = searchDocument.getFieldValue(name);
|
||||
entity.getPropertyAccessor(result).setProperty(property, value);
|
||||
}
|
||||
Object value = searchDocument.getFieldValue(name);
|
||||
entity.getPropertyAccessor(result).setProperty(property, value);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1298,7 +1284,7 @@ public class MappingElasticsearchConverter
|
||||
.toArray(new String[] {});
|
||||
}
|
||||
|
||||
query.addSourceFilter(new FetchSourceFilter(sourceFilter.fetchSource(), includes, excludes));
|
||||
query.addSourceFilter(new FetchSourceFilter(includes, excludes));
|
||||
}
|
||||
|
||||
if (query.getSort() != null) {
|
||||
|
||||
-14
@@ -57,20 +57,6 @@ public interface SearchDocument extends Document {
|
||||
return (V) values.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name the field name
|
||||
* @param <V> the type of elements
|
||||
* @return the values of the given field.
|
||||
*/
|
||||
@Nullable
|
||||
default <V> List<V> getFieldValues(final String name) {
|
||||
List<Object> values = getFields().get(name);
|
||||
if (values == null) {
|
||||
return null;
|
||||
}
|
||||
return (List<V>) values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the sort values for the search hit
|
||||
*/
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.springframework.data.elasticsearch.core.document.Document;
|
||||
|
||||
/**
|
||||
* Interface definition for structures defined in <a href="https://geojson.org">GeoJSON</a>
|
||||
* format. copied from Spring Data Mongodb
|
||||
* format. copied from Spring Data Mongodb
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.7
|
||||
|
||||
+2
-2
@@ -69,7 +69,7 @@ public class GeoJsonLineString implements GeoJson<Iterable<Point>> {
|
||||
Assert.notNull(second, "Second point must not be null!");
|
||||
Assert.notNull(others, "Additional points must not be null!");
|
||||
|
||||
List<Point> points = new ArrayList<>(2 + others.length);
|
||||
List<Point> points = new ArrayList<>();
|
||||
points.add(first);
|
||||
points.add(second);
|
||||
points.addAll(Arrays.asList(others));
|
||||
@@ -103,7 +103,7 @@ public class GeoJsonLineString implements GeoJson<Iterable<Point>> {
|
||||
Assert.notNull(second, "Second point must not be null!");
|
||||
Assert.notNull(others, "Additional points must not be null!");
|
||||
|
||||
List<Point> points = new ArrayList<>(2 + others.length);
|
||||
List<Point> points = new ArrayList<>();
|
||||
points.add(GeoPoint.toPoint(first));
|
||||
points.add(GeoPoint.toPoint(second));
|
||||
points.addAll(Arrays.stream(others).map(GeoPoint::toPoint).collect(Collectors.toList()));
|
||||
|
||||
+2
-2
@@ -69,7 +69,7 @@ public class GeoJsonMultiPoint implements GeoJson<Iterable<Point>> {
|
||||
Assert.notNull(second, "Second point must not be null!");
|
||||
Assert.notNull(others, "Additional points must not be null!");
|
||||
|
||||
List<Point> points = new ArrayList<>(2 + others.length);
|
||||
List<Point> points = new ArrayList<>();
|
||||
points.add(first);
|
||||
points.add(second);
|
||||
points.addAll(Arrays.asList(others));
|
||||
@@ -103,7 +103,7 @@ public class GeoJsonMultiPoint implements GeoJson<Iterable<Point>> {
|
||||
Assert.notNull(second, "Second point must not be null!");
|
||||
Assert.notNull(others, "Additional points must not be null!");
|
||||
|
||||
List<Point> points = new ArrayList<>(2 + others.length);
|
||||
List<Point> points = new ArrayList<>();
|
||||
points.add(GeoPoint.toPoint(first));
|
||||
points.add(GeoPoint.toPoint(second));
|
||||
points.addAll(Arrays.stream(others).map(GeoPoint::toPoint).collect(Collectors.toList()));
|
||||
|
||||
@@ -189,13 +189,7 @@ public class GeoJsonPolygon implements GeoJson<Iterable<GeoJsonLineString>> {
|
||||
@SafeVarargs
|
||||
private static <T> List<T> asList(T first, T second, T third, T fourth, T... others) {
|
||||
|
||||
Assert.notNull(first, "First element must not be null!");
|
||||
Assert.notNull(second, "Second element must not be null!");
|
||||
Assert.notNull(third, "Third element must not be null!");
|
||||
Assert.notNull(fourth, "Fourth element must not be null!");
|
||||
Assert.notNull(others, "Additional elements must not be null!");
|
||||
|
||||
ArrayList<T> result = new ArrayList<>(4 + others.length);
|
||||
ArrayList<T> result = new ArrayList<>(3 + others.length);
|
||||
|
||||
result.add(first);
|
||||
result.add(second);
|
||||
|
||||
+15
-2
@@ -167,6 +167,7 @@ public class ByQueryResponse {
|
||||
public static class Failure {
|
||||
|
||||
@Nullable private final String index;
|
||||
@Nullable private final String type;
|
||||
@Nullable private final String id;
|
||||
@Nullable private final Exception cause;
|
||||
@Nullable private final Integer status;
|
||||
@@ -175,10 +176,11 @@ public class ByQueryResponse {
|
||||
@Nullable private final Boolean aborted;
|
||||
@Nullable private final ElasticsearchErrorCause elasticsearchErrorCause;
|
||||
|
||||
private Failure(@Nullable String index, @Nullable String id, @Nullable Exception cause,
|
||||
private Failure(@Nullable String index, @Nullable String type, @Nullable String id, @Nullable Exception cause,
|
||||
@Nullable Integer status, @Nullable Long seqNo, @Nullable Long term, @Nullable Boolean aborted,
|
||||
@Nullable ElasticsearchErrorCause elasticsearchErrorCause) {
|
||||
this.index = index;
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.cause = cause;
|
||||
this.status = status;
|
||||
@@ -193,6 +195,11 @@ public class ByQueryResponse {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
@@ -243,6 +250,7 @@ public class ByQueryResponse {
|
||||
*/
|
||||
public static final class FailureBuilder {
|
||||
@Nullable private String index;
|
||||
@Nullable private String type;
|
||||
@Nullable private String id;
|
||||
@Nullable private Exception cause;
|
||||
@Nullable private Integer status;
|
||||
@@ -258,6 +266,11 @@ public class ByQueryResponse {
|
||||
return this;
|
||||
}
|
||||
|
||||
public FailureBuilder withType(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FailureBuilder withId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
@@ -294,7 +307,7 @@ public class ByQueryResponse {
|
||||
}
|
||||
|
||||
public Failure build() {
|
||||
return new Failure(index, id, cause, status, seqNo, term, aborted, elasticsearchErrorCause);
|
||||
return new Failure(index, type, id, cause, status, seqNo, term, aborted, elasticsearchErrorCause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+3
-12
@@ -28,16 +28,14 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class FetchSourceFilter implements SourceFilter {
|
||||
|
||||
@Nullable private final Boolean fetchSource;
|
||||
@Nullable private final String[] includes;
|
||||
@Nullable private final String[] excludes;
|
||||
|
||||
/**
|
||||
* @since 5.2
|
||||
*/
|
||||
public static SourceFilter of(@Nullable Boolean fetchSource, @Nullable final String[] includes,
|
||||
@Nullable final String[] excludes) {
|
||||
return new FetchSourceFilter(fetchSource, includes, excludes);
|
||||
public static SourceFilter of(@Nullable final String[] includes, @Nullable final String[] excludes) {
|
||||
return new FetchSourceFilter(includes, excludes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,18 +48,11 @@ public class FetchSourceFilter implements SourceFilter {
|
||||
return builderFunction.apply(new FetchSourceFilterBuilder()).build();
|
||||
}
|
||||
|
||||
public FetchSourceFilter(@Nullable Boolean fetchSource, @Nullable final String[] includes,
|
||||
@Nullable final String[] excludes) {
|
||||
this.fetchSource = fetchSource;
|
||||
public FetchSourceFilter(@Nullable final String[] includes, @Nullable final String[] excludes) {
|
||||
this.includes = includes;
|
||||
this.excludes = excludes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean fetchSource() {
|
||||
return fetchSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getIncludes() {
|
||||
return includes;
|
||||
|
||||
+1
-7
@@ -25,7 +25,6 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
public class FetchSourceFilterBuilder {
|
||||
|
||||
@Nullable private Boolean fetchSource;
|
||||
@Nullable private String[] includes;
|
||||
@Nullable private String[] excludes;
|
||||
|
||||
@@ -39,17 +38,12 @@ public class FetchSourceFilterBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public FetchSourceFilterBuilder withFetchSource(Boolean fetchSource) {
|
||||
this.fetchSource = fetchSource;
|
||||
return this;
|
||||
}
|
||||
|
||||
public SourceFilter build() {
|
||||
if (includes == null)
|
||||
includes = new String[0];
|
||||
if (excludes == null)
|
||||
excludes = new String[0];
|
||||
|
||||
return new FetchSourceFilter(fetchSource, includes, excludes);
|
||||
return new FetchSourceFilter(includes, excludes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,13 +54,6 @@ public class IndexQuery {
|
||||
this.indexName = indexName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.5
|
||||
*/
|
||||
public static IndexQueryBuilder builder() {
|
||||
return new IndexQueryBuilder();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
|
||||
+6
-18
@@ -15,22 +15,22 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.core.query;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 5.1
|
||||
*/
|
||||
public class SearchTemplateQueryBuilder extends BaseQueryBuilder<SearchTemplateQuery, SearchTemplateQueryBuilder> {
|
||||
|
||||
@Nullable private String id;
|
||||
@Nullable
|
||||
private String id;
|
||||
@Nullable String source;
|
||||
|
||||
@Nullable Map<String, Object> params;
|
||||
@Nullable
|
||||
Map<String, Object> params;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
@@ -62,18 +62,6 @@ public class SearchTemplateQueryBuilder extends BaseQueryBuilder<SearchTemplateQ
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchTemplateQueryBuilder withSort(Sort sort) {
|
||||
throw new IllegalArgumentException(
|
||||
"sort is not supported in a searchtemplate query. Sort values must be defined in the stored template");
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchTemplateQueryBuilder withPageable(Pageable pageable) {
|
||||
throw new IllegalArgumentException(
|
||||
"paging is not supported in a searchtemplate query. from and size values must be defined in the stored template");
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchTemplateQuery build() {
|
||||
return new SearchTemplateQuery(this);
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* SourceFilter for providing includes and excludes. Using these helps in reducing the amount of data that is returned
|
||||
* from Elasticsearch especially when the stored documents are large and only some fields from these documents are
|
||||
* from Elasticsearch especially when the stored docuements are large and only some fields from these documents are
|
||||
* needed. If the SourceFilter includes the name of a property that has a different name mapped in Elasticsearch (see
|
||||
* {@link Field#name()} this will automatically be mapped.
|
||||
*
|
||||
@@ -40,15 +40,4 @@ public interface SourceFilter {
|
||||
*/
|
||||
@Nullable
|
||||
String[] getExcludes();
|
||||
|
||||
/**
|
||||
* Flag to set the _source parameter in a query to true or false. If this is not null, the values returned from
|
||||
* getIncludes() and getExcludes() are ignored
|
||||
*
|
||||
* @since 5.5
|
||||
*/
|
||||
@Nullable
|
||||
default Boolean fetchSource() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
-8
@@ -80,10 +80,6 @@ public abstract class HighlightCommonParameters {
|
||||
return boundaryScannerLocale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated the underlying functionality is deprecated since Elasticsearch 8.8.
|
||||
*/
|
||||
@Deprecated(since = "5.5")
|
||||
public boolean getForceSource() {
|
||||
return forceSource;
|
||||
}
|
||||
@@ -177,10 +173,6 @@ public abstract class HighlightCommonParameters {
|
||||
return (SELF) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated the underlying functionality is deprecated since Elasticsearch 8.8.
|
||||
*/
|
||||
@Deprecated(since = "5.5")
|
||||
public SELF withForceSource(boolean forceSource) {
|
||||
this.forceSource = forceSource;
|
||||
return (SELF) this;
|
||||
|
||||
+15
-2
@@ -187,6 +187,7 @@ public class ReindexResponse {
|
||||
public static class Failure {
|
||||
|
||||
@Nullable private final String index;
|
||||
@Nullable private final String type;
|
||||
@Nullable private final String id;
|
||||
@Nullable private final Exception cause;
|
||||
@Nullable private final Integer status;
|
||||
@@ -195,10 +196,11 @@ public class ReindexResponse {
|
||||
@Nullable private final Boolean aborted;
|
||||
@Nullable private final ElasticsearchErrorCause elasticsearchErrorCause;
|
||||
|
||||
private Failure(@Nullable String index, @Nullable String id, @Nullable Exception cause,
|
||||
private Failure(@Nullable String index, @Nullable String type, @Nullable String id, @Nullable Exception cause,
|
||||
@Nullable Integer status, @Nullable Long seqNo, @Nullable Long term, @Nullable Boolean aborted,
|
||||
@Nullable ElasticsearchErrorCause elasticsearchErrorCause) {
|
||||
this.index = index;
|
||||
this.type = type;
|
||||
this.id = id;
|
||||
this.cause = cause;
|
||||
this.status = status;
|
||||
@@ -213,6 +215,11 @@ public class ReindexResponse {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
@@ -262,6 +269,7 @@ public class ReindexResponse {
|
||||
*/
|
||||
public static final class FailureBuilder {
|
||||
@Nullable private String index;
|
||||
@Nullable private String type;
|
||||
@Nullable private String id;
|
||||
@Nullable private Exception cause;
|
||||
@Nullable private Integer status;
|
||||
@@ -277,6 +285,11 @@ public class ReindexResponse {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Failure.FailureBuilder withType(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Failure.FailureBuilder withId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
@@ -313,7 +326,7 @@ public class ReindexResponse {
|
||||
}
|
||||
|
||||
public Failure build() {
|
||||
return new Failure(index, id, cause, status, seqNo, term, aborted, elasticsearchErrorCause);
|
||||
return new Failure(index, type, id, cause, status, seqNo, term, aborted, elasticsearchErrorCause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-14
@@ -24,10 +24,9 @@ import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.DeleteQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.SearchTemplateQuery;
|
||||
import org.springframework.data.expression.ValueEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ParametersParameterAccessor;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.util.StreamUtils;
|
||||
@@ -50,11 +49,11 @@ public abstract class AbstractElasticsearchRepositoryQuery implements Repository
|
||||
protected ElasticsearchQueryMethod queryMethod;
|
||||
protected final ElasticsearchOperations elasticsearchOperations;
|
||||
protected final ElasticsearchConverter elasticsearchConverter;
|
||||
protected final ValueEvaluationContextProvider evaluationContextProvider;
|
||||
protected final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
|
||||
public AbstractElasticsearchRepositoryQuery(ElasticsearchQueryMethod queryMethod,
|
||||
ElasticsearchOperations elasticsearchOperations,
|
||||
ValueEvaluationContextProvider evaluationContextProvider) {
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
Assert.notNull(queryMethod, "queryMethod must not be null");
|
||||
Assert.notNull(elasticsearchOperations, "elasticsearchOperations must not be null");
|
||||
@@ -115,15 +114,11 @@ public abstract class AbstractElasticsearchRepositoryQuery implements Repository
|
||||
: PageRequest.of(0, DEFAULT_STREAM_BATCH_SIZE));
|
||||
result = StreamUtils.createStreamFromIterator(elasticsearchOperations.searchForStream(query, clazz, index));
|
||||
} else if (queryMethod.isCollectionQuery()) {
|
||||
if (query instanceof SearchTemplateQuery) {
|
||||
// we cannot get a count here, from and size would be in the template
|
||||
if (parameterAccessor.getPageable().isUnpaged()) {
|
||||
int itemCount = (int) elasticsearchOperations.count(query, clazz, index);
|
||||
query.setPageable(PageRequest.of(0, Math.max(1, itemCount)));
|
||||
} else {
|
||||
if (parameterAccessor.getPageable().isUnpaged()) {
|
||||
int itemCount = (int) elasticsearchOperations.count(query, clazz, index);
|
||||
query.setPageable(PageRequest.of(0, Math.max(1, itemCount)));
|
||||
} else {
|
||||
query.setPageable(parameterAccessor.getPageable());
|
||||
}
|
||||
query.setPageable(parameterAccessor.getPageable());
|
||||
}
|
||||
result = elasticsearchOperations.search(query, clazz, index);
|
||||
} else {
|
||||
@@ -142,8 +137,7 @@ public abstract class AbstractElasticsearchRepositoryQuery implements Repository
|
||||
var query = createQuery(parameterAccessor);
|
||||
Assert.notNull(query, "unsupported query");
|
||||
|
||||
queryMethod.addSpecialMethodParameters(query, parameterAccessor,
|
||||
elasticsearchOperations.getElasticsearchConverter(),
|
||||
queryMethod.addMethodParameter(query, parameterAccessor, elasticsearchOperations.getElasticsearchConverter(),
|
||||
evaluationContextProvider);
|
||||
|
||||
return query;
|
||||
|
||||
+3
-4
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.repository.query;
|
||||
|
||||
import org.springframework.data.expression.ValueEvaluationContextProvider;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -53,11 +52,11 @@ abstract class AbstractReactiveElasticsearchRepositoryQuery implements Repositor
|
||||
|
||||
protected final ReactiveElasticsearchQueryMethod queryMethod;
|
||||
private final ReactiveElasticsearchOperations elasticsearchOperations;
|
||||
protected final ValueEvaluationContextProvider evaluationContextProvider;
|
||||
protected final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
|
||||
AbstractReactiveElasticsearchRepositoryQuery(ReactiveElasticsearchQueryMethod queryMethod,
|
||||
ReactiveElasticsearchOperations elasticsearchOperations,
|
||||
ValueEvaluationContextProvider evaluationContextProvider) {
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
Assert.notNull(queryMethod, "queryMethod must not be null");
|
||||
Assert.notNull(elasticsearchOperations, "elasticsearchOperations must not be null");
|
||||
@@ -106,7 +105,7 @@ abstract class AbstractReactiveElasticsearchRepositoryQuery implements Repositor
|
||||
var query = createQuery(parameterAccessor);
|
||||
Assert.notNull(query, "unsupported query");
|
||||
|
||||
queryMethod.addSpecialMethodParameters(query, parameterAccessor, elasticsearchOperations.getElasticsearchConverter(),
|
||||
queryMethod.addMethodParameter(query, parameterAccessor, elasticsearchOperations.getElasticsearchConverter(),
|
||||
evaluationContextProvider);
|
||||
|
||||
String indexName = queryMethod.getEntityInformation().getIndexName();
|
||||
|
||||
+41
-6
@@ -16,7 +16,12 @@
|
||||
package org.springframework.data.elasticsearch.repository.query;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.repository.query.parser.ElasticsearchQueryCreator;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
/**
|
||||
* ElasticsearchPartQuery
|
||||
@@ -28,12 +33,42 @@ import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
* @author Rasmus Faber-Espensen
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Haibo Liu
|
||||
* @deprecated since 5.5, use {@link RepositoryPartQuery} instead
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public class ElasticsearchPartQuery extends RepositoryPartQuery {
|
||||
public class ElasticsearchPartQuery extends AbstractElasticsearchRepositoryQuery {
|
||||
|
||||
private final PartTree tree;
|
||||
private final MappingContext<?, ElasticsearchPersistentProperty> mappingContext;
|
||||
|
||||
public ElasticsearchPartQuery(ElasticsearchQueryMethod method, ElasticsearchOperations elasticsearchOperations,
|
||||
ValueExpressionDelegate valueExpressionDelegate) {
|
||||
super(method, elasticsearchOperations, valueExpressionDelegate);
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
super(method, elasticsearchOperations, evaluationContextProvider);
|
||||
this.tree = new PartTree(queryMethod.getName(), queryMethod.getResultProcessor().getReturnedType().getDomainType());
|
||||
this.mappingContext = elasticsearchConverter.getMappingContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCountQuery() {
|
||||
return tree.isCountProjection();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDeleteQuery() {
|
||||
return tree.isDelete();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isExistsQuery() {
|
||||
return tree.isExistsProjection();
|
||||
}
|
||||
|
||||
protected BaseQuery createQuery(ElasticsearchParametersParameterAccessor accessor) {
|
||||
|
||||
BaseQuery query = new ElasticsearchQueryCreator(tree, accessor, mappingContext).createQuery();
|
||||
|
||||
if (tree.getMaxResults() != null) {
|
||||
query.setMaxResults(tree.getMaxResults());
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
}
|
||||
|
||||
+14
-36
@@ -28,7 +28,6 @@ import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.data.elasticsearch.annotations.Highlight;
|
||||
import org.springframework.data.elasticsearch.annotations.Query;
|
||||
import org.springframework.data.elasticsearch.annotations.SearchTemplateQuery;
|
||||
import org.springframework.data.elasticsearch.annotations.SourceFilters;
|
||||
import org.springframework.data.elasticsearch.core.SearchHit;
|
||||
import org.springframework.data.elasticsearch.core.SearchHits;
|
||||
@@ -44,7 +43,6 @@ import org.springframework.data.elasticsearch.core.query.RuntimeField;
|
||||
import org.springframework.data.elasticsearch.core.query.ScriptedField;
|
||||
import org.springframework.data.elasticsearch.core.query.SourceFilter;
|
||||
import org.springframework.data.elasticsearch.repository.support.QueryStringProcessor;
|
||||
import org.springframework.data.expression.ValueEvaluationContextProvider;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
@@ -86,7 +84,6 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
||||
@Nullable private final Query queryAnnotation;
|
||||
@Nullable private final Highlight highlightAnnotation;
|
||||
@Nullable private final SourceFilters sourceFilters;
|
||||
@Nullable private final SearchTemplateQuery searchTemplateQueryAnnotation;
|
||||
|
||||
public ElasticsearchQueryMethod(Method method, RepositoryMetadata repositoryMetadata, ProjectionFactory factory,
|
||||
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) {
|
||||
@@ -101,11 +98,17 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
||||
this.highlightAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, Highlight.class);
|
||||
this.sourceFilters = AnnotatedElementUtils.findMergedAnnotation(method, SourceFilters.class);
|
||||
this.unwrappedReturnType = potentiallyUnwrapReturnTypeFor(repositoryMetadata, method);
|
||||
this.searchTemplateQueryAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, SearchTemplateQuery.class);
|
||||
|
||||
verifyCountQueryTypes();
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
@Override
|
||||
@Deprecated
|
||||
protected Parameters<?, ?> createParameters(Method method, TypeInformation<?> domainType) {
|
||||
return new ElasticsearchParameters(ParametersSource.of(method));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Parameters<?, ?> createParameters(ParametersSource parametersSource) {
|
||||
return new ElasticsearchParameters(parametersSource);
|
||||
@@ -122,16 +125,12 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if the method is annotated with the {@link Query} annotation.
|
||||
*/
|
||||
public boolean hasAnnotatedQuery() {
|
||||
return this.queryAnnotation != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the query String defined in the {@link Query} annotation. Must not be {@literal null} when
|
||||
* {@link #hasAnnotatedQuery()} returns true.
|
||||
* @return the query String. Must not be {@literal null} when {@link #hasAnnotatedQuery()} returns true
|
||||
*/
|
||||
@Nullable
|
||||
public String getAnnotatedQuery() {
|
||||
@@ -159,27 +158,6 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
||||
return new HighlightQuery(highlightConverter.convert(highlightAnnotation), getDomainClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return if the method is annotated with the {@link SearchTemplateQuery} annotation.
|
||||
* @since 5.5
|
||||
*/
|
||||
public boolean hasAnnotatedSearchTemplateQuery() {
|
||||
return this.searchTemplateQueryAnnotation != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link SearchTemplateQuery} annotation
|
||||
* @throws IllegalArgumentException if no {@link SearchTemplateQuery} annotation is present on the method
|
||||
* @since 5.5
|
||||
*/
|
||||
public SearchTemplateQuery getAnnotatedSearchTemplateQuery() {
|
||||
|
||||
Assert.isTrue(hasAnnotatedSearchTemplateQuery(), "no SearchTemplateQuery annotation present on " + getName());
|
||||
Assert.notNull(searchTemplateQueryAnnotation, "highlsearchTemplateQueryAnnotationightAnnotation must not be null");
|
||||
|
||||
return searchTemplateQueryAnnotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link ElasticsearchEntityMetadata} for the query methods {@link #getReturnedObjectType() return type}.
|
||||
* @since 3.2
|
||||
@@ -303,7 +281,7 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
||||
|
||||
/**
|
||||
* @return {@literal true} if the method is annotated with
|
||||
* {@link org.springframework.data.elasticsearch.annotations.CountQuery} or with {@link Query}(count = true)
|
||||
* {@link org.springframework.data.elasticsearch.annotations.CountQuery} or with {@link Query}(count =true)
|
||||
* @since 4.2
|
||||
*/
|
||||
public boolean hasCountQueryAnnotation() {
|
||||
@@ -325,7 +303,7 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
||||
@Nullable
|
||||
SourceFilter getSourceFilter(ElasticsearchParametersParameterAccessor parameterAccessor,
|
||||
ElasticsearchConverter converter,
|
||||
ValueEvaluationContextProvider evaluationContextProvider) {
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
if (sourceFilters == null || (sourceFilters.includes().length == 0 && sourceFilters.excludes().length == 0)) {
|
||||
return null;
|
||||
@@ -348,7 +326,7 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
||||
}
|
||||
|
||||
private String[] mapParameters(String[] source, ElasticsearchParametersParameterAccessor parameterAccessor,
|
||||
ConversionService conversionService, ValueEvaluationContextProvider evaluationContextProvider) {
|
||||
ConversionService conversionService, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
List<String> fieldNames = new ArrayList<>();
|
||||
|
||||
@@ -399,9 +377,9 @@ public class ElasticsearchQueryMethod extends QueryMethod {
|
||||
}
|
||||
}
|
||||
|
||||
void addSpecialMethodParameters(BaseQuery query, ElasticsearchParametersParameterAccessor parameterAccessor,
|
||||
ElasticsearchConverter elasticsearchConverter,
|
||||
ValueEvaluationContextProvider evaluationContextProvider) {
|
||||
void addMethodParameter(BaseQuery query, ElasticsearchParametersParameterAccessor parameterAccessor,
|
||||
ElasticsearchConverter elasticsearchConverter,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
if (hasAnnotatedHighlight()) {
|
||||
var highlightQuery = getAnnotatedHighlightQuery(new HighlightConverter(parameterAccessor,
|
||||
|
||||
+42
-6
@@ -15,8 +15,13 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.repository.query;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.StringQuery;
|
||||
import org.springframework.data.elasticsearch.repository.support.QueryStringProcessor;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* ElasticsearchStringQuery
|
||||
@@ -27,12 +32,43 @@ import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
* @author Taylor Ono
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Haibo Liu
|
||||
* @deprecated since 5.5, use {@link RepositoryStringQuery}
|
||||
*/
|
||||
@Deprecated(since = "5.5", forRemoval = true)
|
||||
public class ElasticsearchStringQuery extends RepositoryStringQuery {
|
||||
public class ElasticsearchStringQuery extends AbstractElasticsearchRepositoryQuery {
|
||||
|
||||
private final String queryString;
|
||||
|
||||
public ElasticsearchStringQuery(ElasticsearchQueryMethod queryMethod, ElasticsearchOperations elasticsearchOperations,
|
||||
String queryString, ValueExpressionDelegate valueExpressionDelegate) {
|
||||
super(queryMethod, elasticsearchOperations, queryString, valueExpressionDelegate);
|
||||
String queryString, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
super(queryMethod, elasticsearchOperations, evaluationContextProvider);
|
||||
|
||||
Assert.notNull(queryString, "Query cannot be empty");
|
||||
Assert.notNull(evaluationContextProvider, "ExpressionEvaluationContextProvider must not be null");
|
||||
|
||||
this.queryString = queryString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCountQuery() {
|
||||
return queryMethod.hasCountQueryAnnotation();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDeleteQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isExistsQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected BaseQuery createQuery(ElasticsearchParametersParameterAccessor parameterAccessor) {
|
||||
ConversionService conversionService = elasticsearchOperations.getElasticsearchConverter().getConversionService();
|
||||
var processed = new QueryStringProcessor(queryString, queryMethod, conversionService, evaluationContextProvider)
|
||||
.createQuery(parameterAccessor);
|
||||
|
||||
return new StringQuery(processed)
|
||||
.addSort(parameterAccessor.getSort());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-5
@@ -25,8 +25,8 @@ import org.springframework.data.elasticsearch.core.query.highlight.Highlight;
|
||||
import org.springframework.data.elasticsearch.core.query.highlight.HighlightField;
|
||||
import org.springframework.data.elasticsearch.core.query.highlight.HighlightParameters;
|
||||
import org.springframework.data.elasticsearch.repository.support.QueryStringProcessor;
|
||||
import org.springframework.data.expression.ValueEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -38,13 +38,13 @@ public class HighlightConverter {
|
||||
|
||||
private final ElasticsearchParametersParameterAccessor parameterAccessor;
|
||||
private final ConversionService conversionService;
|
||||
private final ValueEvaluationContextProvider evaluationContextProvider;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final QueryMethod queryMethod;
|
||||
|
||||
HighlightConverter(ElasticsearchParametersParameterAccessor parameterAccessor,
|
||||
ConversionService conversionService,
|
||||
ValueEvaluationContextProvider evaluationContextProvider,
|
||||
QueryMethod queryMethod) {
|
||||
ConversionService conversionService,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider,
|
||||
QueryMethod queryMethod) {
|
||||
|
||||
Assert.notNull(parameterAccessor, "parameterAccessor must not be null");
|
||||
Assert.notNull(conversionService, "conversionService must not be null");
|
||||
|
||||
+4
-3
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.repository.query;
|
||||
|
||||
import static org.springframework.data.repository.util.ClassUtils.*;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
@@ -34,7 +36,6 @@ import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.util.ReactiveWrapperConverters;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.data.util.ReactiveWrappers;
|
||||
import org.springframework.data.util.ReflectionUtils;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -54,7 +55,7 @@ public class ReactiveElasticsearchQueryMethod extends ElasticsearchQueryMethod {
|
||||
|
||||
super(method, metadata, factory, mappingContext);
|
||||
|
||||
if (ReflectionUtils.hasParameterOfType(method, Pageable.class)) {
|
||||
if (hasParameterOfType(method, Pageable.class)) {
|
||||
|
||||
TypeInformation<?> returnType = TypeInformation.fromReturnTypeOf(method);
|
||||
boolean multiWrapper = ReactiveWrappers.isMultiValueType(returnType.getType());
|
||||
@@ -74,7 +75,7 @@ public class ReactiveElasticsearchQueryMethod extends ElasticsearchQueryMethod {
|
||||
method));
|
||||
}
|
||||
|
||||
if (ReflectionUtils.hasParameterOfType(method, Sort.class)) {
|
||||
if (hasParameterOfType(method, Sort.class)) {
|
||||
throw new IllegalStateException(String.format("Method must not have Pageable *and* Sort parameter. "
|
||||
+ "Use sorting capabilities on Pageable instead! Offending method: %s", method));
|
||||
}
|
||||
|
||||
+50
-8
@@ -15,26 +15,68 @@
|
||||
*/
|
||||
package org.springframework.data.elasticsearch.repository.query;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.StringQuery;
|
||||
import org.springframework.data.elasticsearch.repository.support.QueryStringProcessor;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Taylor Ono
|
||||
* @author Haibo Liu
|
||||
* @since 3.2
|
||||
* @deprecated since 5.5, use {@link ReactiveRepositoryStringQuery}
|
||||
*/
|
||||
@Deprecated(since = "5.5", forRemoval = true)
|
||||
public class ReactiveElasticsearchStringQuery extends ReactiveRepositoryStringQuery {
|
||||
public class ReactiveElasticsearchStringQuery extends AbstractReactiveElasticsearchRepositoryQuery {
|
||||
|
||||
private final String query;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
|
||||
public ReactiveElasticsearchStringQuery(ReactiveElasticsearchQueryMethod queryMethod,
|
||||
ReactiveElasticsearchOperations operations, ValueExpressionDelegate valueExpressionDelegate) {
|
||||
super(queryMethod, operations, valueExpressionDelegate);
|
||||
ReactiveElasticsearchOperations operations, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
this(queryMethod.getAnnotatedQuery(), queryMethod, operations, evaluationContextProvider);
|
||||
}
|
||||
|
||||
public ReactiveElasticsearchStringQuery(String query, ReactiveElasticsearchQueryMethod queryMethod,
|
||||
ReactiveElasticsearchOperations operations, ValueExpressionDelegate valueExpressionDelegate) {
|
||||
super(query, queryMethod, operations, valueExpressionDelegate);
|
||||
ReactiveElasticsearchOperations operations, QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
super(queryMethod, operations, evaluationContextProvider);
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
Assert.notNull(evaluationContextProvider, "evaluationContextProvider must not be null");
|
||||
|
||||
this.query = query;
|
||||
this.evaluationContextProvider = evaluationContextProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseQuery createQuery(ElasticsearchParametersParameterAccessor parameterAccessor) {
|
||||
ConversionService conversionService = getElasticsearchOperations().getElasticsearchConverter()
|
||||
.getConversionService();
|
||||
String processed = new QueryStringProcessor(query, queryMethod, conversionService, evaluationContextProvider)
|
||||
.createQuery(parameterAccessor);
|
||||
return new StringQuery(processed);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isCountQuery() {
|
||||
return queryMethod.hasCountQueryAnnotation();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isDeleteQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isExistsQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isLimiting() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -19,8 +19,8 @@ import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperatio
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
|
||||
import org.springframework.data.elasticsearch.repository.query.parser.ElasticsearchQueryCreator;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ResultProcessor;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
/**
|
||||
@@ -35,9 +35,8 @@ public class ReactivePartTreeElasticsearchQuery extends AbstractReactiveElastics
|
||||
|
||||
public ReactivePartTreeElasticsearchQuery(ReactiveElasticsearchQueryMethod queryMethod,
|
||||
ReactiveElasticsearchOperations elasticsearchOperations,
|
||||
ValueExpressionDelegate valueExpressionDelegate) {
|
||||
super(queryMethod, elasticsearchOperations,
|
||||
valueExpressionDelegate.createValueContextProvider(queryMethod.getParameters()));
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
super(queryMethod, elasticsearchOperations, evaluationContextProvider);
|
||||
|
||||
ResultProcessor processor = queryMethod.getResultProcessor();
|
||||
this.tree = new PartTree(queryMethod.getName(), processor.getReturnedType().getDomainType());
|
||||
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* 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.repository.query;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.SearchTemplateQuery;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A reactive repository query that uses a search template already stored in Elasticsearch.
|
||||
*
|
||||
* @author P.J. Meisch (pj.meisch@sothawo.com)
|
||||
* @since 5.5
|
||||
*/
|
||||
public class ReactiveRepositorySearchTemplateQuery extends AbstractReactiveElasticsearchRepositoryQuery {
|
||||
|
||||
private String id;
|
||||
private Map<String, Object> params;
|
||||
|
||||
public ReactiveRepositorySearchTemplateQuery(ReactiveElasticsearchQueryMethod queryMethod,
|
||||
ReactiveElasticsearchOperations elasticsearchOperations,
|
||||
ValueExpressionDelegate valueExpressionDelegate,
|
||||
String id) {
|
||||
super(queryMethod, elasticsearchOperations,
|
||||
valueExpressionDelegate.createValueContextProvider(queryMethod.getParameters()));
|
||||
Assert.hasLength(id, "id must not be null or empty");
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Map<String, Object> getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCountQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDeleteQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isExistsQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isLimiting() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseQuery createQuery(ElasticsearchParametersParameterAccessor parameterAccessor) {
|
||||
|
||||
var searchTemplateParameters = new LinkedHashMap<String, Object>();
|
||||
var values = parameterAccessor.getValues();
|
||||
|
||||
parameterAccessor.getParameters().forEach(parameter -> {
|
||||
if (!parameter.isSpecialParameter() && parameter.getName().isPresent() && parameter.getIndex() <= values.length) {
|
||||
searchTemplateParameters.put(parameter.getName().get(), values[parameter.getIndex()]);
|
||||
}
|
||||
});
|
||||
|
||||
return SearchTemplateQuery.builder()
|
||||
.withId(id)
|
||||
.withParams(searchTemplateParameters)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-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.repository.query;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.StringQuery;
|
||||
import org.springframework.data.elasticsearch.repository.support.QueryStringProcessor;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Was originally named ReactiveElasticsearchStringQuery.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Taylor Ono
|
||||
* @author Haibo Liu
|
||||
* @since 3.2
|
||||
*/
|
||||
public class ReactiveRepositoryStringQuery extends AbstractReactiveElasticsearchRepositoryQuery {
|
||||
|
||||
private final String query;
|
||||
|
||||
public ReactiveRepositoryStringQuery(ReactiveElasticsearchQueryMethod queryMethod,
|
||||
ReactiveElasticsearchOperations operations, ValueExpressionDelegate valueExpressionDelegate) {
|
||||
|
||||
this(queryMethod.getAnnotatedQuery(), queryMethod, operations, valueExpressionDelegate);
|
||||
}
|
||||
|
||||
public ReactiveRepositoryStringQuery(String query, ReactiveElasticsearchQueryMethod queryMethod,
|
||||
ReactiveElasticsearchOperations operations, ValueExpressionDelegate valueExpressionDelegate) {
|
||||
super(queryMethod, operations, valueExpressionDelegate.createValueContextProvider(queryMethod.getParameters()));
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseQuery createQuery(ElasticsearchParametersParameterAccessor parameterAccessor) {
|
||||
ConversionService conversionService = getElasticsearchOperations().getElasticsearchConverter()
|
||||
.getConversionService();
|
||||
String processed = new QueryStringProcessor(query, queryMethod, conversionService, evaluationContextProvider)
|
||||
.createQuery(parameterAccessor);
|
||||
return new StringQuery(processed);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isCountQuery() {
|
||||
return queryMethod.hasCountQueryAnnotation();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isDeleteQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isExistsQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isLimiting() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-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.repository.query;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.repository.query.parser.ElasticsearchQueryCreator;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
/**
|
||||
* A repository query that is built from the the method name in the repository definition. Was originally named
|
||||
* ElasticsearchPartQuery.
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Kevin Leturc
|
||||
* @author Mark Paluch
|
||||
* @author Rasmus Faber-Espensen
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Haibo Liu
|
||||
*/
|
||||
public class RepositoryPartQuery extends AbstractElasticsearchRepositoryQuery {
|
||||
|
||||
private final PartTree tree;
|
||||
private final MappingContext<?, ElasticsearchPersistentProperty> mappingContext;
|
||||
|
||||
public RepositoryPartQuery(ElasticsearchQueryMethod method, ElasticsearchOperations elasticsearchOperations,
|
||||
ValueExpressionDelegate valueExpressionDelegate) {
|
||||
super(method, elasticsearchOperations,
|
||||
valueExpressionDelegate.createValueContextProvider(method.getParameters()));
|
||||
this.tree = new PartTree(queryMethod.getName(), queryMethod.getResultProcessor().getReturnedType().getDomainType());
|
||||
this.mappingContext = elasticsearchConverter.getMappingContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCountQuery() {
|
||||
return tree.isCountProjection();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDeleteQuery() {
|
||||
return tree.isDelete();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isExistsQuery() {
|
||||
return tree.isExistsProjection();
|
||||
}
|
||||
|
||||
protected BaseQuery createQuery(ElasticsearchParametersParameterAccessor accessor) {
|
||||
|
||||
BaseQuery query = new ElasticsearchQueryCreator(tree, accessor, mappingContext).createQuery();
|
||||
|
||||
if (tree.getMaxResults() != null) {
|
||||
query.setMaxResults(tree.getMaxResults());
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
}
|
||||
-87
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* 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.repository.query;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.SearchTemplateQuery;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A repository query that uses a search template already stored in Elasticsearch.
|
||||
*
|
||||
* @author P.J. Meisch (pj.meisch@sothawo.com)
|
||||
* @since 5.5
|
||||
*/
|
||||
public class RepositorySearchTemplateQuery extends AbstractElasticsearchRepositoryQuery {
|
||||
|
||||
private String id;
|
||||
private Map<String, Object> params;
|
||||
|
||||
public RepositorySearchTemplateQuery(ElasticsearchQueryMethod queryMethod,
|
||||
ElasticsearchOperations elasticsearchOperations, ValueExpressionDelegate valueExpressionDelegate,
|
||||
String id) {
|
||||
super(queryMethod, elasticsearchOperations,
|
||||
valueExpressionDelegate.createValueContextProvider(queryMethod.getParameters()));
|
||||
Assert.hasLength(id, "id must not be null or empty");
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Map<String, Object> getParams() {
|
||||
return params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCountQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDeleteQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isExistsQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseQuery createQuery(ElasticsearchParametersParameterAccessor parameterAccessor) {
|
||||
|
||||
var searchTemplateParameters = new LinkedHashMap<String, Object>();
|
||||
var values = parameterAccessor.getValues();
|
||||
|
||||
parameterAccessor.getParameters().forEach(parameter -> {
|
||||
if (!parameter.isSpecialParameter() && parameter.getName().isPresent() && parameter.getIndex() <= values.length) {
|
||||
searchTemplateParameters.put(parameter.getName().get(), values[parameter.getIndex()]);
|
||||
}
|
||||
});
|
||||
|
||||
return SearchTemplateQuery.builder()
|
||||
.withId(id)
|
||||
.withParams(searchTemplateParameters)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
package org.springframework.data.elasticsearch.repository.query;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.query.BaseQuery;
|
||||
import org.springframework.data.elasticsearch.core.query.StringQuery;
|
||||
import org.springframework.data.elasticsearch.repository.support.QueryStringProcessor;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A repository query that is defined by a String containing the query. Was originally named ElasticsearchStringQuery.
|
||||
*
|
||||
* @author Rizwan Idrees
|
||||
* @author Mohsin Husen
|
||||
* @author Mark Paluch
|
||||
* @author Taylor Ono
|
||||
* @author Peter-Josef Meisch
|
||||
* @author Haibo Liu
|
||||
*/
|
||||
public class RepositoryStringQuery extends AbstractElasticsearchRepositoryQuery {
|
||||
private final String queryString;
|
||||
|
||||
public RepositoryStringQuery(ElasticsearchQueryMethod queryMethod, ElasticsearchOperations elasticsearchOperations,
|
||||
String queryString, ValueExpressionDelegate valueExpressionDelegate) {
|
||||
super(queryMethod, elasticsearchOperations,
|
||||
valueExpressionDelegate.createValueContextProvider(queryMethod.getParameters()));
|
||||
|
||||
Assert.notNull(queryString, "Query cannot be empty");
|
||||
|
||||
this.queryString = queryString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCountQuery() {
|
||||
return queryMethod.hasCountQueryAnnotation();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDeleteQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isExistsQuery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected BaseQuery createQuery(ElasticsearchParametersParameterAccessor parameterAccessor) {
|
||||
ConversionService conversionService = elasticsearchOperations.getElasticsearchConverter().getConversionService();
|
||||
var processed = new QueryStringProcessor(queryString, queryMethod, conversionService, evaluationContextProvider)
|
||||
.createQuery(parameterAccessor);
|
||||
|
||||
return new StringQuery(processed)
|
||||
.addSort(parameterAccessor.getSort());
|
||||
}
|
||||
}
|
||||
+13
-18
@@ -22,10 +22,9 @@ import java.util.Optional;
|
||||
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
import org.springframework.data.elasticsearch.repository.query.ElasticsearchPartQuery;
|
||||
import org.springframework.data.elasticsearch.repository.query.ElasticsearchQueryMethod;
|
||||
import org.springframework.data.elasticsearch.repository.query.RepositoryPartQuery;
|
||||
import org.springframework.data.elasticsearch.repository.query.RepositorySearchTemplateQuery;
|
||||
import org.springframework.data.elasticsearch.repository.query.RepositoryStringQuery;
|
||||
import org.springframework.data.elasticsearch.repository.query.ElasticsearchStringQuery;
|
||||
import org.springframework.data.elasticsearch.repository.support.querybyexample.QueryByExampleElasticsearchExecutor;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
|
||||
@@ -38,8 +37,8 @@ import org.springframework.data.repository.core.support.RepositoryFragment;
|
||||
import org.springframework.data.repository.query.QueryByExampleExecutor;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -97,16 +96,16 @@ public class ElasticsearchRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
@Override
|
||||
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key,
|
||||
ValueExpressionDelegate valueExpressionDelegate) {
|
||||
return Optional.of(new ElasticsearchQueryLookupStrategy(valueExpressionDelegate));
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
return Optional.of(new ElasticsearchQueryLookupStrategy(evaluationContextProvider));
|
||||
}
|
||||
|
||||
private class ElasticsearchQueryLookupStrategy implements QueryLookupStrategy {
|
||||
|
||||
private final ValueExpressionDelegate valueExpressionDelegate;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
|
||||
ElasticsearchQueryLookupStrategy(ValueExpressionDelegate valueExpressionDelegate) {
|
||||
this.valueExpressionDelegate = valueExpressionDelegate;
|
||||
ElasticsearchQueryLookupStrategy(QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
this.evaluationContextProvider = evaluationContextProvider;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -123,17 +122,13 @@ public class ElasticsearchRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
if (namedQueries.hasQuery(namedQueryName)) {
|
||||
String namedQuery = namedQueries.getQuery(namedQueryName);
|
||||
return new RepositoryStringQuery(queryMethod, elasticsearchOperations, namedQuery,
|
||||
valueExpressionDelegate);
|
||||
return new ElasticsearchStringQuery(queryMethod, elasticsearchOperations, namedQuery,
|
||||
evaluationContextProvider);
|
||||
} else if (queryMethod.hasAnnotatedQuery()) {
|
||||
return new RepositoryStringQuery(queryMethod, elasticsearchOperations, queryMethod.getAnnotatedQuery(),
|
||||
valueExpressionDelegate);
|
||||
} else if (queryMethod.hasAnnotatedSearchTemplateQuery()) {
|
||||
var searchTemplateQuery = queryMethod.getAnnotatedSearchTemplateQuery();
|
||||
return new RepositorySearchTemplateQuery(queryMethod, elasticsearchOperations, valueExpressionDelegate,
|
||||
searchTemplateQuery.id());
|
||||
return new ElasticsearchStringQuery(queryMethod, elasticsearchOperations, queryMethod.getAnnotatedQuery(),
|
||||
evaluationContextProvider);
|
||||
}
|
||||
return new RepositoryPartQuery(queryMethod, elasticsearchOperations, valueExpressionDelegate);
|
||||
return new ElasticsearchPartQuery(queryMethod, elasticsearchOperations, evaluationContextProvider);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-3
@@ -18,7 +18,6 @@ package org.springframework.data.elasticsearch.repository.support;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.data.elasticsearch.repository.query.ElasticsearchParametersParameterAccessor;
|
||||
import org.springframework.data.elasticsearch.repository.support.spel.QueryStringSpELEvaluator;
|
||||
import org.springframework.data.expression.ValueEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -35,10 +34,10 @@ public class QueryStringProcessor {
|
||||
private final String query;
|
||||
private final QueryMethod queryMethod;
|
||||
private final ConversionService conversionService;
|
||||
private final ValueEvaluationContextProvider evaluationContextProvider;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
|
||||
public QueryStringProcessor(String query, QueryMethod queryMethod, ConversionService conversionService,
|
||||
ValueEvaluationContextProvider evaluationContextProvider) {
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
|
||||
Assert.notNull(query, "query must not be null");
|
||||
Assert.notNull(queryMethod, "queryMethod must not be null");
|
||||
|
||||
+16
-17
@@ -23,9 +23,8 @@ import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperatio
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentEntity;
|
||||
import org.springframework.data.elasticsearch.core.mapping.ElasticsearchPersistentProperty;
|
||||
import org.springframework.data.elasticsearch.repository.query.ReactiveElasticsearchQueryMethod;
|
||||
import org.springframework.data.elasticsearch.repository.query.ReactiveElasticsearchStringQuery;
|
||||
import org.springframework.data.elasticsearch.repository.query.ReactivePartTreeElasticsearchQuery;
|
||||
import org.springframework.data.elasticsearch.repository.query.ReactiveRepositorySearchTemplateQuery;
|
||||
import org.springframework.data.elasticsearch.repository.query.ReactiveRepositoryStringQuery;
|
||||
import org.springframework.data.elasticsearch.repository.support.querybyexample.ReactiveQueryByExampleElasticsearchExecutor;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
@@ -37,9 +36,9 @@ import org.springframework.data.repository.core.support.RepositoryComposition;
|
||||
import org.springframework.data.repository.core.support.RepositoryFragment;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.ReactiveQueryByExampleExecutor;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -93,10 +92,14 @@ public class ReactiveElasticsearchRepositoryFactory extends ReactiveRepositoryFa
|
||||
return getTargetRepositoryViaReflection(information, entityInformation, operations);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key, org.springframework.data.repository.query.EvaluationContextProvider)
|
||||
*/
|
||||
@Override
|
||||
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable Key key,
|
||||
ValueExpressionDelegate valueExpressionDelegate) {
|
||||
return Optional.of(new ElasticsearchQueryLookupStrategy(operations, valueExpressionDelegate, mappingContext));
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider) {
|
||||
return Optional.of(new ElasticsearchQueryLookupStrategy(operations, evaluationContextProvider, mappingContext));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -127,19 +130,19 @@ public class ReactiveElasticsearchRepositoryFactory extends ReactiveRepositoryFa
|
||||
private static class ElasticsearchQueryLookupStrategy implements QueryLookupStrategy {
|
||||
|
||||
private final ReactiveElasticsearchOperations operations;
|
||||
private final ValueExpressionDelegate valueExpressionDelegate;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext;
|
||||
|
||||
public ElasticsearchQueryLookupStrategy(ReactiveElasticsearchOperations operations,
|
||||
ValueExpressionDelegate valueExpressionDelegate,
|
||||
QueryMethodEvaluationContextProvider evaluationContextProvider,
|
||||
MappingContext<? extends ElasticsearchPersistentEntity<?>, ElasticsearchPersistentProperty> mappingContext) {
|
||||
|
||||
Assert.notNull(operations, "operations must not be null");
|
||||
Assert.notNull(valueExpressionDelegate, "evaluationContextProvider must not be null");
|
||||
Assert.notNull(evaluationContextProvider, "evaluationContextProvider must not be null");
|
||||
Assert.notNull(mappingContext, "mappingContext must not be null");
|
||||
|
||||
this.operations = operations;
|
||||
this.valueExpressionDelegate = valueExpressionDelegate;
|
||||
this.evaluationContextProvider = evaluationContextProvider;
|
||||
this.mappingContext = mappingContext;
|
||||
}
|
||||
|
||||
@@ -158,16 +161,12 @@ public class ReactiveElasticsearchRepositoryFactory extends ReactiveRepositoryFa
|
||||
if (namedQueries.hasQuery(namedQueryName)) {
|
||||
String namedQuery = namedQueries.getQuery(namedQueryName);
|
||||
|
||||
return new ReactiveRepositoryStringQuery(namedQuery, queryMethod, operations,
|
||||
valueExpressionDelegate);
|
||||
return new ReactiveElasticsearchStringQuery(namedQuery, queryMethod, operations,
|
||||
evaluationContextProvider);
|
||||
} else if (queryMethod.hasAnnotatedQuery()) {
|
||||
return new ReactiveRepositoryStringQuery(queryMethod, operations, valueExpressionDelegate);
|
||||
} else if (queryMethod.hasAnnotatedSearchTemplateQuery()) {
|
||||
var searchTemplateQuery = queryMethod.getAnnotatedSearchTemplateQuery();
|
||||
return new ReactiveRepositorySearchTemplateQuery(queryMethod, operations, valueExpressionDelegate,
|
||||
searchTemplateQuery.id());
|
||||
return new ReactiveElasticsearchStringQuery(queryMethod, operations, evaluationContextProvider);
|
||||
} else {
|
||||
return new ReactivePartTreeElasticsearchQuery(queryMethod, operations, valueExpressionDelegate);
|
||||
return new ReactivePartTreeElasticsearchQuery(queryMethod, operations, evaluationContextProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -384,7 +384,7 @@ public class SimpleElasticsearchRepository<T, ID> implements ElasticsearchReposi
|
||||
public void deleteAll() {
|
||||
|
||||
executeAndRefresh((OperationsCallback<Void>) operations -> {
|
||||
operations.delete(DeleteQuery.builder(Query.findAll()).build(), entityClass, getIndexCoordinates());
|
||||
operations.delete(Query.findAll(), entityClass, getIndexCoordinates());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
+5
-5
@@ -24,8 +24,8 @@ import org.springframework.data.elasticsearch.repository.query.ElasticsearchPara
|
||||
import org.springframework.data.elasticsearch.repository.support.value.ElasticsearchCollectionValueToStringConverter;
|
||||
import org.springframework.data.elasticsearch.repository.support.value.ElasticsearchQueryValueConversionService;
|
||||
import org.springframework.data.elasticsearch.repository.support.value.ElasticsearchStringValueToStringConverter;
|
||||
import org.springframework.data.expression.ValueEvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ParserContext;
|
||||
@@ -53,11 +53,11 @@ public class QueryStringSpELEvaluator {
|
||||
private final String queryString;
|
||||
private final ElasticsearchParametersParameterAccessor parameterAccessor;
|
||||
private final QueryMethod queryMethod;
|
||||
private final ValueEvaluationContextProvider evaluationContextProvider;
|
||||
private final QueryMethodEvaluationContextProvider evaluationContextProvider;
|
||||
private final TypeConverter elasticsearchSpELTypeConverter;
|
||||
|
||||
public QueryStringSpELEvaluator(String queryString, ElasticsearchParametersParameterAccessor parameterAccessor,
|
||||
QueryMethod queryMethod, ValueEvaluationContextProvider evaluationContextProvider,
|
||||
QueryMethod queryMethod, QueryMethodEvaluationContextProvider evaluationContextProvider,
|
||||
ConversionService conversionService) {
|
||||
|
||||
Assert.notNull(queryString, "queryString must not be null");
|
||||
@@ -83,8 +83,8 @@ public class QueryStringSpELEvaluator {
|
||||
Expression expr = getQueryExpression(queryString);
|
||||
|
||||
if (expr != null) {
|
||||
EvaluationContext context = evaluationContextProvider.getEvaluationContext(parameterAccessor.getValues())
|
||||
.getRequiredEvaluationContext();
|
||||
EvaluationContext context = evaluationContextProvider.getEvaluationContext(parameterAccessor.getParameters(),
|
||||
parameterAccessor.getValues());
|
||||
|
||||
if (context instanceof StandardEvaluationContext standardEvaluationContext) {
|
||||
standardEvaluationContext.setTypeConverter(elasticsearchSpELTypeConverter);
|
||||
|
||||
+2
-2
@@ -33,11 +33,11 @@ inline fun <reified T : Any> SearchOperations.searchOne(query: Query): SearchHit
|
||||
inline fun <reified T : Any> SearchOperations.searchOne(query: Query, index: IndexCoordinates): SearchHit<T>? =
|
||||
searchOne(query, T::class.java, index)
|
||||
|
||||
inline fun <reified T : Any> SearchOperations.multiSearch(queries: List<Query>): List<SearchHits<T>> =
|
||||
inline fun <reified T : Any> SearchOperations.multiSearch(queries: List<out Query>): List<SearchHits<T>> =
|
||||
multiSearch(queries, T::class.java)
|
||||
|
||||
inline fun <reified T : Any> SearchOperations.multiSearch(
|
||||
queries: List<Query>,
|
||||
queries: List<out Query>,
|
||||
index: IndexCoordinates
|
||||
): List<SearchHits<T>> =
|
||||
multiSearch(queries, T::class.java, index)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Spring Data Elasticsearch 5.5.3 (2025.0.3)
|
||||
Spring Data Elasticsearch 5.4.13 (2024.1.13)
|
||||
Copyright (c) [2013-2022] Pivotal Software, Inc.
|
||||
|
||||
This product is licensed to you under the Apache License, Version 2.0 (the "License").
|
||||
@@ -25,6 +25,12 @@ conditions of the subcomponent's license, as noted in the LICENSE file.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -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 {
|
||||
|
||||
@@ -77,7 +77,7 @@ public class DevTests {
|
||||
private static final SimpleElasticsearchMappingContext mappingContext = new SimpleElasticsearchMappingContext();
|
||||
private static final MappingElasticsearchConverter converter = new MappingElasticsearchConverter(mappingContext);
|
||||
|
||||
private final TransportOptions transportOptions = new RestClientOptions(RequestOptions.DEFAULT, false).toBuilder()
|
||||
private final TransportOptions transportOptions = new RestClientOptions(RequestOptions.DEFAULT).toBuilder()
|
||||
.addHeader("X-SpringDataElasticsearch-AlwaysThere", "true").setParameter("pretty", "true").build();
|
||||
|
||||
private final JsonpMapper jsonpMapper = new JacksonJsonpMapper();
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ 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.RepositoryPartQueryIntegrationTests;
|
||||
import org.springframework.data.elasticsearch.core.query.ElasticsearchPartQueryIntegrationTests;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration;
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplat
|
||||
* @author Peter-Josef Meisch
|
||||
* @since 4.4
|
||||
*/
|
||||
public class ElasticsearchPartQueryELCIntegrationTests extends RepositoryPartQueryIntegrationTests {
|
||||
public class ElasticsearchPartQueryELCIntegrationTests extends ElasticsearchPartQueryIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@Import({ ElasticsearchTemplateConfiguration.class })
|
||||
|
||||
-33
@@ -30,7 +30,6 @@ 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.core.mapping.IndexCoordinates;
|
||||
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
|
||||
import org.springframework.data.elasticsearch.core.query.FetchSourceFilterBuilder;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.core.query.SourceFilter;
|
||||
@@ -187,38 +186,6 @@ public abstract class SourceFilterIntegrationTests {
|
||||
assertThat(entity.getField3()).isNull();
|
||||
}
|
||||
|
||||
@Test // #3009
|
||||
@DisplayName("should not return any fields when source is set to false")
|
||||
void shouldNotReturnAnyFieldsWhenSourceIsSetToFalse() {
|
||||
|
||||
Query query = Query.findAll();
|
||||
query.addSourceFilter(FetchSourceFilter.of(b -> b.withFetchSource(false)));
|
||||
|
||||
SearchHits<Entity> entities = operations.search(query, Entity.class);
|
||||
|
||||
assertThat(entities).hasSize(1);
|
||||
Entity entity = entities.getSearchHit(0).getContent();
|
||||
assertThat(entity.getField1()).isNull();
|
||||
assertThat(entity.getField2()).isNull();
|
||||
assertThat(entity.getField3()).isNull();
|
||||
}
|
||||
|
||||
@Test // #3009
|
||||
@DisplayName("should return all fields when source is set to true")
|
||||
void shouldReturnAllFieldsWhenSourceIsSetToTrue() {
|
||||
|
||||
Query query = Query.findAll();
|
||||
query.addSourceFilter(FetchSourceFilter.of(b -> b.withFetchSource(true)));
|
||||
|
||||
SearchHits<Entity> entities = operations.search(query, Entity.class);
|
||||
|
||||
assertThat(entities).hasSize(1);
|
||||
Entity entity = entities.getSearchHit(0).getContent();
|
||||
assertThat(entity.getField1()).isNotNull();
|
||||
assertThat(entity.getField2()).isNotNull();
|
||||
assertThat(entity.getField3()).isNotNull();
|
||||
}
|
||||
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
public static class Entity {
|
||||
@Nullable
|
||||
|
||||
@@ -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());
|
||||
});
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -34,15 +34,15 @@ import org.springframework.data.elasticsearch.annotations.FieldType;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
import org.springframework.data.elasticsearch.repository.query.ElasticsearchPartQuery;
|
||||
import org.springframework.data.elasticsearch.repository.query.ElasticsearchQueryMethod;
|
||||
import org.springframework.data.elasticsearch.repository.query.RepositoryPartQuery;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Tests for {@link RepositoryPartQuery}. The tests make sure that queries are built according to the method naming.
|
||||
* Tests for {@link ElasticsearchPartQuery}. The tests make sure that queries are built according to the method naming.
|
||||
* Classes implementing this abstract class are in the packages of their request factories and converters as these are
|
||||
* kept package private.
|
||||
*
|
||||
@@ -51,7 +51,7 @@ import org.springframework.lang.Nullable;
|
||||
*/
|
||||
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
|
||||
@SpringIntegrationTest
|
||||
public abstract class RepositoryPartQueryIntegrationTests {
|
||||
public abstract class ElasticsearchPartQueryIntegrationTests {
|
||||
|
||||
public static final String BOOK_TITLE = "Title";
|
||||
public static final int BOOK_PRICE = 42;
|
||||
@@ -727,8 +727,8 @@ public abstract class RepositoryPartQueryIntegrationTests {
|
||||
ElasticsearchQueryMethod queryMethod = new ElasticsearchQueryMethod(method,
|
||||
new DefaultRepositoryMetadata(SampleRepository.class), new SpelAwareProxyProjectionFactory(),
|
||||
operations.getElasticsearchConverter().getMappingContext());
|
||||
RepositoryPartQuery partQuery = new RepositoryPartQuery(queryMethod, operations,
|
||||
ValueExpressionDelegate.create());
|
||||
ElasticsearchPartQuery partQuery = new ElasticsearchPartQuery(queryMethod, operations,
|
||||
QueryMethodEvaluationContextProvider.DEFAULT);
|
||||
Query query = partQuery.createQuery(parameters);
|
||||
return buildQueryString(query, Book.class);
|
||||
}
|
||||
+29
-129
@@ -99,8 +99,6 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
||||
@DisplayName("should use runtime-field without script")
|
||||
void shouldUseRuntimeFieldWithoutScript() {
|
||||
|
||||
// a runtime field without a script can be used to redefine the type of a field for the search,
|
||||
// here we change the type from text to double
|
||||
insert("1", "11", 10);
|
||||
Query query = new CriteriaQuery(new Criteria("description").matches(11.0));
|
||||
RuntimeField runtimeField = new RuntimeField("description", "double");
|
||||
@@ -135,25 +133,6 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
||||
assertThat(foundPerson.getBirthDate()).isEqualTo(birthDate);
|
||||
}
|
||||
|
||||
@Test // #3076
|
||||
@DisplayName("should return scripted fields that are lists")
|
||||
void shouldReturnScriptedFieldsThatAreLists() {
|
||||
var person = new Person();
|
||||
person.setFirstName("John");
|
||||
person.setLastName("Doe");
|
||||
operations.save(person);
|
||||
var query = Query.findAll();
|
||||
query.addFields("allNames");
|
||||
query.addSourceFilter(new FetchSourceFilterBuilder().withIncludes("*").build());
|
||||
|
||||
var searchHits = operations.search(query, Person.class);
|
||||
|
||||
assertThat(searchHits.getTotalHits()).isEqualTo(1);
|
||||
var foundPerson = searchHits.getSearchHit(0).getContent();
|
||||
// the painless script seems to return the data sorted no matter in which order the values are emitted
|
||||
assertThat(foundPerson.getAllNames()).containsExactlyInAnyOrderElementsOf(List.of("John", "Doe"));
|
||||
}
|
||||
|
||||
@Test // #2035
|
||||
@DisplayName("should use repository method with ScriptedField parameters")
|
||||
void shouldUseRepositoryMethodWithScriptedFieldParameters() {
|
||||
@@ -164,11 +143,9 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
||||
|
||||
repository.save(entity);
|
||||
|
||||
org.springframework.data.elasticsearch.core.query.ScriptedField scriptedField1 = buildScriptedField(
|
||||
"scriptedValue1",
|
||||
org.springframework.data.elasticsearch.core.query.ScriptedField scriptedField1 = getScriptedField("scriptedValue1",
|
||||
2);
|
||||
org.springframework.data.elasticsearch.core.query.ScriptedField scriptedField2 = buildScriptedField(
|
||||
"scriptedValue2",
|
||||
org.springframework.data.elasticsearch.core.query.ScriptedField scriptedField2 = getScriptedField("scriptedValue2",
|
||||
3);
|
||||
|
||||
var searchHits = repository.findByValue(3, scriptedField1, scriptedField2);
|
||||
@@ -180,6 +157,17 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
||||
assertThat(foundEntity.getScriptedValue2()).isEqualTo(9);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static org.springframework.data.elasticsearch.core.query.ScriptedField getScriptedField(String fieldName,
|
||||
int factor) {
|
||||
return org.springframework.data.elasticsearch.core.query.ScriptedField.of(
|
||||
fieldName,
|
||||
ScriptData.of(b -> b
|
||||
.withType(ScriptType.INLINE)
|
||||
.withScript("doc['value'].size() > 0 ? doc['value'].value * params['factor'] : 0")
|
||||
.withParams(Map.of("factor", factor))));
|
||||
}
|
||||
|
||||
@Test // #2035
|
||||
@DisplayName("should use repository string query method with ScriptedField parameters")
|
||||
void shouldUseRepositoryStringQueryMethodWithScriptedFieldParameters() {
|
||||
@@ -190,11 +178,9 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
||||
|
||||
repository.save(entity);
|
||||
|
||||
org.springframework.data.elasticsearch.core.query.ScriptedField scriptedField1 = buildScriptedField(
|
||||
"scriptedValue1",
|
||||
org.springframework.data.elasticsearch.core.query.ScriptedField scriptedField1 = getScriptedField("scriptedValue1",
|
||||
2);
|
||||
org.springframework.data.elasticsearch.core.query.ScriptedField scriptedField2 = buildScriptedField(
|
||||
"scriptedValue2",
|
||||
org.springframework.data.elasticsearch.core.query.ScriptedField scriptedField2 = getScriptedField("scriptedValue2",
|
||||
3);
|
||||
|
||||
var searchHits = repository.findWithScriptedFields(3, scriptedField1, scriptedField2);
|
||||
@@ -216,8 +202,8 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
||||
|
||||
repository.save(entity);
|
||||
|
||||
var runtimeField1 = buildRuntimeField("scriptedValue1", 3);
|
||||
var runtimeField2 = buildRuntimeField("scriptedValue2", 4);
|
||||
var runtimeField1 = getRuntimeField("scriptedValue1", 3);
|
||||
var runtimeField2 = getRuntimeField("scriptedValue2", 4);
|
||||
|
||||
var searchHits = repository.findByValue(3, runtimeField1, runtimeField2);
|
||||
|
||||
@@ -228,6 +214,14 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
||||
assertThat(foundEntity.getScriptedValue2()).isEqualTo(12);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static RuntimeField getRuntimeField(String fieldName, int factor) {
|
||||
return new RuntimeField(
|
||||
fieldName,
|
||||
"long",
|
||||
String.format("emit(doc['value'].size() > 0 ? doc['value'].value * %d : 0)", factor));
|
||||
}
|
||||
|
||||
@Test // #2035
|
||||
@DisplayName("should use repository string query method with RuntimeField parameters")
|
||||
void shouldUseRepositoryStringQueryMethodWithRuntimeFieldParameters() {
|
||||
@@ -238,8 +232,8 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
||||
|
||||
repository.save(entity);
|
||||
|
||||
var runtimeField1 = buildRuntimeField("scriptedValue1", 3);
|
||||
var runtimeField2 = buildRuntimeField("scriptedValue2", 4);
|
||||
var runtimeField1 = getRuntimeField("scriptedValue1", 3);
|
||||
var runtimeField2 = getRuntimeField("scriptedValue2", 4);
|
||||
|
||||
var searchHits = repository.findWithRuntimeFields(3, runtimeField1, runtimeField2);
|
||||
|
||||
@@ -269,7 +263,8 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
||||
"priceWithTax",
|
||||
"double",
|
||||
"emit(doc['price'].value * params.tax)",
|
||||
Map.of("tax", 1.19));
|
||||
Map.of("tax", 1.19)
|
||||
);
|
||||
var query = CriteriaQuery.builder(
|
||||
Criteria.where("priceWithTax").greaterThan(100.0))
|
||||
.withRuntimeFields(List.of(runtimeField))
|
||||
@@ -280,56 +275,6 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
||||
assertThat(searchHits).hasSize(1);
|
||||
}
|
||||
|
||||
@Test // #3076
|
||||
@DisplayName("should use runtime fields in queries returning lists")
|
||||
void shouldUseRuntimeFieldsInQueriesReturningLists() {
|
||||
|
||||
insert("1", "item 1", 80.0);
|
||||
|
||||
var runtimeField = new RuntimeField(
|
||||
"someStrings",
|
||||
"keyword",
|
||||
"emit('foo'); emit('bar');",
|
||||
null);
|
||||
|
||||
var query = Query.findAll();
|
||||
query.addRuntimeField(runtimeField);
|
||||
query.addFields("someStrings");
|
||||
query.addSourceFilter(new FetchSourceFilterBuilder().withIncludes("*").build());
|
||||
|
||||
var searchHits = operations.search(query, SomethingToBuy.class);
|
||||
|
||||
assertThat(searchHits).hasSize(1);
|
||||
var somethingToBuy = searchHits.getSearchHit(0).getContent();
|
||||
assertThat(somethingToBuy.someStrings).containsExactlyInAnyOrder("foo", "bar");
|
||||
}
|
||||
|
||||
/**
|
||||
* build a {@link org.springframework.data.elasticsearch.core.query.ScriptedField} to return the product of the
|
||||
* document's value property and the given factor
|
||||
*/
|
||||
@NotNull
|
||||
private static org.springframework.data.elasticsearch.core.query.ScriptedField buildScriptedField(String fieldName,
|
||||
int factor) {
|
||||
return org.springframework.data.elasticsearch.core.query.ScriptedField.of(
|
||||
fieldName,
|
||||
ScriptData.of(b -> b
|
||||
.withType(ScriptType.INLINE)
|
||||
.withScript("doc['value'].size() > 0 ? doc['value'].value * params['factor'] : 0")
|
||||
.withParams(Map.of("factor", factor))));
|
||||
}
|
||||
|
||||
/**
|
||||
* build a {@link RuntimeField} to return the product of the document's value property and the given factor
|
||||
*/
|
||||
@NotNull
|
||||
private static RuntimeField buildRuntimeField(String fieldName, int factor) {
|
||||
return new RuntimeField(
|
||||
fieldName,
|
||||
"long",
|
||||
String.format("emit(doc['value'].size() > 0 ? doc['value'].value * %d : 0)", factor));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}-something-to-by")
|
||||
private static class SomethingToBuy {
|
||||
@@ -341,9 +286,6 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
||||
@Nullable
|
||||
@Field(type = FieldType.Double) private Double price;
|
||||
|
||||
@Nullable
|
||||
@ScriptedField private List<String> someStrings;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
@@ -370,15 +312,6 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
||||
public void setPrice(@Nullable Double price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<String> getSomeStrings() {
|
||||
return someStrings;
|
||||
}
|
||||
|
||||
public void setSomeStrings(@Nullable List<String> someStrings) {
|
||||
this.someStrings = someStrings;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -387,13 +320,6 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
||||
public static class Person {
|
||||
@Nullable private String id;
|
||||
|
||||
// need keywords as we are using them in the script
|
||||
@Nullable
|
||||
@Field(type = FieldType.Keyword) private String firstName;
|
||||
@Nullable
|
||||
@Field(type = FieldType.Keyword) private String lastName;
|
||||
@ScriptedField private List<String> allNames = List.of();
|
||||
|
||||
@Field(type = FieldType.Date, format = DateFormat.basic_date)
|
||||
@Nullable private LocalDate birthDate;
|
||||
|
||||
@@ -409,24 +335,6 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(@Nullable String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(@Nullable String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LocalDate getBirthDate() {
|
||||
return birthDate;
|
||||
@@ -444,14 +352,6 @@ public abstract class ScriptedAndRuntimeFieldsIntegrationTests {
|
||||
public void setAge(@Nullable Integer age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public List<String> getAllNames() {
|
||||
return allNames;
|
||||
}
|
||||
|
||||
public void setAllNames(List<String> allNames) {
|
||||
this.allNames = allNames;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2021-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.repository.query;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.convert.CustomConversions;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions;
|
||||
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public class ElasticsearchStringQueryUnitTestBase {
|
||||
|
||||
protected ElasticsearchConverter setupConverter() {
|
||||
MappingElasticsearchConverter converter = new MappingElasticsearchConverter(
|
||||
new SimpleElasticsearchMappingContext());
|
||||
Collection<Converter<?, ?>> converters = new ArrayList<>();
|
||||
converters.add(ElasticsearchStringQueryUnitTests.CarConverter.INSTANCE);
|
||||
CustomConversions customConversions = new ElasticsearchCustomConversions(converters);
|
||||
converter.setConversions(customConversions);
|
||||
converter.afterPropertiesSet();
|
||||
return converter;
|
||||
}
|
||||
|
||||
static class Car {
|
||||
@Nullable private String name;
|
||||
@Nullable private String model;
|
||||
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(@Nullable String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(@Nullable String model) {
|
||||
this.model = model;
|
||||
}
|
||||
}
|
||||
|
||||
enum CarConverter implements Converter<Car, String> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String convert(ElasticsearchStringQueryUnitTests.Car car) {
|
||||
return (car.getName() != null ? car.getName() : "null") + '-'
|
||||
+ (car.getModel() != null ? car.getModel() : "null");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+28
-56
@@ -16,7 +16,9 @@
|
||||
package org.springframework.data.elasticsearch.repository.query;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -24,26 +26,29 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import org.skyscreamer.jsonassert.JSONCompareMode;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.convert.CustomConversions;
|
||||
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.InnerField;
|
||||
import org.springframework.data.elasticsearch.annotations.MultiField;
|
||||
import org.springframework.data.elasticsearch.annotations.Query;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.SearchHits;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions;
|
||||
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.query.StringQuery;
|
||||
import org.springframework.data.elasticsearch.repositories.custommethod.QueryParameter;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
@@ -52,53 +57,14 @@ import org.springframework.lang.Nullable;
|
||||
* @author Niklas Herder
|
||||
* @author Haibo Liu
|
||||
*/
|
||||
public class RepositoryStringQueryUnitTests extends RepositoryStringQueryUnitTestsBase {
|
||||
/**
|
||||
* Adds some data class and custom conversion to the base class implementation.
|
||||
*/
|
||||
protected MappingElasticsearchConverter setupConverter() {
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class ElasticsearchStringQueryUnitTests extends ElasticsearchStringQueryUnitTestBase {
|
||||
|
||||
Collection<Converter<?, ?>> converters = new ArrayList<>();
|
||||
converters.add(RepositoryStringQueryUnitTests.CarConverter.INSTANCE);
|
||||
CustomConversions customConversions = new ElasticsearchCustomConversions(converters);
|
||||
@Mock ElasticsearchOperations operations;
|
||||
|
||||
MappingElasticsearchConverter converter = super.setupConverter();
|
||||
converter.setConversions(customConversions);
|
||||
converter.afterPropertiesSet();
|
||||
return converter;
|
||||
}
|
||||
|
||||
static class Car {
|
||||
@Nullable private String name;
|
||||
@Nullable private String model;
|
||||
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(@Nullable String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(@Nullable String model) {
|
||||
this.model = model;
|
||||
}
|
||||
}
|
||||
|
||||
enum CarConverter implements Converter<Car, String> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String convert(Car car) {
|
||||
return (car.getName() != null ? car.getName() : "null") + '-'
|
||||
+ (car.getModel() != null ? car.getModel() : "null");
|
||||
}
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
when(operations.getElasticsearchConverter()).thenReturn(setupConverter());
|
||||
}
|
||||
|
||||
@Test // DATAES-552
|
||||
@@ -384,9 +350,8 @@ public class RepositoryStringQueryUnitTests extends RepositoryStringQueryUnitTes
|
||||
throws NoSuchMethodException {
|
||||
|
||||
Class<?>[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
|
||||
ElasticsearchQueryMethod queryMethod = getQueryMethod(RepositoryStringQueryUnitTests.SampleRepository.class,
|
||||
methodName, argTypes);
|
||||
RepositoryStringQuery elasticsearchStringQuery = queryForMethod(queryMethod);
|
||||
ElasticsearchQueryMethod queryMethod = getQueryMethod(methodName, argTypes);
|
||||
ElasticsearchStringQuery elasticsearchStringQuery = queryForMethod(queryMethod);
|
||||
return elasticsearchStringQuery.createQuery(new ElasticsearchParametersParameterAccessor(queryMethod, args));
|
||||
}
|
||||
|
||||
@@ -405,9 +370,16 @@ public class RepositoryStringQueryUnitTests extends RepositoryStringQueryUnitTes
|
||||
.isEqualTo("{ 'bool' : { 'must' : { 'term' : { 'car' : 'Toyota-Prius' } } } }");
|
||||
}
|
||||
|
||||
private RepositoryStringQuery queryForMethod(ElasticsearchQueryMethod queryMethod) {
|
||||
return new RepositoryStringQuery(queryMethod, operations, queryMethod.getAnnotatedQuery(),
|
||||
ValueExpressionDelegate.create());
|
||||
private ElasticsearchStringQuery queryForMethod(ElasticsearchQueryMethod queryMethod) {
|
||||
return new ElasticsearchStringQuery(queryMethod, operations, queryMethod.getAnnotatedQuery(),
|
||||
QueryMethodEvaluationContextProvider.DEFAULT);
|
||||
}
|
||||
|
||||
private ElasticsearchQueryMethod getQueryMethod(String name, Class<?>... parameters) throws NoSuchMethodException {
|
||||
|
||||
Method method = SampleRepository.class.getMethod(name, parameters);
|
||||
return new ElasticsearchQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class),
|
||||
new SpelAwareProxyProjectionFactory(), operations.getElasticsearchConverter().getMappingContext());
|
||||
}
|
||||
|
||||
private interface SampleRepository extends Repository<Person, String> {
|
||||
+29
-60
@@ -16,11 +16,12 @@
|
||||
package org.springframework.data.elasticsearch.repository.query;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -28,27 +29,28 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.skyscreamer.jsonassert.JSONAssert;
|
||||
import org.skyscreamer.jsonassert.JSONCompareMode;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.convert.CustomConversions;
|
||||
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.InnerField;
|
||||
import org.springframework.data.elasticsearch.annotations.MultiField;
|
||||
import org.springframework.data.elasticsearch.annotations.Query;
|
||||
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.SearchHit;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions;
|
||||
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.query.StringQuery;
|
||||
import org.springframework.data.elasticsearch.repositories.custommethod.QueryParameter;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -58,54 +60,13 @@ import org.springframework.lang.Nullable;
|
||||
* @author Haibo Liu
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class ReactiveRepositoryStringQueryUnitTests extends ReactiveRepositoryQueryUnitTestsBase {
|
||||
public class ReactiveElasticsearchStringQueryUnitTests extends ElasticsearchStringQueryUnitTestBase {
|
||||
|
||||
/**
|
||||
* Adds some data class and custom conversion to the base class implementation.
|
||||
*/
|
||||
protected MappingElasticsearchConverter setupConverter() {
|
||||
@Mock ReactiveElasticsearchOperations operations;
|
||||
|
||||
Collection<Converter<?, ?>> converters = new ArrayList<>();
|
||||
converters.add(CarConverter.INSTANCE);
|
||||
CustomConversions customConversions = new ElasticsearchCustomConversions(converters);
|
||||
|
||||
MappingElasticsearchConverter converter = super.setupConverter();
|
||||
converter.setConversions(customConversions);
|
||||
converter.afterPropertiesSet();
|
||||
return converter;
|
||||
}
|
||||
|
||||
static class Car {
|
||||
@Nullable private String name;
|
||||
@Nullable private String model;
|
||||
|
||||
@Nullable
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(@Nullable String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(@Nullable String model) {
|
||||
this.model = model;
|
||||
}
|
||||
}
|
||||
|
||||
enum CarConverter implements Converter<Car, String> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String convert(Car car) {
|
||||
return (car.getName() != null ? car.getName() : "null") + '-'
|
||||
+ (car.getModel() != null ? car.getModel() : "null");
|
||||
}
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
when(operations.getElasticsearchConverter()).thenReturn(setupConverter());
|
||||
}
|
||||
|
||||
@Test // DATAES-519
|
||||
@@ -406,21 +367,29 @@ public class ReactiveRepositoryStringQueryUnitTests extends ReactiveRepositoryQu
|
||||
|
||||
Class<?>[] argTypes = Arrays.stream(args).map(Object::getClass)
|
||||
.map(clazz -> Collection.class.isAssignableFrom(clazz) ? List.class : clazz).toArray(Class[]::new);
|
||||
ReactiveElasticsearchQueryMethod queryMethod = getQueryMethod(SampleRepository.class, methodName, argTypes);
|
||||
ReactiveRepositoryStringQuery elasticsearchStringQuery = queryForMethod(queryMethod);
|
||||
ReactiveElasticsearchQueryMethod queryMethod = getQueryMethod(methodName, argTypes);
|
||||
ReactiveElasticsearchStringQuery elasticsearchStringQuery = queryForMethod(queryMethod);
|
||||
|
||||
return elasticsearchStringQuery.createQuery(new ElasticsearchParametersParameterAccessor(queryMethod, args));
|
||||
}
|
||||
|
||||
private ReactiveRepositoryStringQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception {
|
||||
|
||||
ReactiveElasticsearchQueryMethod queryMethod = getQueryMethod(SampleRepository.class, name, parameters);
|
||||
return queryForMethod(queryMethod);
|
||||
private ReactiveElasticsearchStringQuery queryForMethod(ReactiveElasticsearchQueryMethod queryMethod) {
|
||||
return new ReactiveElasticsearchStringQuery(queryMethod, operations,
|
||||
QueryMethodEvaluationContextProvider.DEFAULT);
|
||||
}
|
||||
|
||||
private ReactiveRepositoryStringQuery queryForMethod(ReactiveElasticsearchQueryMethod queryMethod) {
|
||||
return new ReactiveRepositoryStringQuery(queryMethod, operations,
|
||||
ValueExpressionDelegate.create());
|
||||
private ReactiveElasticsearchQueryMethod getQueryMethod(String name, Class<?>... parameters)
|
||||
throws NoSuchMethodException {
|
||||
|
||||
Method method = SampleRepository.class.getMethod(name, parameters);
|
||||
return new ReactiveElasticsearchQueryMethod(method, new DefaultRepositoryMetadata(SampleRepository.class),
|
||||
new SpelAwareProxyProjectionFactory(), operations.getElasticsearchConverter().getMappingContext());
|
||||
}
|
||||
|
||||
private ReactiveElasticsearchStringQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception {
|
||||
|
||||
ReactiveElasticsearchQueryMethod queryMethod = getQueryMethod(name, parameters);
|
||||
return queryForMethod(queryMethod);
|
||||
}
|
||||
|
||||
private interface SampleRepository extends Repository<Person, String> {
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* 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.repository.query;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.elasticsearch.core.ReactiveElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class ReactiveRepositoryQueryUnitTestsBase {
|
||||
|
||||
@Mock ReactiveElasticsearchOperations operations;
|
||||
|
||||
/**
|
||||
* set up the {operations} mock to return the {@link ElasticsearchConverter} from setupConverter().
|
||||
*/
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
when(operations.getElasticsearchConverter()).thenReturn(setupConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a simple {@link MappingElasticsearchConverter} with no special setup.
|
||||
*/
|
||||
protected MappingElasticsearchConverter setupConverter() {
|
||||
return new MappingElasticsearchConverter(
|
||||
new SimpleElasticsearchMappingContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ReactiveElasticsearchQueryMethod} for the given method
|
||||
*
|
||||
* @param repositoryClass
|
||||
* @param name
|
||||
* @param parameters
|
||||
* @return
|
||||
* @throws NoSuchMethodException
|
||||
*/
|
||||
|
||||
protected ReactiveElasticsearchQueryMethod getQueryMethod(Class<?> repositoryClass, String name,
|
||||
Class<?>... parameters)
|
||||
throws NoSuchMethodException {
|
||||
|
||||
Method method = repositoryClass.getMethod(name, parameters);
|
||||
return new ReactiveElasticsearchQueryMethod(method,
|
||||
new DefaultRepositoryMetadata(repositoryClass),
|
||||
new SpelAwareProxyProjectionFactory(), operations.getElasticsearchConverter().getMappingContext());
|
||||
}
|
||||
}
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* 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.repository.query;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.SearchTemplateQuery;
|
||||
import org.springframework.data.elasticsearch.core.SearchHits;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
public class ReactiveRepositorySearchTemplateQueryUnitTests extends ReactiveRepositoryQueryUnitTestsBase {
|
||||
|
||||
@Test // #2997
|
||||
@DisplayName("should set searchtemplate id")
|
||||
void shouldSetSearchTemplateId() throws NoSuchMethodException {
|
||||
|
||||
var query = createQuery("searchWithArgs", "answer", 42);
|
||||
|
||||
assertThat(query).isInstanceOf(org.springframework.data.elasticsearch.core.query.SearchTemplateQuery.class);
|
||||
var searchTemplateQuery = (org.springframework.data.elasticsearch.core.query.SearchTemplateQuery) query;
|
||||
|
||||
assertThat(searchTemplateQuery.getId()).isEqualTo("searchtemplate-42");
|
||||
}
|
||||
|
||||
@Test // #2997
|
||||
@DisplayName("should set searchtemplate parameters")
|
||||
void shouldSetSearchTemplateParameters() throws NoSuchMethodException {
|
||||
|
||||
var query = createQuery("searchWithArgs", "answer", 42);
|
||||
|
||||
assertThat(query).isInstanceOf(org.springframework.data.elasticsearch.core.query.SearchTemplateQuery.class);
|
||||
var searchTemplateQuery = (org.springframework.data.elasticsearch.core.query.SearchTemplateQuery) query;
|
||||
|
||||
var params = searchTemplateQuery.getParams();
|
||||
assertThat(params).isNotNull().hasSize(2);
|
||||
assertThat(params.get("stringArg")).isEqualTo("answer");
|
||||
assertThat(params.get("intArg")).isEqualTo(42);
|
||||
}
|
||||
|
||||
// region helper methods
|
||||
private Query createQuery(String methodName, Object... args) throws NoSuchMethodException {
|
||||
Class<?>[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
|
||||
ReactiveElasticsearchQueryMethod queryMethod = getQueryMethod(SampleRepository.class, methodName, argTypes);
|
||||
|
||||
ReactiveRepositorySearchTemplateQuery repositorySearchTemplateQuery = queryForMethod(queryMethod);
|
||||
|
||||
return repositorySearchTemplateQuery.createQuery(new ElasticsearchParametersParameterAccessor(queryMethod, args));
|
||||
}
|
||||
|
||||
private ReactiveRepositorySearchTemplateQuery queryForMethod(ReactiveElasticsearchQueryMethod queryMethod) {
|
||||
return new ReactiveRepositorySearchTemplateQuery(queryMethod, operations, ValueExpressionDelegate.create(),
|
||||
queryMethod.getAnnotatedSearchTemplateQuery().id());
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region test data
|
||||
private interface SampleRepository extends ElasticsearchRepository<SampleEntity, String> {
|
||||
@SearchTemplateQuery(id = "searchtemplate-42")
|
||||
SearchHits<SampleEntity> searchWithArgs(String stringArg, Integer intArg);
|
||||
|
||||
@SearchTemplateQuery(id = "searchtemplate-42")
|
||||
SearchHits<SampleEntity> searchWithArgsAndSort(String stringArg, Integer intArg, Sort sort);
|
||||
}
|
||||
|
||||
@Document(indexName = "not-relevant")
|
||||
static class SampleEntity {
|
||||
@Nullable
|
||||
@Id String id;
|
||||
@Nullable String data;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(@Nullable String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(@Nullable String data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* 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.repository.query;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.convert.MappingElasticsearchConverter;
|
||||
import org.springframework.data.elasticsearch.core.mapping.SimpleElasticsearchMappingContext;
|
||||
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
|
||||
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class RepositoryQueryUnitTestsBase {
|
||||
|
||||
@Mock ElasticsearchOperations operations;
|
||||
|
||||
/**
|
||||
* set up the {operations} mock to return the {@link ElasticsearchConverter} from setupConverter().
|
||||
*/
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
when(operations.getElasticsearchConverter()).thenReturn(setupConverter());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a simple {@link MappingElasticsearchConverter} with no special setup.
|
||||
*/
|
||||
protected MappingElasticsearchConverter setupConverter() {
|
||||
return new MappingElasticsearchConverter(
|
||||
new SimpleElasticsearchMappingContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ElasticsearchQueryMethod} for the given method
|
||||
*
|
||||
* @param repositoryClass
|
||||
* @param name
|
||||
* @param parameters
|
||||
* @return
|
||||
* @throws NoSuchMethodException
|
||||
*/
|
||||
protected ElasticsearchQueryMethod getQueryMethod(Class<?> repositoryClass, String name, Class<?>... parameters)
|
||||
throws NoSuchMethodException {
|
||||
|
||||
Method method = repositoryClass.getMethod(name, parameters);
|
||||
return new ElasticsearchQueryMethod(method, new DefaultRepositoryMetadata(repositoryClass),
|
||||
new SpelAwareProxyProjectionFactory(), operations.getElasticsearchConverter().getMappingContext());
|
||||
}
|
||||
|
||||
}
|
||||
-113
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* 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.repository.query;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.elasticsearch.annotations.Document;
|
||||
import org.springframework.data.elasticsearch.annotations.SearchTemplateQuery;
|
||||
import org.springframework.data.elasticsearch.core.SearchHits;
|
||||
import org.springframework.data.elasticsearch.core.query.Query;
|
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
public class RepositorySearchTemplateQueryUnitTests extends RepositoryQueryUnitTestsBase {
|
||||
|
||||
@Test // #2997
|
||||
@DisplayName("should set searchtemplate id")
|
||||
void shouldSetSearchTemplateId() throws NoSuchMethodException {
|
||||
|
||||
var query = createQuery("searchWithArgs", "answer", 42);
|
||||
|
||||
assertThat(query).isInstanceOf(org.springframework.data.elasticsearch.core.query.SearchTemplateQuery.class);
|
||||
var searchTemplateQuery = (org.springframework.data.elasticsearch.core.query.SearchTemplateQuery) query;
|
||||
|
||||
assertThat(searchTemplateQuery.getId()).isEqualTo("searchtemplate-42");
|
||||
}
|
||||
|
||||
@Test // #2997
|
||||
@DisplayName("should set searchtemplate parameters")
|
||||
void shouldSetSearchTemplateParameters() throws NoSuchMethodException {
|
||||
|
||||
var query = createQuery("searchWithArgs", "answer", 42);
|
||||
|
||||
assertThat(query).isInstanceOf(org.springframework.data.elasticsearch.core.query.SearchTemplateQuery.class);
|
||||
var searchTemplateQuery = (org.springframework.data.elasticsearch.core.query.SearchTemplateQuery) query;
|
||||
|
||||
var params = searchTemplateQuery.getParams();
|
||||
assertThat(params).isNotNull().hasSize(2);
|
||||
assertThat(params.get("stringArg")).isEqualTo("answer");
|
||||
assertThat(params.get("intArg")).isEqualTo(42);
|
||||
}
|
||||
|
||||
// region helper methods
|
||||
private Query createQuery(String methodName, Object... args) throws NoSuchMethodException {
|
||||
Class<?>[] argTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
|
||||
ElasticsearchQueryMethod queryMethod = getQueryMethod(SampleRepository.class, methodName, argTypes);
|
||||
|
||||
RepositorySearchTemplateQuery repositorySearchTemplateQuery = queryForMethod(queryMethod);
|
||||
|
||||
return repositorySearchTemplateQuery.createQuery(new ElasticsearchParametersParameterAccessor(queryMethod, args));
|
||||
}
|
||||
|
||||
private RepositorySearchTemplateQuery queryForMethod(ElasticsearchQueryMethod queryMethod) {
|
||||
return new RepositorySearchTemplateQuery(queryMethod, operations, ValueExpressionDelegate.create(),
|
||||
queryMethod.getAnnotatedSearchTemplateQuery().id());
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region test data
|
||||
private interface SampleRepository extends ElasticsearchRepository<SampleEntity, String> {
|
||||
@SearchTemplateQuery(id = "searchtemplate-42")
|
||||
SearchHits<SampleEntity> searchWithArgs(String stringArg, Integer intArg);
|
||||
|
||||
@SearchTemplateQuery(id = "searchtemplate-42")
|
||||
SearchHits<SampleEntity> searchWithArgsAndSort(String stringArg, Integer intArg, Sort sort);
|
||||
}
|
||||
|
||||
@Document(indexName = "not-relevant")
|
||||
static class SampleEntity {
|
||||
@Nullable
|
||||
@Id String id;
|
||||
@Nullable String data;
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(@Nullable String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(@Nullable String data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2021-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.repository.query;
|
||||
|
||||
/**
|
||||
* @author Peter-Josef Meisch
|
||||
*/
|
||||
public class RepositoryStringQueryUnitTestsBase extends RepositoryQueryUnitTestsBase {
|
||||
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* 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.repository.support;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* @since 5.5
|
||||
*/
|
||||
@ContextConfiguration(classes = ReactiveRepositoryQueryELCIntegrationTests.Config.class)
|
||||
public class ReactiveRepositoryQueryELCIntegrationTests
|
||||
extends ReactiveRepositoryQueryIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@Import({ ReactiveElasticsearchTemplateConfiguration.class })
|
||||
@EnableReactiveElasticsearchRepositories(considerNestedRepositories = true)
|
||||
static class Config {
|
||||
@Bean
|
||||
IndexNameProvider indexNameProvider() {
|
||||
return new IndexNameProvider("reactive-repository-query");
|
||||
}
|
||||
}
|
||||
}
|
||||
-159
@@ -1,159 +0,0 @@
|
||||
/*
|
||||
* 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.repository.support;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.springframework.data.elasticsearch.core.IndexOperationsAdapter.*;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
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.SearchTemplateQuery;
|
||||
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.core.script.Script;
|
||||
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;
|
||||
|
||||
/**
|
||||
* @since 5.5
|
||||
*/
|
||||
@SpringIntegrationTest
|
||||
abstract class ReactiveRepositoryQueryIntegrationTests {
|
||||
@Autowired private SampleElasticsearchRepository repository;
|
||||
@Autowired private ReactiveElasticsearchOperations operations;
|
||||
@Autowired private IndexNameProvider indexNameProvider;
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
indexNameProvider.increment();
|
||||
blocking(operations.indexOps(LOTRCharacter.class)).createWithMapping();
|
||||
}
|
||||
|
||||
@Test
|
||||
@org.junit.jupiter.api.Order(Integer.MAX_VALUE)
|
||||
public void cleanup() {
|
||||
blocking(operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*"))).delete();
|
||||
}
|
||||
|
||||
@Test // #2997
|
||||
@DisplayName("should use searchtemplate query")
|
||||
void shouldUseSearchtemplateQuery() {
|
||||
// store some data
|
||||
repository.saveAll(List.of(
|
||||
new LOTRCharacter("1", "Frodo is a hobbit"),
|
||||
new LOTRCharacter("2", "Legolas is an elf"),
|
||||
new LOTRCharacter("3", "Gandalf is a wizard"),
|
||||
new LOTRCharacter("4", "Bilbo is a hobbit"),
|
||||
new LOTRCharacter("5", "Gimli is a dwarf")))
|
||||
.blockLast();
|
||||
|
||||
// store a searchtemplate
|
||||
String searchInCharacter = """
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": [
|
||||
{
|
||||
"match": {
|
||||
"lotrCharacter": "{{word}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"from": 0,
|
||||
"size": 100,
|
||||
"sort": {
|
||||
"id": {
|
||||
"order": "desc"
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Script scriptSearchInCharacter = Script.builder() //
|
||||
.withId("searchInCharacter") //
|
||||
.withLanguage("mustache") //
|
||||
.withSource(searchInCharacter) //
|
||||
.build();
|
||||
|
||||
var success = operations.putScript(scriptSearchInCharacter).block();
|
||||
assertThat(success).isTrue();
|
||||
|
||||
// search with repository for hobbits order by id descending
|
||||
var searchHits = repository.searchInCharacter("hobbit")
|
||||
.collectList().block();
|
||||
|
||||
// check result (bilbo, frodo)
|
||||
assertThat(searchHits).isNotNull();
|
||||
assertThat(searchHits.size()).isEqualTo(2);
|
||||
assertThat(searchHits.get(0).getId()).isEqualTo("4");
|
||||
assertThat(searchHits.get(1).getId()).isEqualTo("1");
|
||||
}
|
||||
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class LOTRCharacter {
|
||||
@Nullable
|
||||
@Id
|
||||
@Field(fielddata = true) // needed for the sort to work
|
||||
private String id;
|
||||
|
||||
@Field(type = FieldType.Text)
|
||||
@Nullable private String lotrCharacter;
|
||||
|
||||
public LOTRCharacter(@Nullable String id, @Nullable String lotrCharacter) {
|
||||
this.id = id;
|
||||
this.lotrCharacter = lotrCharacter;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(@Nullable String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getLotrCharacter() {
|
||||
return lotrCharacter;
|
||||
}
|
||||
|
||||
public void setLotrCharacter(@Nullable String lotrCharacter) {
|
||||
this.lotrCharacter = lotrCharacter;
|
||||
}
|
||||
}
|
||||
|
||||
interface SampleElasticsearchRepository
|
||||
extends ReactiveElasticsearchRepository<LOTRCharacter, String> {
|
||||
@SearchTemplateQuery(id = "searchInCharacter")
|
||||
Flux<SearchHit<LOTRCharacter>> searchInCharacter(String word);
|
||||
}
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* 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.repository.support;
|
||||
|
||||
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;
|
||||
|
||||
@ContextConfiguration(classes = {RepositoryQueryELCIntegrationTests.Config.class })public class RepositoryQueryELCIntegrationTests extends RepositoryQueryIntegrationTests {
|
||||
@Configuration
|
||||
@Import({ElasticsearchTemplateConfiguration.class })
|
||||
@EnableElasticsearchRepositories(basePackages = {"org.springframework.data.elasticsearch.repository.support" },
|
||||
considerNestedRepositories = true)
|
||||
static class Config {
|
||||
@Bean
|
||||
IndexNameProvider indexNameProvider() {
|
||||
return new IndexNameProvider("repository-query");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-151
@@ -1,151 +0,0 @@
|
||||
/*
|
||||
* 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.repository.support;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
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.SearchTemplateQuery;
|
||||
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.script.Script;
|
||||
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;
|
||||
|
||||
@SpringIntegrationTest
|
||||
abstract class RepositoryQueryIntegrationTests {
|
||||
@Autowired private SampleElasticsearchRepository repository;
|
||||
@Autowired private ElasticsearchOperations operations;
|
||||
@Autowired private IndexNameProvider indexNameProvider;
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
indexNameProvider.increment();
|
||||
operations.indexOps(LOTRCharacter.class).createWithMapping();
|
||||
}
|
||||
|
||||
@Test
|
||||
@org.junit.jupiter.api.Order(Integer.MAX_VALUE)
|
||||
public void cleanup() {
|
||||
operations.indexOps(IndexCoordinates.of(indexNameProvider.getPrefix() + "*")).delete();
|
||||
}
|
||||
|
||||
@Test // #2997
|
||||
@DisplayName("should use searchtemplate query")
|
||||
void shouldUseSearchtemplateQuery() {
|
||||
// store some data
|
||||
repository.saveAll(List.of(
|
||||
new LOTRCharacter("1", "Frodo is a hobbit"),
|
||||
new LOTRCharacter("2", "Legolas is an elf"),
|
||||
new LOTRCharacter("3", "Gandalf is a wizard"),
|
||||
new LOTRCharacter("4", "Bilbo is a hobbit"),
|
||||
new LOTRCharacter("5", "Gimli is a dwarf")));
|
||||
|
||||
// store a searchtemplate
|
||||
String searchInCharacter = """
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": [
|
||||
{
|
||||
"match": {
|
||||
"lotrCharacter": "{{word}}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"from": 0,
|
||||
"size": 100,
|
||||
"sort": {
|
||||
"id": {
|
||||
"order": "desc"
|
||||
}
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
Script scriptSearchInCharacter = Script.builder() //
|
||||
.withId("searchInCharacter") //
|
||||
.withLanguage("mustache") //
|
||||
.withSource(searchInCharacter) //
|
||||
.build();
|
||||
|
||||
var success = operations.putScript(scriptSearchInCharacter);
|
||||
assertThat(success).isTrue();
|
||||
|
||||
// search with repository for hobbits order by id descending
|
||||
var searchHits = repository.searchInCharacter("hobbit");
|
||||
|
||||
// check result (bilbo, frodo)
|
||||
assertThat(searchHits).isNotNull();
|
||||
assertThat(searchHits.getTotalHits()).isEqualTo(2);
|
||||
assertThat(searchHits.getSearchHit(0).getId()).isEqualTo("4");
|
||||
assertThat(searchHits.getSearchHit(1).getId()).isEqualTo("1");
|
||||
}
|
||||
|
||||
@Document(indexName = "#{@indexNameProvider.indexName()}")
|
||||
static class LOTRCharacter {
|
||||
@Nullable
|
||||
@Id
|
||||
@Field(fielddata = true) // needed for the sort to work
|
||||
private String id;
|
||||
|
||||
@Field(type = FieldType.Text)
|
||||
@Nullable private String lotrCharacter;
|
||||
|
||||
public LOTRCharacter(@Nullable String id, @Nullable String lotrCharacter) {
|
||||
this.id = id;
|
||||
this.lotrCharacter = lotrCharacter;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(@Nullable String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getLotrCharacter() {
|
||||
return lotrCharacter;
|
||||
}
|
||||
|
||||
public void setLotrCharacter(@Nullable String lotrCharacter) {
|
||||
this.lotrCharacter = lotrCharacter;
|
||||
}
|
||||
}
|
||||
|
||||
interface SampleElasticsearchRepository
|
||||
extends ElasticsearchRepository<LOTRCharacter, String> {
|
||||
@SearchTemplateQuery(id = "searchInCharacter")
|
||||
SearchHits<LOTRCharacter> searchInCharacter(String word);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
api.version=1.44
|
||||
@@ -5,12 +5,5 @@
|
||||
"lang": "painless",
|
||||
"source": "Instant currentDate = Instant.ofEpochMilli(new Date().getTime()); Instant startDate = doc['birthDate'].value.toInstant(); emit(ChronoUnit.DAYS.between(startDate, currentDate) / 365);"
|
||||
}
|
||||
},
|
||||
"allNames": {
|
||||
"type": "keyword",
|
||||
"script": {
|
||||
"lang": "painless",
|
||||
"source": "emit(doc['firstName'].value);emit(doc['lastName'].value);"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#
|
||||
#
|
||||
sde.testcontainers.image-name=docker.elastic.co/elasticsearch/elasticsearch
|
||||
sde.testcontainers.image-version=8.18.5
|
||||
sde.testcontainers.image-version=8.15.5
|
||||
#
|
||||
#
|
||||
# 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
|
||||
|
||||
Reference in New Issue
Block a user