Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
* @author Michael Reiche
* @author Jorge Rodriguez Martin
* @author Tigran Babloyan
* @author Emilien Bevierre
* @since 3.0
*/
public class CouchbaseTemplate implements CouchbaseOperations, ApplicationContextAware {
Expand Down Expand Up @@ -188,6 +189,11 @@ public <T> ExecutableFindByAnalytics<T> findByAnalytics(Class<T> domainType) {
return new ExecutableFindByAnalyticsOperationSupport(this).findByAnalytics(domainType);
}

@Override
public <T> ExecutableFindBySearch<T> findBySearch(Class<T> domainType) {
return new ExecutableFindBySearchOperationSupport(this).findBySearch(domainType);
}

@Override
@Deprecated
public ExecutableRemoveById removeById() {
Expand Down
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));
}
}

/**
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
@@ -0,0 +1,215 @@
/*
* Copyright 2025-present the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.couchbase.core;

import java.util.List;
import java.util.Map;
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.support.InCollection;
import org.springframework.data.couchbase.core.support.InScope;
import org.springframework.data.couchbase.core.support.WithSearchConsistency;
import org.springframework.data.couchbase.core.support.WithSearchOptions;
import org.springframework.data.couchbase.core.support.WithSearchQuery;
import org.jspecify.annotations.Nullable;

import com.couchbase.client.java.search.HighlightStyle;
import com.couchbase.client.java.search.SearchOptions;
import com.couchbase.client.java.search.SearchRequest;
import com.couchbase.client.java.search.SearchScanConsistency;
import com.couchbase.client.java.search.facet.SearchFacet;
import com.couchbase.client.java.search.result.SearchRow;
import com.couchbase.client.java.search.sort.SearchSort;

/**
* Full-Text Search (FTS) Operations (Blocking)
*
* @author Emilien Bevierre
* @since 6.2
*/
public interface ExecutableFindBySearchOperation {

/**
* Queries the Full-Text Search (FTS) service.
*
* @param domainType the entity type to use for the results.
*/
<T> ExecutableFindBySearch<T> findBySearch(Class<T> domainType);

interface TerminatingFindBySearch<T> {

/**
* Get exactly zero or one result.
*
* @return {@link Optional#empty()} if no match found.
* @throws IncorrectResultSizeDataAccessException if more than one match found.
*/
default Optional<T> one() {
return Optional.ofNullable(oneValue());
}

/**
* Get exactly zero or one result.
*
* @return {@literal null} if no match found.
* @throws IncorrectResultSizeDataAccessException if more than one match found.
*/
@Nullable
T oneValue();

/**
* Get the first or no result.
*
* @return {@link Optional#empty()} if no match found.
*/
default Optional<T> first() {
return Optional.ofNullable(firstValue());
}

/**
* Get the first or no result.
*
* @return {@literal null} if no match found.
*/
@Nullable
T firstValue();

/**
* Get all matching elements, hydrated as entities via KV GET.
* <p>
* If no limit is specified via {@code withLimit(...)} or {@code withOptions(...)}, a default limit of 10,000 is
* applied (the FTS service would otherwise return only its default of 10 hits).
*
* @return never {@literal null}.
*/
List<T> all();

/**
* Stream all matching elements.
*
* @return a {@link Stream} of results. Never {@literal null}.
*/
Stream<T> stream();

/**
* Get the number of matching elements.
*
* @return total number of matching elements.
*/
long count();

/**
* Check for the presence of matching elements.
*
* @return {@literal true} if at least one matching element exists.
*/
boolean exists();

/**
* Get raw FTS search rows (without entity hydration).
*
* @return never {@literal null}.
*/
List<SearchRow> rows();

/**
* Get a combined result including hydrated entities, raw rows, metadata, and facet results.
*
* @return a {@link SearchResult} containing the full response.
*/
SearchResult<T> result();
}

interface FindBySearchWithQuery<T> extends TerminatingFindBySearch<T>, WithSearchQuery<T> {
@Override
TerminatingFindBySearch<T> matching(SearchRequest searchRequest);
}

interface FindBySearchWithOptions<T> extends FindBySearchWithQuery<T>, WithSearchOptions<T> {
@Override
FindBySearchWithQuery<T> withOptions(SearchOptions options);
}

interface FindBySearchInCollection<T> extends FindBySearchWithOptions<T>, InCollection<T> {
@Override
FindBySearchWithOptions<T> inCollection(String collection);
}

interface FindBySearchInScope<T> extends FindBySearchInCollection<T>, InScope<T> {
@Override
FindBySearchInCollection<T> inScope(String scope);
}

interface FindBySearchWithConsistency<T> extends FindBySearchInScope<T>, WithSearchConsistency<T> {
@Override
FindBySearchInScope<T> withConsistency(SearchScanConsistency scanConsistency);
}

interface FindBySearchWithLimit<T> extends FindBySearchWithConsistency<T> {
FindBySearchWithConsistency<T> withLimit(int limit);
}

interface FindBySearchWithSkip<T> extends FindBySearchWithLimit<T> {
FindBySearchWithLimit<T> withSkip(int skip);
}

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> {
FindBySearchWithSort<T> withHighlight(HighlightStyle style, String... fields);

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> {
FindBySearchWithHighlight<T> withFacets(Map<String, SearchFacet> facets);
}

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

<P> FindBySearchWithFacets<T> withFields(TypedPropertyPath<P, ?> field,
TypedPropertyPath<P, ?>... additionalFields);
}

interface FindBySearchWithProjection<T> extends FindBySearchWithFields<T> {
<R> FindBySearchWithFields<R> as(Class<R> returnType);
}

interface FindBySearchWithIndex<T> extends FindBySearchWithProjection<T> {
FindBySearchWithProjection<T> withIndex(String indexName);
}

interface ExecutableFindBySearch<T> extends FindBySearchWithIndex<T> {}
}
Loading
Loading