Browse Source

Polishing.

Turn newly introduced methods on ParameterAccessor into default ones allowing modules to pick up changes at their own pace.
Add issue references and missing documentation.
Align Search- and GeoResults toString method with Page.

Original Pull Request: #3285
pull/3304/head
Christoph Strobl 8 months ago committed by Mark Paluch
parent
commit
9a7ea62b2e
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 12
      src/main/antora/modules/ROOT/pages/repositories/vector-search.adoc
  2. 1
      src/main/java/org/springframework/data/domain/SearchResult.java
  3. 4
      src/main/java/org/springframework/data/domain/SearchResults.java
  4. 3
      src/main/java/org/springframework/data/geo/GeoResult.java
  5. 7
      src/main/java/org/springframework/data/geo/GeoResults.java
  6. 15
      src/main/java/org/springframework/data/repository/query/ParameterAccessor.java
  7. 22
      src/main/java/org/springframework/data/repository/query/Parameters.java
  8. 9
      src/test/java/org/springframework/data/domain/SearchResultUnitTests.java
  9. 6
      src/test/java/org/springframework/data/domain/SearchResultsUnitTests.java
  10. 15
      src/test/java/org/springframework/data/domain/SimilarityUnitTests.java
  11. 4
      src/test/java/org/springframework/data/repository/query/SimpleParameterAccessorUnitTests.java

12
src/main/antora/modules/ROOT/pages/repositories/vector-search.adoc

@ -80,11 +80,11 @@ Score values are not part of a domain model and therefore represented best as ou @@ -80,11 +80,11 @@ Score values are not part of a domain model and therefore represented best as ou
Generally, a Score is computed by a `ScoringFunction`.
The actual scoring function used to calculate this score can depends on the underlying database and can be obtained from a search index or input parameters.
Spring Data supports declares constants for commonly used functions such as:
Spring Data support declares constants for commonly used functions such as:
Euclidean distance:: Calculates the straight-line distance in n-dimensional space involving the square root of the sum of squared differences.
Cosine similarity:: Measures the angle between two vectors by calculating the Dot product first and then normalizing its result by dividing by the product of their lengths.
Dot product:: Computes the sum of element-wise multiplications.
Euclidean Distance:: Calculates the straight-line distance in n-dimensional space involving the square root of the sum of squared differences.
Cosine Similarity:: Measures the angle between two vectors by calculating the Dot product first and then normalizing its result by dividing by the product of their lengths.
Dot Product:: Computes the sum of element-wise multiplications.
The choice of similarity function can impact both the performance and semantics of the search and is often determined by the underlying database or index being used.
Spring Data adopts to the database's native scoring function capabilities and whether the score can be used to limit results.
@ -107,7 +107,7 @@ Generally, you have the choice of declaring a search method using two approaches @@ -107,7 +107,7 @@ Generally, you have the choice of declaring a search method using two approaches
* Query Derivation
* Declaring a String-based Query
Generally, Vector Search methods must declare a `Vector` parameter to define the query vector.
Vector Search methods must declare a `Vector` parameter to define the query vector.
[[vector-search.method.derivation]]
=== Derived Search Methods
@ -142,7 +142,7 @@ endif::[] @@ -142,7 +142,7 @@ endif::[]
With more control over the actual query, Spring Data can make fewer assumptions about the query and its parameters.
For example, `Similarity` normalization uses the native score function within the query to normalize the given similarity into a score predicate value and vice versa.
If an annotated query doesn't define e.g. the score, then the score value in the returned `SearchResult<T>` will be zero.
If an annotated query does not define e.g. the score, then the score value in the returned `SearchResult<T>` will be zero.
[[vector-search.method.sorting]]
=== Sorting

1
src/main/java/org/springframework/data/domain/SearchResult.java

@ -20,7 +20,6 @@ import java.io.Serializable; @@ -20,7 +20,6 @@ import java.io.Serializable;
import java.util.function.Function;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

4
src/main/java/org/springframework/data/domain/SearchResults.java

@ -26,7 +26,6 @@ import java.util.stream.Stream; @@ -26,7 +26,6 @@ import java.util.stream.Stream;
import org.springframework.data.util.Streamable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Value object encapsulating a collection of {@link SearchResult} instances.
@ -123,8 +122,7 @@ public class SearchResults<T> implements Iterable<SearchResult<T>>, Serializable @@ -123,8 +122,7 @@ public class SearchResults<T> implements Iterable<SearchResult<T>>, Serializable
@Override
public String toString() {
return results.isEmpty() ? "SearchResults: [empty]"
: String.format("SearchResults: [results: %s]", StringUtils.collectionToCommaDelimitedString(results));
return results.isEmpty() ? "SearchResults [empty]" : String.format("SearchResults [size: %s]", results.size());
}
}

3
src/main/java/org/springframework/data/geo/GeoResult.java

