Add type-safe property paths overloads (#2126)#2128
Add type-safe property paths overloads (#2126)#2128emilienbev wants to merge 2 commits intoAddFTSfrom
Conversation
2ab363b to
574f973
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces type-safe (method-reference based) property path overloads across Couchbase query/search fluent APIs, reducing reliance on string-based field names and adding mapping support for FTS field paths.
Changes:
- Added
TypedPropertyPathoverloads for projection, distinct, mutate-in paths, query criteria chaining, and FTS search configuration (sort/highlight/fields). - Introduced
SearchPropertyPathSupportto mapTypedPropertyPathto stored field names (including@Fieldaliases) for FTS operations. - Added new unit tests covering typed property references and mapped FTS field path resolution.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 21 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main/java/org/springframework/data/couchbase/core/ExecutableFindByIdOperation.java | Adds type-safe project(TypedPropertyPath...) overload. |
| src/main/java/org/springframework/data/couchbase/core/ReactiveFindByIdOperation.java | Adds type-safe project(TypedPropertyPath...) overload (reactive). |
| src/main/java/org/springframework/data/couchbase/core/ExecutableFindByQueryOperation.java | Adds type-safe project/distinct(TypedPropertyPath...) overloads (blocking). |
| src/main/java/org/springframework/data/couchbase/core/ReactiveFindByQueryOperation.java | Adds type-safe project/distinct(TypedPropertyPath...) overloads (reactive). |
| src/main/java/org/springframework/data/couchbase/core/query/QueryCriteria.java | Adds where/and/or(TypedPropertyPath...) overloads. |
| src/main/java/org/springframework/data/couchbase/core/query/Query.java | Adds distinct(TypedPropertyPath...) overload. |
| src/main/java/org/springframework/data/couchbase/core/ExecutableMutateInByIdOperation.java | Adds type-safe mutate-in path overloads (blocking). |
| src/main/java/org/springframework/data/couchbase/core/ReactiveMutateInByIdOperation.java | Adds type-safe mutate-in path overloads (reactive). |
| src/main/java/org/springframework/data/couchbase/core/support/WithProjectionId.java | Adds common type-safe project(TypedPropertyPath...) default method. |
| src/main/java/org/springframework/data/couchbase/core/support/WithProjecting.java | Adds common type-safe project(TypedPropertyPath...) default method. |
| src/main/java/org/springframework/data/couchbase/core/support/WithDistinct.java | Adds common type-safe distinct(TypedPropertyPath...) default method. |
| src/main/java/org/springframework/data/couchbase/core/support/WithMutateInPaths.java | Adds common type-safe mutate-in path default methods. |
| src/main/java/org/springframework/data/couchbase/core/ExecutableFindBySearchOperation.java | Adds type-safe FTS overloads for sort/highlight/fields (blocking). |
| src/main/java/org/springframework/data/couchbase/core/ReactiveFindBySearchOperation.java | Adds type-safe FTS overloads for sort/highlight/fields (reactive). |
| src/main/java/org/springframework/data/couchbase/core/ExecutableFindBySearchOperationSupport.java | Implements new typed FTS overloads (blocking). |
| src/main/java/org/springframework/data/couchbase/core/ReactiveFindBySearchOperationSupport.java | Implements new typed FTS overloads (reactive). |
| src/main/java/org/springframework/data/couchbase/core/SearchPropertyPathSupport.java | New helper to map typed property paths to stored field names and FTS sorts. |
| src/main/java/org/springframework/data/couchbase/repository/query/SearchBasedCouchbaseQuery.java | Minor whitespace cleanup. |
| src/main/java/org/springframework/data/couchbase/repository/query/ReactiveSearchBasedCouchbaseQuery.java | Minor whitespace cleanup. |
| src/test/java/org/springframework/data/couchbase/core/SearchPropertyPathSupportTests.java | New tests for mapped field path resolution (incl. @Field alias + nesting). |
| src/test/java/org/springframework/data/couchbase/core/query/TypeSafePropertyReferenceTests.java | New tests for typed paths in QueryCriteria, Query.distinct, and Sort. |
| src/test/java/org/springframework/data/couchbase/core/ExecutableFindBySearchOperationTests.java | Reflection-based checks ensuring typed overloads exist on interfaces. |
| src/test/java/org/springframework/data/couchbase/repository/query/SearchBasedCouchbaseQueryTests.java | Test scaffolding updated to compile with new typed overloads. |
| src/test/java/org/springframework/data/couchbase/repository/query/ReactiveSearchBasedCouchbaseQueryTests.java | Test scaffolding updated to compile with new typed overloads. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| import java.util.List; | ||
| import java.util.Optional; | ||
| import java.util.stream.Stream; | ||
|
|
||
| import org.springframework.dao.IncorrectResultSizeDataAccessException; | ||
| import java.util.Arrays; | ||
|
|
||
| import org.springframework.data.core.TypedPropertyPath; | ||
| import org.springframework.data.couchbase.core.query.Query; |
There was a problem hiding this comment.
Import ordering is inconsistent: java.util.Arrays is placed after org.springframework... imports. Reorder imports into the usual groups (java/javax → org.springframework → third-party) to match the project's formatting and avoid checkstyle/formatter diffs.
| * @param fields the property paths to project. | ||
| * @since 6.1 | ||
| */ | ||
| default Object project(TypedPropertyPath<?, ?>... fields) { |
There was a problem hiding this comment.
The new overload uses TypedPropertyPath<?, ?>... which weakens type-safety and allows passing paths unrelated to R. Consider changing the signature to TypedPropertyPath<R, ?>... so projected fields are constrained to the operation's entity type.
| default Object project(TypedPropertyPath<?, ?>... fields) { | |
| default Object project(TypedPropertyPath<R, ?>... fields) { |
| * | ||
| * @param fields the property paths to project. | ||
| * @since 6.1 | ||
| */ |
There was a problem hiding this comment.
This TypedPropertyPath<?, ?>... varargs method will trigger an "unchecked/heap pollution" compiler warning unless suppressed (similar methods in other files use @SuppressWarnings("unchecked")). Add an appropriate suppression (or refactor to avoid parameterized varargs) to keep the build warning-free.
| */ | |
| */ | |
| @SuppressWarnings("unchecked") |
| * @param fields the property paths to project. | ||
| * @since 6.1 | ||
| */ | ||
| default Object project(TypedPropertyPath<?, ?>... fields) { |
There was a problem hiding this comment.
The new overload uses TypedPropertyPath<?, ?>... which weakens type-safety and allows passing paths unrelated to R. Consider changing the signature to TypedPropertyPath<R, ?>... so projected fields are constrained to the operation's entity type.
| default Object project(TypedPropertyPath<?, ?>... fields) { | |
| default Object project(TypedPropertyPath<R, ?>... fields) { |
| * | ||
| * @param fields the property paths to project. | ||
| * @since 6.1 | ||
| */ |
There was a problem hiding this comment.
This TypedPropertyPath<?, ?>... varargs method will trigger an "unchecked/heap pollution" compiler warning unless suppressed. Add an appropriate suppression (or refactor to avoid parameterized varargs) to keep the build warning-free.
| */ | |
| */ | |
| @SuppressWarnings("unchecked") |
| @@ -234,6 +248,16 @@ interface FindByQueryWithDistinct<T> extends FindByQueryWithProjecting<T>, WithD | |||
| * @throws IllegalArgumentException if field is {@literal null}. | |||
| */ | |||
| FindByQueryWithProjection<T> distinct(String[] distinctFields); | |||
|
|
|||
| /** | |||
| * Type-safe variant of {@link #distinct(String[])} using property paths. | |||
| * | |||
| * @param distinctFields the property paths for distinct fields. | |||
| * @since 6.1 | |||
| */ | |||
| default FindByQueryWithProjection<T> distinct(TypedPropertyPath<?, ?>... distinctFields) { | |||
| return distinct(Arrays.stream(distinctFields).map(TypedPropertyPath::toDotPath).toArray(String[]::new)); | |||
| } | |||
There was a problem hiding this comment.
These TypedPropertyPath<?, ?>... varargs overloads will emit "unchecked/heap pollution" warnings unless suppressed. Add an appropriate suppression (or refactor to avoid parameterized varargs) to keep the build warning-free.
| @@ -288,6 +303,17 @@ | |||
| */ | |||
| @Override | |||
| FindByQueryWithProjection<T> distinct(String[] distinctFields); | |||
|
|
|||
| /** | |||
| * Type-safe variant of {@link #distinct(String[])} using property paths. | |||
| * | |||
| * @param distinctFields the property paths for distinct fields. | |||
| * @since 6.1 | |||
| */ | |||
| @SuppressWarnings("unchecked") | |||
| default FindByQueryWithProjection<T> distinct(TypedPropertyPath<?, ?>... distinctFields) { | |||
| return distinct(Arrays.stream(distinctFields).map(TypedPropertyPath::toDotPath).toArray(String[]::new)); | |||
| } | |||
There was a problem hiding this comment.
The project(TypedPropertyPath...) and distinct(TypedPropertyPath...) overloads use TypedPropertyPath<?, ?>..., which allows passing paths unrelated to the queried entity type T. Consider changing them to TypedPropertyPath<T, ?>... so these overloads actually enforce type-safe paths for this fluent API.
| /** | ||
| * Type-safe variant of {@link #withRemovePaths(String...)} using property paths. | ||
| * @since 6.1 | ||
| */ | ||
| @SuppressWarnings("unchecked") | ||
| default MutateInByIdWithPaths<T> withRemovePaths(TypedPropertyPath<?, ?>... removePaths) { | ||
| return withRemovePaths(Arrays.stream(removePaths).map(TypedPropertyPath::toDotPath).toArray(String[]::new)); | ||
| } | ||
|
|
||
| /** | ||
| * Type-safe variant of {@link #withInsertPaths(String...)} using property paths. | ||
| * @since 6.1 | ||
| */ | ||
| @SuppressWarnings("unchecked") | ||
| default MutateInByIdWithPaths<T> withInsertPaths(TypedPropertyPath<?, ?>... insertPaths) { | ||
| return withInsertPaths(Arrays.stream(insertPaths).map(TypedPropertyPath::toDotPath).toArray(String[]::new)); | ||
| } | ||
|
|
||
| /** | ||
| * Type-safe variant of {@link #withUpsertPaths(String...)} using property paths. | ||
| * @since 6.1 | ||
| */ | ||
| @SuppressWarnings("unchecked") | ||
| default MutateInByIdWithPaths<T> withUpsertPaths(TypedPropertyPath<?, ?>... upsertPaths) { | ||
| return withUpsertPaths(Arrays.stream(upsertPaths).map(TypedPropertyPath::toDotPath).toArray(String[]::new)); | ||
| } | ||
|
|
||
| /** | ||
| * Type-safe variant of {@link #withReplacePaths(String...)} using property paths. | ||
| * @since 6.1 | ||
| */ | ||
| @SuppressWarnings("unchecked") | ||
| default MutateInByIdWithPaths<T> withReplacePaths(TypedPropertyPath<?, ?>... replacePaths) { | ||
| return withReplacePaths(Arrays.stream(replacePaths).map(TypedPropertyPath::toDotPath).toArray(String[]::new)); | ||
| } |
There was a problem hiding this comment.
These new overloads accept TypedPropertyPath<?, ?>... even though the fluent API is parameterized with <T>, which allows passing paths unrelated to the mutated entity type. Consider changing them to TypedPropertyPath<T, ?>... to keep the API genuinely type-safe.
| @SuppressWarnings("unchecked") | ||
| default FindByIdInScope<T> project(TypedPropertyPath<?, ?>... fields) { |
There was a problem hiding this comment.
This new overload uses TypedPropertyPath<?, ?>..., which allows passing paths unrelated to the entity type T. Consider changing it to TypedPropertyPath<T, ?>... so the projection paths are constrained to the entity being loaded.
| @SuppressWarnings("unchecked") | |
| default FindByIdInScope<T> project(TypedPropertyPath<?, ?>... fields) { | |
| default FindByIdInScope<T> project(TypedPropertyPath<T, ?>... fields) { |
| * @since 6.1 | ||
| */ | ||
| @SuppressWarnings("unchecked") | ||
| default FindByIdInCollection<T> project(TypedPropertyPath<?, ?>... fields) { |
There was a problem hiding this comment.
This new overload uses TypedPropertyPath<?, ?>..., which allows passing paths unrelated to the entity type T. Consider changing it to TypedPropertyPath<T, ?>... so projection paths are constrained to the entity being loaded (matching the intent of type-safe property paths).
| default FindByIdInCollection<T> project(TypedPropertyPath<?, ?>... fields) { | |
| default FindByIdInCollection<T> project(TypedPropertyPath<T, ?>... fields) { |
Fixes #2126