Skip to content

Commit 3c4f4b1

Browse files
committed
Explore returning Search Results.
1 parent 838aea9 commit 3c4f4b1

23 files changed

+1031
-233
lines changed

spring-data-jpa/pom.xml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@
8888
<optional>true</optional>
8989
</dependency>
9090

91+
<dependency>
92+
<groupId>org.springframework</groupId>
93+
<artifactId>spring-test</artifactId>
94+
<scope>test</scope>
95+
</dependency>
96+
9197
<dependency>
9298
<groupId>org.junit.platform</groupId>
9399
<artifactId>junit-platform-launcher</artifactId>
@@ -183,6 +189,13 @@
183189
</exclusions>
184190
</dependency>
185191

192+
<dependency>
193+
<groupId>${hibernate.groupId}.orm</groupId>
194+
<artifactId>hibernate-vector</artifactId>
195+
<version>${hibernate}</version>
196+
<optional>true</optional>
197+
</dependency>
198+
186199
<dependency>
187200
<groupId>${hibernate.groupId}.orm</groupId>
188201
<artifactId>hibernate-jpamodelgen</artifactId>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.jpa.convert;
17+
18+
import jakarta.persistence.AttributeConverter;
19+
import jakarta.persistence.Converter;
20+
21+
import org.jspecify.annotations.Nullable;
22+
23+
import org.springframework.data.domain.Vector;
24+
25+
/**
26+
* JPA {@link Converter} for {@link Vector} types.
27+
*
28+
* @author Mark Paluch
29+
* @since 4.0
30+
*/
31+
public class VectorConverters {
32+
33+
@Converter(autoApply = true)
34+
public static class VectorAsFloatArrayConverter implements AttributeConverter<@Nullable Vector, @Nullable float[]> {
35+
36+
@Override
37+
public @Nullable float[] convertToDatabaseColumn(@Nullable Vector vector) {
38+
return vector == null ? null : vector.toFloatArray();
39+
}
40+
41+
@Override
42+
public @Nullable Vector convertToEntityAttribute(@Nullable float[] floats) {
43+
return floats == null ? null : Vector.of(floats);
44+
}
45+
}
46+
47+
@Converter(autoApply = true)
48+
public static class VectorAsDoubleArrayConverter implements AttributeConverter<@Nullable Vector, @Nullable double[]> {
49+
50+
@Override
51+
public @Nullable double[] convertToDatabaseColumn(@Nullable Vector vector) {
52+
return vector == null ? null : vector.toDoubleArray();
53+
}
54+
55+
@Override
56+
public @Nullable Vector convertToEntityAttribute(@Nullable double[] doubles) {
57+
return doubles == null ? null : Vector.of(doubles);
58+
}
59+
}
60+
61+
}

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/aot/QueriesFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,8 @@ private AotQuery createQuery(PartTree partTree, ReturnedType returnedType, JpaPa
224224

225225
ParameterMetadataProvider metadataProvider = new ParameterMetadataProvider(parameters, EscapeCharacter.DEFAULT,
226226
templates);
227-
JpaQueryCreator queryCreator = new JpaQueryCreator(partTree, returnedType, metadataProvider, templates, metamodel);
227+
JpaQueryCreator queryCreator = new JpaQueryCreator(partTree, false, returnedType, metadataProvider, templates,
228+
metamodel);
228229

229230
return StringAotQuery.jpqlQuery(queryCreator.createQuery(), metadataProvider.getBindings(),
230231
partTree.getResultLimit(), partTree.isDelete(), partTree.isExistsProjection());

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public AbstractJpaQuery(JpaQueryMethod method, EntityManager em) {
101101
return new StreamExecution();
102102
} else if (method.isProcedureQuery()) {
103103
return new ProcedureExecution(method.isCollectionQuery());
104-
} else if (method.isCollectionQuery()) {
104+
} else if (method.isCollectionQuery() || method.isSearchQuery()) {
105105
return new CollectionExecution();
106106
} else if (method.isSliceQuery()) {
107107
return new SlicedExecution();
@@ -140,17 +140,18 @@ protected JpaMetamodel getMetamodel() {
140140

141141
@Override
142142
public @Nullable Object execute(Object[] parameters) {
143-
return doExecute(getExecution(), parameters);
143+
144+
JpaParametersParameterAccessor accessor = obtainParameterAccessor(parameters);
145+
return doExecute(getExecution(accessor), accessor);
144146
}
145147

146148
/**
147149
* @param execution
148150
* @param values
149151
* @return
150152
*/
151-
private @Nullable Object doExecute(JpaQueryExecution execution, Object[] values) {
153+
private @Nullable Object doExecute(JpaQueryExecution execution, JpaParametersParameterAccessor accessor) {
152154

153-
JpaParametersParameterAccessor accessor = obtainParameterAccessor(values);
154155
Object result = execution.execute(this, accessor);
155156

156157
ResultProcessor withDynamicProjection = method.getResultProcessor().withDynamicProjection(accessor);
@@ -167,10 +168,17 @@ private JpaParametersParameterAccessor obtainParameterAccessor(Object[] values)
167168
return new JpaParametersParameterAccessor(method.getParameters(), values);
168169
}
169170

170-
protected JpaQueryExecution getExecution() {
171+
protected JpaQueryExecution getExecution(JpaParametersParameterAccessor accessor) {
171172

172173
JpaQueryExecution execution = this.execution.getNullable();
173174

175+
if (method.isSearchQuery()) {
176+
177+
ReturnedType returnedType = method.getResultProcessor().withDynamicProjection(accessor).getReturnedType();
178+
return new JpaQueryExecution.SearchResultExecution(execution == null ? new SingleEntityExecution() : execution,
179+
returnedType, accessor.getScoringFunction());
180+
}
181+
174182
if (execution != null) {
175183
return execution;
176184
}

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public class JpaCountQueryCreator extends JpaQueryCreator {
4848
public JpaCountQueryCreator(PartTree tree, ReturnedType returnedType, ParameterMetadataProvider provider,
4949
JpqlQueryTemplates templates, EntityManager em) {
5050

51-
super(tree, returnedType, provider, templates, em);
51+
super(tree, returnedType, provider, templates, em.getMetamodel());
5252

5353
this.distinct = tree.isDistinct();
5454
this.returnedType = returnedType;

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaKeysetScrollQueryCreator.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import java.util.List;
2424
import java.util.Map;
2525

26+
import java.util.concurrent.atomic.AtomicInteger;
27+
2628
import org.jspecify.annotations.Nullable;
2729

2830
import org.springframework.data.domain.KeysetScrollPosition;
@@ -49,7 +51,7 @@ public JpaKeysetScrollQueryCreator(PartTree tree, ReturnedType type, ParameterMe
4951
JpqlQueryTemplates templates, JpaEntityInformation<?, ?> entityInformation, KeysetScrollPosition scrollPosition,
5052
EntityManager em) {
5153

52-
super(tree, type, provider, templates, em);
54+
super(tree, type, provider, templates, em.getMetamodel());
5355

5456
this.entityInformation = entityInformation;
5557
this.scrollPosition = scrollPosition;

spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/JpaParametersParameterAccessor.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
import org.jspecify.annotations.Nullable;
1919

20+
import org.springframework.data.domain.Range;
21+
import org.springframework.data.domain.Score;
22+
import org.springframework.data.domain.ScoringFunction;
2023
import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter;
2124
import org.springframework.data.repository.query.Parameter;
2225
import org.springframework.data.repository.query.Parameters;
@@ -68,4 +71,28 @@ protected Object potentiallyUnwrap(Object parameterValue) {
6871
return parameterValue;
6972
}
7073

74+
public ScoringFunction getScoringFunction() {
75+
76+
Score score = getScore();
77+
if (score != null) {
78+
return score.getFunction();
79+
}
80+
81+
JpaParameters parameters = getParameters();
82+
if (parameters.hasScoreRangeParameter()) {
83+
84+
Range<Score> range = getScoreRange();
85+
86+
if (range.getUpperBound().isBounded()) {
87+
return range.getUpperBound().getValue().get().getFunction();
88+
}
89+
90+
if (range.getLowerBound().isBounded()) {
91+
return range.getLowerBound().getValue().get().getFunction();
92+
}
93+
}
94+
95+
return ScoringFunction.UNSPECIFIED;
96+
}
97+
7198
}

0 commit comments

Comments
 (0)