@ -19,7 +19,6 @@ import java.io.Serial; @@ -19,7 +19,6 @@ import java.io.Serial;
import java.io.Serializable;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@ -79,6 +78,6 @@ public final class GeoResult<T> implements Serializable { @@ -79,6 +78,6 @@ public final class GeoResult<T> implements Serializable {
@Override
public String toString() {
return String.format("GeoResult [content: %s, distance: %s, ]", content.toString(), distance.toString());
return String.format("GeoResult [content: %s, distance: %s]", content, distance);
}
}

7
src/main/java/org/springframework/data/geo/GeoResults.java

@ -15,7 +15,6 @@ @@ -15,7 +15,6 @@
*/
package org.springframework.data.geo;
import java.io.Serial;
import java.io.Serializable;
import java.util.Collections;
@ -23,11 +22,9 @@ import java.util.Iterator; @@ -23,11 +22,9 @@ import java.util.Iterator;
import java.util.List;
import org.jspecify.annotations.Nullable;
import org.springframework.data.annotation.PersistenceCreator;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Value object to capture {@link GeoResult}s as well as the average distance they have.
@ -129,8 +126,8 @@ public class GeoResults<T> implements Iterable<GeoResult<T>>, Serializable { @@ -129,8 +126,8 @@ public class GeoResults<T> implements Iterable<GeoResult<T>>, Serializable {
@Override
public String toString() {
return String.format("GeoResults: [averageDistance: %s, results: %s]", averageDistance.toString(),
StringUtils.collectionToCommaDelimitedString(results));
return results.isEmpty() ? "GeoResults [empty]"
: String.format("GeoResults [averageDistance: %s, size: %s]", averageDistance, results.size());
}
private static Distance calculateAverageDistance(List<? extends GeoResult<?>> results, Metric metric) {

15
src/main/java/org/springframework/data/repository/query/ParameterAccessor.java

@ -39,22 +39,25 @@ public interface ParameterAccessor extends Iterable<Object> { @@ -39,22 +39,25 @@ public interface ParameterAccessor extends Iterable<Object> {
* @return the {@link Vector} of the parameters, if available; {@literal null} otherwise.
* @since 4.0
*/
@Nullable
Vector getVector();
default @Nullable Vector getVector() {
return null;
}
/**
* @return the {@link Score} of the parameters, if available; {@literal null} otherwise.
* @since 4.0
*/
@Nullable
Score getScore();
default @Nullable Score getScore() {
return null;
}
/**
* @return the {@link Range} of {@link Score} of the parameters, if available; {@literal null} otherwise.
* @since 4.0
*/
@Nullable
Range<Score> getScoreRange();
default @Nullable Range<Score> getScoreRange() {
return null;
}
/**
* @return the {@link ScrollPosition} of the parameters, if available; {@literal null} otherwise.

22
src/main/java/org/springframework/data/repository/query/Parameters.java

@ -15,7 +15,7 @@ @@ -15,7 +15,7 @@
*/
package org.springframework.data.repository.query;
import static java.lang.String.*;
import static java.lang.String.format;
import java.lang.reflect.Method;
import java.util.ArrayList;
@ -226,6 +226,12 @@ public abstract class Parameters<S extends Parameters<S, T>, T extends Parameter @@ -226,6 +226,12 @@ public abstract class Parameters<S extends Parameters<S, T>, T extends Parameter
return vectorIndex != -1;
}
/**
* Returns the index of the {@link Vector} argument.
*
* @return the argument index or {@literal -1} if none defined.
* @since 4.0
*/
public int getVectorIndex() {
return vectorIndex;
}
@ -240,12 +246,18 @@ public abstract class Parameters<S extends Parameters<S, T>, T extends Parameter @@ -240,12 +246,18 @@ public abstract class Parameters<S extends Parameters<S, T>, T extends Parameter
return scoreIndex != -1;
}
/**
* Returns the index of the {@link Score} argument.
*
* @return the argument index or {@literal -1} if none defined.
* @since 4.0
*/
public int getScoreIndex() {
return scoreIndex;
}
/**
* Returns whether the method the {@link Parameters} was created for contains a {@link Range} of {@link Score}
* Returns whether the method, the {@link Parameters} was created for, contains a {@link Range} of {@link Score}
* argument.
*
* @return
@ -255,6 +267,12 @@ public abstract class Parameters<S extends Parameters<S, T>, T extends Parameter @@ -255,6 +267,12 @@ public abstract class Parameters<S extends Parameters<S, T>, T extends Parameter
return scoreRangeIndex != -1;
}
/**
* Returns the index of the argument that contains a {@link Range} of {@link Score}.
*
* @return the argument index or {@literal -1} if none defined.
* @since 4.0
*/
public int getScoreRangeIndex() {
return scoreRangeIndex;
}

9
src/test/java/org/springframework/data/domain/SearchResultUnitTests.java

@ -33,12 +33,12 @@ class SearchResultUnitTests { @@ -33,12 +33,12 @@ class SearchResultUnitTests {
SearchResult<String> third = new SearchResult<>("Bar", Score.of(2.5));
SearchResult<String> fourth = new SearchResult<>("Foo", Score.of(5.2));
@Test // GH-
@Test // GH-3285
void considersSameInstanceEqual() {
assertThat(first.equals(first)).isTrue();
}
@Test // GH-
@Test // GH-3285
void considersSameValuesAsEqual() {
assertThat(first.equals(second)).isTrue();
@ -49,14 +49,13 @@ class SearchResultUnitTests { @@ -49,14 +49,13 @@ class SearchResultUnitTests {
assertThat(fourth.equals(first)).isFalse();
}
@Test
@Test // GH-3285
@SuppressWarnings({ "rawtypes", "unchecked" })
// GH-
void rejectsNullContent() {
assertThatIllegalArgumentException().isThrownBy(() -> new SearchResult(null, Score.of(2.5)));
}
@Test // GH-
@Test // GH-3285
@SuppressWarnings("unchecked")
void testSerialization() {

6
src/test/java/org/springframework/data/domain/SearchResultsUnitTests.java

@ -33,7 +33,7 @@ import org.springframework.util.SerializationUtils; @@ -33,7 +33,7 @@ import org.springframework.util.SerializationUtils;
class SearchResultsUnitTests {
@SuppressWarnings("unchecked")
@Test // GH-
@Test // GH-3285
void testSerialization() {
var result = new SearchResult<>("test", Score.of(2));
@ -45,7 +45,7 @@ class SearchResultsUnitTests { @@ -45,7 +45,7 @@ class SearchResultsUnitTests {
}
@SuppressWarnings("unchecked")
@Test // GH-
@Test // GH-3285
void testStream() {
var result = new SearchResult<>("test", Score.of(2));
@ -56,7 +56,7 @@ class SearchResultsUnitTests { @@ -56,7 +56,7 @@ class SearchResultsUnitTests {
}
@SuppressWarnings("unchecked")
@Test // GH-
@Test // GH-3285
void testContentStream() {
var result = new SearchResult<>("test", Score.of(2));

15
src/test/java/org/springframework/data/domain/SimilarityUnitTests.java

@ -15,7 +15,8 @@ @@ -15,7 +15,8 @@
*/
package org.springframework.data.domain;
import static org.assertj.core.api.Assertions.*;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import org.junit.jupiter.api.Test;
@ -26,14 +27,14 @@ import org.junit.jupiter.api.Test; @@ -26,14 +27,14 @@ import org.junit.jupiter.api.Test;
*/
class SimilarityUnitTests {
@Test
@Test // GH-3285
void shouldBeBounded() {
assertThatIllegalArgumentException().isThrownBy(() -> Similarity.of(-1));
assertThatIllegalArgumentException().isThrownBy(() -> Similarity.of(1.01));
}
@Test
@Test // GH-3285
void shouldConstructRawSimilarity() {
Similarity similarity = Similarity.raw(2, ScoringFunction.unspecified());
@ -41,7 +42,7 @@ class SimilarityUnitTests { @@ -41,7 +42,7 @@ class SimilarityUnitTests {
assertThat(similarity.getValue()).isEqualTo(2);
}
@Test
@Test // GH-3285
void shouldConstructGenericSimilarity() {
Similarity similarity = Similarity.of(1);
@ -51,7 +52,7 @@ class SimilarityUnitTests { @@ -51,7 +52,7 @@ class SimilarityUnitTests {
assertThat(similarity.getFunction()).isEqualTo(ScoringFunction.unspecified());
}
@Test
@Test // GH-3285
void shouldConstructMeteredSimilarity() {
Similarity similarity = Similarity.of(1, VectorScoringFunctions.COSINE);
@ -62,7 +63,7 @@ class SimilarityUnitTests { @@ -62,7 +63,7 @@ class SimilarityUnitTests {
assertThat(similarity.getFunction()).isEqualTo(VectorScoringFunctions.COSINE);
}
@Test
@Test // GH-3285
void shouldConstructRange() {
Range<Similarity> range = Similarity.between(0.5, 1);
@ -74,7 +75,7 @@ class SimilarityUnitTests { @@ -74,7 +75,7 @@ class SimilarityUnitTests {
assertThat(range.getUpperBound().isInclusive()).isTrue();
}
@Test
@Test // GH-3285
void shouldConstructRangeWithFunction() {
Range<Similarity> range = Similarity.between(0.5, 1, VectorScoringFunctions.COSINE);

4
src/test/java/org/springframework/data/repository/query/SimpleParameterAccessorUnitTests.java

@ -128,7 +128,7 @@ class SimpleParameterAccessorUnitTests { @@ -128,7 +128,7 @@ class SimpleParameterAccessorUnitTests {
assertThat(accessor.getSort()).isEqualTo(sort);
}
@Test
@Test // GH-3285
void returnsScoreIfAvailable() {
Score score = Score.of(1);
@ -137,7 +137,7 @@ class SimpleParameterAccessorUnitTests { @@ -137,7 +137,7 @@ class SimpleParameterAccessorUnitTests {
assertThat(accessor.getScore()).isEqualTo(score);
}
@Test
@Test // GH-3285
void returnsScoreRangeIfAvailable() {
Range<Similarity> range = Similarity.between(0, 1);

Loading…
Cancel
Save