Skip to content

Commit e35d597

Browse files
emilienbevclaude
andcommitted
Resolve typed property paths to mapped field names outside FTS
The TypedPropertyPath overloads for QueryCriteria where/and/or, Query.distinct, fluent project/distinct and mutate-in paths converted paths with toDotPath(), baking raw Java property names into N1QL statements, KV sub-document projections and mutate-in paths. Fields renamed with @field (e.g. Person.middlename stored as "nickname") silently matched nothing, while the FTS overloads already resolved aliases via the converter. - QueryCriteria keeps the property path and resolves the mapped field name at export time, when a converter is available; string-based criteria are emitted verbatim as before. - Query.distinct(TypedPropertyPath...) keeps the typed paths and resolves them in toN1qlSelectString. - The operation Support classes override the typed project/distinct/ withXxxPaths defaults and map through template.getConverter(), mirroring the FTS withSort/withFields/withHighlight implementations. - SearchPropertyPathSupport renamed to PropertyPathSupport now that it serves all typed overloads, with an array-based mapping variant. - Added @SuppressWarnings("unchecked") on the reactive mutate-in typed defaults to match the blocking variant; unit tests cover alias resolution for where/and/or, distinct and the no-converter fallback. Signed-off-by: Emilien Bevierre <emilien.bevierre@couchbase.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent e375fd0 commit e35d597

16 files changed

Lines changed: 246 additions & 19 deletions

src/main/java/org/springframework/data/couchbase/core/ExecutableFindByIdOperationSupport.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.couchbase.core;
1717

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

102+
@Override
103+
@SafeVarargs
104+
// maps property references to stored field names (honoring @Field aliases) via the converter
105+
public final FindByIdInScope<T> project(TypedPropertyPath<T, ?>... fields) {
106+
return project(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), fields));
107+
}
108+
101109
@Override
102110
public FindByIdWithProjection<T> withExpiry(final Duration expiry) {
103111
return new ExecutableFindByIdSupport<>(template, domainType, scope, collection, options, fields,

src/main/java/org/springframework/data/couchbase/core/ExecutableFindByQueryOperation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
*/
1616
package org.springframework.data.couchbase.core;
1717

18-
import java.util.List;
1918
import java.util.Arrays;
19+
import java.util.List;
2020
import java.util.Optional;
2121
import java.util.stream.Stream;
2222

src/main/java/org/springframework/data/couchbase/core/ExecutableFindByQueryOperationSupport.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.couchbase.core;
1717

18+
import org.springframework.data.core.TypedPropertyPath;
1819
import java.util.List;
1920
import java.util.stream.Stream;
2021

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

151+
@Override
152+
@SafeVarargs
153+
// maps property references to stored field names (honoring @Field aliases) via the converter
154+
public final FindByQueryWithProjection<T> project(TypedPropertyPath<T, ?>... fields) {
155+
return project(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), fields));
156+
}
157+
158+
@Override
159+
@SafeVarargs
160+
// maps property references to stored field names (honoring @Field aliases) via the converter
161+
public final FindByQueryWithProjection<T> distinct(TypedPropertyPath<T, ?>... distinctFields) {
162+
return distinct(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), distinctFields));
163+
}
164+
150165
@Override
151166
public Stream<T> stream() {
152167
return reactiveSupport.all().toStream();

src/main/java/org/springframework/data/couchbase/core/ExecutableFindBySearchOperationSupport.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ public FindBySearchWithSkip<T> withSort(SearchSort... sort) {
219219
@Override
220220
public <P> FindBySearchWithSkip<T> withSort(TypedPropertyPath<P, ?> property,
221221
TypedPropertyPath<P, ?>... additionalProperties) {
222-
return withSort(SearchPropertyPathSupport.toSearchSorts(template.getConverter(), property, additionalProperties));
222+
return withSort(PropertyPathSupport.toSearchSorts(template.getConverter(), property, additionalProperties));
223223
}
224224

225225
@Override
@@ -232,7 +232,7 @@ public FindBySearchWithSort<T> withHighlight(HighlightStyle style, String... fie
232232
public <P> FindBySearchWithSort<T> withHighlight(HighlightStyle style, TypedPropertyPath<P, ?> field,
233233
TypedPropertyPath<P, ?>... additionalFields) {
234234
return withHighlight(style,
235-
SearchPropertyPathSupport.getMappedFieldPaths(template.getConverter(), field, additionalFields));
235+
PropertyPathSupport.getMappedFieldPaths(template.getConverter(), field, additionalFields));
236236
}
237237

238238
@Override
@@ -252,7 +252,7 @@ public FindBySearchWithFacets<T> withFields(String... fields) {
252252
@Override
253253
public <P> FindBySearchWithFacets<T> withFields(TypedPropertyPath<P, ?> field,
254254
TypedPropertyPath<P, ?>... additionalFields) {
255-
return withFields(SearchPropertyPathSupport.getMappedFieldPaths(template.getConverter(), field, additionalFields));
255+
return withFields(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), field, additionalFields));
256256
}
257257
}
258258
}

