Skip to content

Commit a320669

Browse files
mp911dechristophstrobl
authored andcommitted
Explore returning Search Results.
Closes: #3868
1 parent b5ecb8b commit a320669

39 files changed

+1931
-223
lines changed

pom.xml

+22
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
<jsqlparser>5.0</jsqlparser>
3939
<mysql-connector-java>9.1.0</mysql-connector-java>
4040
<postgresql>42.7.4</postgresql>
41+
<oracle>23.7.0.25.01</oracle>
4142
<springdata.commons>4.0.0-SNAPSHOT</springdata.commons>
4243
<vavr>0.10.3</vavr>
4344

@@ -56,6 +57,14 @@
5657
<profiles>
5758
<profile>
5859
<id>jmh</id>
60+
<dependencies>
61+
<dependency>
62+
<groupId>com.github.mp911de.microbenchmark-runner</groupId>
63+
<artifactId>microbenchmark-runner-junit5</artifactId>
64+
<version>0.5.0.RELEASE</version>
65+
<scope>test</scope>
66+
</dependency>
67+
</dependencies>
5968
<repositories>
6069
<repository>
6170
<id>jitpack</id>
@@ -112,6 +121,19 @@
112121
</includes>
113122
</configuration>
114123
</execution>
124+
<execution>
125+
<id>oracle-test</id>
126+
<phase>test</phase>
127+
<goals>
128+
<goal>test</goal>
129+
</goals>
130+
<configuration>
131+
<includes>
132+
<include>**/Oracle*IntegrationTests.java
133+
</include>
134+
</includes>
135+
</configuration>
136+
</execution>
115137
</executions>
116138
</plugin>
117139
</plugins>

spring-data-jpa/pom.xml

+36
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>
@@ -161,6 +167,28 @@
161167
<scope>test</scope>
162168
</dependency>
163169

170+
<!-- Oracle testing support -->
171+
172+
<dependency>
173+
<groupId>com.oracle.database.jdbc</groupId>
174+
<artifactId>ojdbc17</artifactId>
175+
<version>${oracle}</version>
176+
<scope>test</scope>
177+
</dependency>
178+
179+
<dependency>
180+
<groupId>com.oracle.database.jdbc</groupId>
181+
<artifactId>ucp17</artifactId>
182+
<version>${oracle}</version>
183+
<scope>test</scope>
184+
</dependency>
185+
186+
<dependency>
187+
<groupId>org.testcontainers</groupId>
188+
<artifactId>oracle-free</artifactId>
189+
<scope>test</scope>
190+
</dependency>
191+
164192
<dependency>
165193
<groupId>io.vavr</groupId>
166194
<artifactId>vavr</artifactId>
@@ -183,6 +211,13 @@
183211
</exclusions>
184212
</dependency>
185213

214+
<dependency>
215+
<groupId>${hibernate.groupId}.orm</groupId>
216+
<artifactId>hibernate-vector</artifactId>
217+
<version>${hibernate}</version>
218+
<optional>true</optional>
219+
</dependency>
220+
186221
<dependency>
187222
<groupId>${hibernate.groupId}.orm</groupId>
188223
<artifactId>hibernate-jpamodelgen</artifactId>
@@ -318,6 +353,7 @@
318353
<exclude>**/EclipseLink*</exclude>
319354
<exclude>**/MySql*</exclude>
320355
<exclude>**/Postgres*</exclude>
356+
<exclude>**/Oracle*</exclude>
321357
</excludes>
322358
<argLine>
323359
-Xmx4G

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

+2-1
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

