Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
package org.springframework.data.couchbase.core;

import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;

import org.springframework.data.core.TypedPropertyPath;
import org.springframework.data.couchbase.core.support.OneAndAllId;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.WithGetOptions;
Expand All @@ -33,6 +35,7 @@
*
* @author Christoph Strobl
* @author Tigran Babloyan
* @author Emilien Bevierre
* @since 2.0
*/
public interface ExecutableFindByIdOperation {
Expand Down Expand Up @@ -122,6 +125,17 @@ interface FindByIdWithProjection<T> extends FindByIdInScope<T>, WithProjectionId
*/
@Override
FindByIdInScope<T> project(String... fields);

/**
* Type-safe variant of {@link #project(String...)} using property paths.
*
* @param fields the property paths to project.
* @since 6.1
*/
@SuppressWarnings("unchecked")
default FindByIdInScope<T> project(TypedPropertyPath<T, ?>... fields) {
return project(Arrays.stream(fields).map(TypedPropertyPath::toDotPath).toArray(String[]::new));
}
}

interface FindByIdWithExpiry<T> extends FindByIdWithProjection<T>, WithExpiry<T> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.springframework.data.couchbase.core;

import org.springframework.data.core.TypedPropertyPath;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
Expand Down Expand Up @@ -98,6 +99,13 @@ public FindByIdInScope<T> project(String... fields) {
return new ExecutableFindByIdSupport<>(template, domainType, scope, collection, options, Arrays.asList(fields), expiry, lockDuration);
}

@Override
@SafeVarargs
// maps property references to stored field names (honoring @Field aliases) via the converter
public final FindByIdInScope<T> project(TypedPropertyPath<T, ?>... fields) {
return project(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), fields));
}

@Override
public FindByIdWithProjection<T> withExpiry(final Duration expiry) {
return new ExecutableFindByIdSupport<>(template, domainType, scope, collection, options, fields,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
*/
package org.springframework.data.couchbase.core;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

import org.springframework.dao.IncorrectResultSizeDataAccessException;

import org.springframework.data.core.TypedPropertyPath;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.QueryCriteriaDefinition;
import org.springframework.data.couchbase.core.support.InCollection;
Expand All @@ -38,6 +41,7 @@
* Query Operations
*
* @author Christoph Strobl
* @author Emilien Bevierre
* @since 2.0
*/
public interface ExecutableFindByQueryOperation {
Expand Down Expand Up @@ -270,6 +274,17 @@ interface FindByQueryWithProjecting<T> extends FindByQueryWithProjection<T> {
* @throws IllegalArgumentException if returnType is {@literal null}.
*/
FindByQueryWithProjection<T> project(String[] fields);

/**
* Type-safe variant of {@link #project(String[])} using property paths.
*
* @param fields the property paths to project.
* @since 6.1
*/
@SuppressWarnings("unchecked")
default FindByQueryWithProjection<T> project(TypedPropertyPath<T, ?>... fields) {
return project(Arrays.stream(fields).map(TypedPropertyPath::toDotPath).toArray(String[]::new));
}
}

/**
Expand All @@ -288,6 +303,17 @@ interface FindByQueryWithDistinct<T> extends FindByQueryWithProjecting<T>, WithD
*/
@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<T, ?>... distinctFields) {
return distinct(Arrays.stream(distinctFields).map(TypedPropertyPath::toDotPath).toArray(String[]::new));
}
Comment on lines 284 to +316

Copilot AI Apr 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.springframework.data.couchbase.core;

import org.springframework.data.core.TypedPropertyPath;
import java.util.List;
import java.util.stream.Stream;

Expand Down Expand Up @@ -147,6 +148,20 @@ public FindByQueryWithProjection<T> distinct(final String[] distinctFields) {
collection, options, dFields, fields);
}

@Override
@SafeVarargs
// maps property references to stored field names (honoring @Field aliases) via the converter
public final FindByQueryWithProjection<T> project(TypedPropertyPath<T, ?>... fields) {
return project(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), fields));
}

@Override
@SafeVarargs
// maps property references to stored field names (honoring @Field aliases) via the converter
public final FindByQueryWithProjection<T> distinct(TypedPropertyPath<T, ?>... distinctFields) {
return distinct(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), distinctFields));
}