src/main/java/org/springframework/data/couchbase/core/ExecutableMutateInByIdOperationSupport.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.couchbase.core;
1717

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

182+
183+
@Override
184+
@SafeVarargs
185+
// maps property references to stored field names (honoring @Field aliases) via the converter
186+
public final MutateInByIdWithPaths<T> withRemovePaths(TypedPropertyPath<T, ?>... removePaths) {
187+
return withRemovePaths(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), removePaths));
188+
}
189+
190+
@Override
191+
@SafeVarargs
192+
// maps property references to stored field names (honoring @Field aliases) via the converter
193+
public final MutateInByIdWithPaths<T> withUpsertPaths(TypedPropertyPath<T, ?>... upsertPaths) {
194+
return withUpsertPaths(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), upsertPaths));
195+
}
196+
197+
@Override
198+
@SafeVarargs
199+
// maps property references to stored field names (honoring @Field aliases) via the converter
200+
public final MutateInByIdWithPaths<T> withInsertPaths(TypedPropertyPath<T, ?>... insertPaths) {
201+
return withInsertPaths(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), insertPaths));
202+
}
203+
204+
@Override
205+
@SafeVarargs
206+
// maps property references to stored field names (honoring @Field aliases) via the converter
207+
public final MutateInByIdWithPaths<T> withReplacePaths(TypedPropertyPath<T, ?>... replacePaths) {
208+
return withReplacePaths(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), replacePaths));
209+
}
210+
181211
@Override
182212
public MutateInByIdWithPaths<T> withCasProvided() {
183213
return new ExecutableMutateInByIdSupport<>(template, domainType, scope, collection, options, persistTo, replicateTo,

src/main/java/org/springframework/data/couchbase/core/SearchPropertyPathSupport.java renamed to src/main/java/org/springframework/data/couchbase/core/PropertyPathSupport.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,25 @@
2525

2626
import com.couchbase.client.java.search.sort.SearchSort;
2727

28-
final class SearchPropertyPathSupport {
28+
/**
29+
* Maps {@link TypedPropertyPath} property references to the stored field names, honoring {@code @Field} aliases on
30+
* every segment of the path.
31+
*
32+
* @author Emilien Bevierre
33+
* @since 6.2
34+
*/
35+
final class PropertyPathSupport {
36+
37+
private PropertyPathSupport() {
38+
}
39+
40+
static <P> String[] getMappedFieldPaths(CouchbaseConverter converter, TypedPropertyPath<P, ?>[] properties) {
2941

30-
private SearchPropertyPathSupport() {
42+
String[] fields = new String[properties.length];
43+
for (int i = 0; i < properties.length; i++) {
44+
fields[i] = getMappedFieldPath(converter, properties[i]);
45+
}
46+
return fields;
3147
}
3248

3349
static <P> String getMappedFieldPath(CouchbaseConverter converter, TypedPropertyPath<P, ?> property) {

src/main/java/org/springframework/data/couchbase/core/ReactiveFindByIdOperationSupport.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.couchbase.core;
1717

18+
import org.springframework.data.core.TypedPropertyPath;
1819
import static com.couchbase.client.java.kv.GetAndLockOptions.getAndLockOptions;
1920
import static com.couchbase.client.java.kv.GetAndTouchOptions.getAndTouchOptions;
2021
import static com.couchbase.client.java.transactions.internal.ConverterUtil.makeCollectionIdentifier;
@@ -210,6 +211,13 @@ public FindByIdInCollection<T> project(String... fields) {
210211
expiry, lockDuration, support);
211212
}
212213

214+
@Override
215+
@SafeVarargs
216+
// maps property references to stored field names (honoring @Field aliases) via the converter
217+
public final FindByIdInCollection<T> project(TypedPropertyPath<T, ?>... fields) {
218+
return project(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), fields));
219+
}
220+
213221
@Override
214222
public FindByIdWithProjection<T> withExpiry(final Duration expiry) {
215223
return new ReactiveFindByIdSupport<>(template, domainType, scope, collection, options, fields, expiry,

src/main/java/org/springframework/data/couchbase/core/ReactiveFindByQueryOperationSupport.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.couchbase.core;
1717

18+
import org.springframework.data.core.TypedPropertyPath;
1819
import reactor.core.publisher.Flux;
1920
import reactor.core.publisher.Mono;
2021

@@ -164,6 +165,20 @@ public FindByQueryWithDistinct<T> distinct(final String[] distinctFields) {
164165
collection, options, dFields, fields, support);
165166
}
166167

168+
@Override
169+
@SafeVarargs
170+
// maps property references to stored field names (honoring @Field aliases) via the converter
171+
public final FindByQueryWithProjection<T> project(TypedPropertyPath<T, ?>... fields) {
172+
return project(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), fields));
173+
}
174+
175+
@Override
176+
@SafeVarargs
177+
// maps property references to stored field names (honoring @Field aliases) via the converter
178+
public final FindByQueryWithProjection<T> distinct(TypedPropertyPath<T, ?>... distinctFields) {
179+
return distinct(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), distinctFields));
180+
}
181+
167182
@Override
168183
public Mono<T> one() {
169184
return all().singleOrEmpty();

src/main/java/org/springframework/data/couchbase/core/ReactiveFindBySearchOperationSupport.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ public FindBySearchWithSkip<T> withSort(SearchSort... sort) {
183183
@Override
184184
public <P> FindBySearchWithSkip<T> withSort(TypedPropertyPath<P, ?> property,
185185
TypedPropertyPath<P, ?>... additionalProperties) {
186-
return withSort(SearchPropertyPathSupport.toSearchSorts(template.getConverter(), property, additionalProperties));
186+
return withSort(PropertyPathSupport.toSearchSorts(template.getConverter(), property, additionalProperties));
187187
}
188188

189189
@Override
@@ -197,7 +197,7 @@ public FindBySearchWithSort<T> withHighlight(HighlightStyle style, String... fie
197197
public <P> FindBySearchWithSort<T> withHighlight(HighlightStyle style, TypedPropertyPath<P, ?> field,
198198
TypedPropertyPath<P, ?>... additionalFields) {
199199
return withHighlight(style,
200-
SearchPropertyPathSupport.getMappedFieldPaths(template.getConverter(), field, additionalFields));
200+
PropertyPathSupport.getMappedFieldPaths(template.getConverter(), field, additionalFields));
201201
}
202202

203203
@Override
@@ -217,7 +217,7 @@ public FindBySearchWithFacets<T> withFields(String... fields) {
217217
@Override
218218
public <P> FindBySearchWithFacets<T> withFields(TypedPropertyPath<P, ?> field,
219219
TypedPropertyPath<P, ?>... additionalFields) {
220-
return withFields(SearchPropertyPathSupport.getMappedFieldPaths(template.getConverter(), field, additionalFields));
220+
return withFields(PropertyPathSupport.getMappedFieldPaths(template.getConverter(), field, additionalFields));
221221
}
222222

223223
@Override

src/main/java/org/springframework/data/couchbase/core/ReactiveMutateInByIdOperation.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ interface MutateInByIdWithPaths<T> extends TerminatingMutateInById<T>, WithMutat
106106
* Type-safe variant of {@link #withRemovePaths(String...)} using property paths.
107107
* @since 6.1
108108
*/
109+
@SuppressWarnings("unchecked")
109110
default MutateInByIdWithPaths<T> withRemovePaths(TypedPropertyPath<T, ?>... removePaths) {
110111
return withRemovePaths(Arrays.stream(removePaths).map(TypedPropertyPath::toDotPath).toArray(String[]::new));
111112
}
@@ -114,6 +115,7 @@ default MutateInByIdWithPaths<T> withRemovePaths(TypedPropertyPath<T, ?>... remo
114115
* Type-safe variant of {@link #withInsertPaths(String...)} using property paths.
115116
* @since 6.1
116117
*/
118+
@SuppressWarnings("unchecked")
117119
default MutateInByIdWithPaths<T> withInsertPaths(TypedPropertyPath<T, ?>... insertPaths) {
118120
return withInsertPaths(Arrays.stream(insertPaths).map(TypedPropertyPath::toDotPath).toArray(String[]::new));
119121
}
@@ -122,6 +124,7 @@ default MutateInByIdWithPaths<T> withInsertPaths(TypedPropertyPath<T, ?>... inse
122124
* Type-safe variant of {@link #withUpsertPaths(String...)} using property paths.
123125
* @since 6.1
124126
*/
127+
@SuppressWarnings("unchecked")
125128
default MutateInByIdWithPaths<T> withUpsertPaths(TypedPropertyPath<T, ?>... upsertPaths) {
126129
return withUpsertPaths(Arrays.stream(upsertPaths).map(TypedPropertyPath::toDotPath).toArray(String[]::new));
127130
}
@@ -130,6 +133,7 @@ default MutateInByIdWithPaths<T> withUpsertPaths(TypedPropertyPath<T, ?>... upse
130133
* Type-safe variant of {@link #withReplacePaths(String...)} using property paths.
131134
* @since 6.1
132135
*/
136+
@SuppressWarnings("unchecked")
133137
default MutateInByIdWithPaths<T> withReplacePaths(TypedPropertyPath<T, ?>... replacePaths) {
134138
return withReplacePaths(Arrays.stream(replacePaths).map(TypedPropertyPath::toDotPath).toArray(String[]::new));
135139
}

0 commit comments

Comments
 (0)