+13-5
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();
@@ -149,17 +149,18 @@ protected JpaMetamodel getMetamodel() {
149149

150150
@Override
151151
public @Nullable Object execute(Object[] parameters) {
152-
return doExecute(getExecution(), parameters);
152+
153+
JpaParametersParameterAccessor accessor = obtainParameterAccessor(parameters);
154+
return doExecute(getExecution(accessor), accessor);
153155
}
154156

155157
/**
156158
* @param execution
157159
* @param values
158160
* @return
159161
*/
160-
private @Nullable Object doExecute(JpaQueryExecution execution, Object[] values) {
162+
private @Nullable Object doExecute(JpaQueryExecution execution, JpaParametersParameterAccessor accessor) {
161163

162-
JpaParametersParameterAccessor accessor = obtainParameterAccessor(values);
163164
Object result = execution.execute(this, accessor);
164165

165166
ResultProcessor withDynamicProjection = method.getResultProcessor().withDynamicProjection(accessor);
@@ -176,10 +177,17 @@ private JpaParametersParameterAccessor obtainParameterAccessor(Object[] values)
176177
return new JpaParametersParameterAccessor(method.getParameters(), values);
177178
}
178179

179-
protected JpaQueryExecution getExecution() {
180+
protected JpaQueryExecution getExecution(JpaParametersParameterAccessor accessor) {
180181

181182
JpaQueryExecution execution = this.execution.getNullable();
182183

184+
if (method.isSearchQuery()) {
185+
186+
ReturnedType returnedType = method.getResultProcessor().withDynamicProjection(accessor).getReturnedType();
187+
return new JpaQueryExecution.SearchResultExecution(execution == null ? new SingleEntityExecution() : execution,
188+
returnedType, accessor.getScoringFunction(), accessor.normalizeSimilarity());
189+
}
190+
183191
if (execution != null) {
184192
return execution;
185193
}

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

+1-1
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

+3-1
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

+58
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,16 @@
1515
*/
1616
package org.springframework.data.jpa.repository.query;
1717

18+
import java.util.function.Function;
19+
import java.util.function.Predicate;
20+
import java.util.function.Supplier;
21+
1822
import org.jspecify.annotations.Nullable;
1923

24+
import org.springframework.data.domain.Range;
25+
import org.springframework.data.domain.Score;
26+
import org.springframework.data.domain.ScoringFunction;
27+
import org.springframework.data.domain.Similarity;
2028
import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter;
2129
import org.springframework.data.repository.query.Parameter;
2230
import org.springframework.data.repository.query.Parameters;
@@ -68,4 +76,54 @@ protected Object potentiallyUnwrap(Object parameterValue) {
6876
return parameterValue;
6977
}
7078

79+
/**
80+
* Returns the {@link ScoringFunction}.
81+
*
82+
* @return
83+
*/
84+
public ScoringFunction getScoringFunction() {
85+
return doWithScore(Score::getFunction, Score.class::isInstance, ScoringFunction::unspecified);
86+
}
87+
88+
/**
89+
* Returns whether to normalize similarities (i.e. translate the database-specific score into {@link Similarity}).
90+
*
91+
* @return
92+
*/
93+
public boolean normalizeSimilarity() {
94+
return doWithScore(it -> true, Similarity.class::isInstance, () -> false);
95+
}
96+
97+
/**
98+
* Returns the {@link ScoringFunction}.
99+
*
100+
* @return
101+
*/
102+
public <T> T doWithScore(Function<Score, T> function, Predicate<Score> scoreFilter, Supplier<T> defaultValue) {
103+
104+
Score score = getScore();
105+
if (score != null && scoreFilter.test(score)) {
106+
return function.apply(score);
107+
}
108+
109+
JpaParameters parameters = getParameters();
110+
if (parameters.hasScoreRangeParameter()) {
111+
112+
Range<Score> range = getScoreRange();
113+
114+
if (range != null && range.getLowerBound().isBounded()
115+
&& scoreFilter.test(range.getLowerBound().getValue().get())) {
116+
return function.apply(range.getUpperBound().getValue().get());
117+
}
118+
119+
if (range != null && range.getUpperBound().isBounded()
120+
&& scoreFilter.test(range.getUpperBound().getValue().get())) {
121+
return function.apply(range.getUpperBound().getValue().get());
122+
}
123+
124+
}
125+
126+
return defaultValue.get();
127+
}
128+
71129
}

0 commit comments

Comments
 (0)