@Override
public Stream<T> stream() {
return reactiveSupport.all().toStream();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.stream.Stream;

import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.core.TypedPropertyPath;
import org.springframework.data.couchbase.core.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.WithSearchConsistency;
Expand Down Expand Up @@ -170,6 +171,9 @@ interface FindBySearchWithSkip<T> extends FindBySearchWithLimit<T> {

interface FindBySearchWithSort<T> extends FindBySearchWithSkip<T> {
FindBySearchWithSkip<T> withSort(SearchSort... sort);

<P> FindBySearchWithSkip<T> withSort(TypedPropertyPath<P, ?> property,
TypedPropertyPath<P, ?>... additionalProperties);
}

interface FindBySearchWithHighlight<T> extends FindBySearchWithSort<T> {
Expand All @@ -178,6 +182,14 @@ interface FindBySearchWithHighlight<T> extends FindBySearchWithSort<T> {
default FindBySearchWithSort<T> withHighlight(String... fields) {
return withHighlight(HighlightStyle.SERVER_DEFAULT, fields);
}

<P> FindBySearchWithSort<T> withHighlight(HighlightStyle style, TypedPropertyPath<P, ?> field,
TypedPropertyPath<P, ?>... additionalFields);

default <P> FindBySearchWithSort<T> withHighlight(TypedPropertyPath<P, ?> field,
TypedPropertyPath<P, ?>... additionalFields) {
return withHighlight(HighlightStyle.SERVER_DEFAULT, field, additionalFields);
}
}

interface FindBySearchWithFacets<T> extends FindBySearchWithHighlight<T> {
Expand All @@ -186,6 +198,9 @@ interface FindBySearchWithFacets<T> extends FindBySearchWithHighlight<T> {

interface FindBySearchWithFields<T> extends FindBySearchWithFacets<T> {
FindBySearchWithFacets<T> withFields(String... fields);

<P> FindBySearchWithFacets<T> withFields(TypedPropertyPath<P, ?> field,
TypedPropertyPath<P, ?>... additionalFields);
Comment on lines 172 to +203

Copilot AI Apr 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new overloads declare their own type parameter <P> instead of reusing the fluent API's entity type <T>, which allows callers to pass property paths unrelated to the searched entity. Consider changing these signatures to TypedPropertyPath<T, ?> (and corresponding varargs) to make the overloads actually type-safe for the operation.

Copilot uses AI. Check for mistakes.
}

interface FindBySearchWithProjection<T> extends FindBySearchWithFields<T> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import java.util.Map;
import java.util.stream.Stream;

import org.springframework.data.core.TypedPropertyPath;
import org.springframework.data.couchbase.core.ReactiveFindBySearchOperationSupport.ReactiveFindBySearchSupport;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.util.Assert;
Expand Down Expand Up @@ -190,8 +191,8 @@ public FindBySearchWithOptions<T> inCollection(final String collection) {
@Override
public FindBySearchWithQuery<T> withOptions(final SearchOptions options) {
return new ExecutableFindBySearchSupport<>(template, domainType, returnType, indexName, searchRequest,
scanConsistency, scope, collection, options != null ? options : this.options, sort,
highlightStyle, highlightFields, facets, fields, limitSkip);
scanConsistency, scope, collection, options != null ? options : this.options, sort, highlightStyle,
highlightFields, facets, fields, limitSkip);
}

@Override
Expand Down Expand Up @@ -219,11 +220,23 @@ public FindBySearchWithSkip<T> withSort(SearchSort... sort) {
fields, limitSkip);
}

@Override
public <P> FindBySearchWithSkip<T> withSort(TypedPropertyPath<P, ?> property,
TypedPropertyPath<P, ?>... additionalProperties) {
return withSort(PropertyPathSupport.toSearchSorts(template.getConverter(), property, additionalProperties));
}
Comment on lines +223 to +227

Copilot AI Apr 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These implementations mirror the interface issue: the overload introduces a fresh <P> type parameter instead of tying the path root to the searched entity type <T>, so callers can pass property paths for unrelated classes. Update the signature to accept TypedPropertyPath<T, ?> (and varargs) so it is truly type-safe for this operation.

Copilot uses AI. Check for mistakes.

@Override
public FindBySearchWithSort<T> withHighlight(HighlightStyle style, String... fields) {
return new ExecutableFindBySearchSupport<>(template, domainType, returnType, indexName, searchRequest,
scanConsistency, scope, collection, options, sort, style, fields, facets,
this.fields, limitSkip);
scanConsistency, scope, collection, options, sort, style, fields, facets, this.fields, limitSkip);
}

@Override
public <P> FindBySearchWithSort<T> withHighlight(HighlightStyle style, TypedPropertyPath<P, ?> field,
TypedPropertyPath<P, ?>... additionalFields) {
return withHighlight(style,
PropertyPathSupport.getMappedFieldPaths(template.getConverter(), field, additionalFields));
}

@Override
Expand All @@ -239,5 +252,11 @@ public FindBySearchWithFacets<T> withFields(String... fields) {
scanConsistency, scope, collection, options, sort, highlightStyle, highlightFields, facets,
fields, limitSkip);
}

@Override
public <P> FindBySearchWithFacets<T> withFields(TypedPropertyPath<P, ?> field,
TypedPropertyPath<P, ?>... additionalFields) {
return withFields(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), field, additionalFields));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,20 @@
import com.couchbase.client.java.kv.MutateInOptions;
import com.couchbase.client.java.kv.PersistTo;
import com.couchbase.client.java.kv.ReplicateTo;
import org.springframework.data.core.TypedPropertyPath;
import org.springframework.data.couchbase.core.support.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;

/**
* Mutate In Operations
*
* @author Tigran Babloyan
* @author Emilien Bevierre
* @since 5.1
*/
public interface ExecutableMutateInByIdOperation {
Expand Down Expand Up @@ -98,6 +101,42 @@ interface MutateInByIdWithPaths<T> extends TerminatingMutateInById<T>, WithMutat
* By default the CAS value is not provided.
*/
MutateInByIdWithPaths<T> withCasProvided();

/**
* Type-safe variant of {@link #withRemovePaths(String...)} using property paths.
* @since 6.1
*/
@SuppressWarnings("unchecked")
default MutateInByIdWithPaths<T> withRemovePaths(TypedPropertyPath<T, ?>... 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<T, ?>... 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<T, ?>... 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<T, ?>... replacePaths) {
return withReplacePaths(Arrays.stream(replacePaths).map(TypedPropertyPath::toDotPath).toArray(String[]::new));
}
Comment on lines +105 to +139

Copilot AI Apr 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package org.springframework.data.couchbase.core;

import org.springframework.data.core.TypedPropertyPath;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -178,6 +179,35 @@ public MutateInByIdWithDurability<T> withReplacePaths(final String... replacePat
durabilityLevel, expiry, removePaths, upsertPaths, insertPaths, Arrays.asList(replacePaths), provideCas);
}


@Override
@SafeVarargs
// maps property references to stored field names (honoring @Field aliases) via the converter
public final MutateInByIdWithPaths<T> withRemovePaths(TypedPropertyPath<T, ?>... removePaths) {
return withRemovePaths(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), removePaths));
}

@Override
@SafeVarargs
// maps property references to stored field names (honoring @Field aliases) via the converter
public final MutateInByIdWithPaths<T> withUpsertPaths(TypedPropertyPath<T, ?>... upsertPaths) {
return withUpsertPaths(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), upsertPaths));
}

@Override
@SafeVarargs
// maps property references to stored field names (honoring @Field aliases) via the converter
public final MutateInByIdWithPaths<T> withInsertPaths(TypedPropertyPath<T, ?>... insertPaths) {
return withInsertPaths(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), insertPaths));
}

@Override
@SafeVarargs
// maps property references to stored field names (honoring @Field aliases) via the converter
public final MutateInByIdWithPaths<T> withReplacePaths(TypedPropertyPath<T, ?>... replacePaths) {
return withReplacePaths(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), replacePaths));
}

@Override
public MutateInByIdWithPaths<T> withCasProvided() {
return new ExecutableMutateInByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,
Expand Down
Loading
Loading