Skip to content

Add project(fields) to findByQuery similar to same method on findById. #1212

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 13, 2021
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 @@ -221,7 +221,7 @@ interface FindByQueryConsistentWith<T> extends FindByQueryInScope<T> {
}

/**
* Fluent method to specify scan consistency. Scan consistency may also come from an annotation.
* Fluent method to specify scan consistency. Scan consistency may also come from an annotation.
*
* @param <T> the entity type to use for the results.
*/
Expand Down Expand Up @@ -254,12 +254,30 @@ interface FindByQueryWithProjection<T> extends FindByQueryWithConsistency<T> {
<R> FindByQueryWithConsistency<R> as(Class<R> returnType);
}

/**
* Fluent method to specify fields to project.
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithProjecting<T> extends FindByQueryWithProjection<T> {

/**
* Define the target type fields should be mapped to. <br />
* Skip this step if you are anyway only interested in the original domain type.
*
* @param fields to project
* @return new instance of {@link ReactiveFindByQueryOperation.FindByQueryWithConsistency}.
* @throws IllegalArgumentException if returnType is {@literal null}.
*/
FindByQueryWithProjection<T> project(String[] fields);
}

/**
* Fluent method to specify DISTINCT fields
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjection<T>, WithDistinct<T> {
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjecting<T>, WithDistinct<T> {

/**
* Finds the distinct values for a specified {@literal field} across a single collection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.util.stream.Stream;

import org.springframework.data.couchbase.core.ReactiveFindByQueryOperationSupport.ReactiveFindByQuerySupport;
import org.springframework.data.couchbase.core.CouchbaseQueryExecutionException;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.util.Assert;

Expand All @@ -45,7 +44,7 @@ public ExecutableFindByQueryOperationSupport(final CouchbaseTemplate template) {
@Override
public <T> ExecutableFindByQuery<T> findByQuery(final Class<T> domainType) {
return new ExecutableFindByQuerySupport<T>(template, domainType, domainType, ALL_QUERY, null, null, null, null,
null);
null, null);
}

static class ExecutableFindByQuerySupport<T> implements ExecutableFindByQuery<T> {
Expand All @@ -60,22 +59,24 @@ static class ExecutableFindByQuerySupport<T> implements ExecutableFindByQuery<T>
private final String collection;
private final QueryOptions options;
private final String[] distinctFields;
private final String[] fields;

ExecutableFindByQuerySupport(final CouchbaseTemplate template, final Class<?> domainType, final Class<T> returnType,
final Query query, final QueryScanConsistency scanConsistency, final String scope, final String collection,
final QueryOptions options, final String[] distinctFields) {
final QueryOptions options, final String[] distinctFields, final String[] fields) {
this.template = template;
this.domainType = domainType;
this.returnType = returnType;
this.query = query;
this.reactiveSupport = new ReactiveFindByQuerySupport<T>(template.reactive(), domainType, returnType, query,
scanConsistency, scope, collection, options, distinctFields,
scanConsistency, scope, collection, options, distinctFields, fields,
new NonReactiveSupportWrapper(template.support()));
this.scanConsistency = scanConsistency;
this.scope = scope;
this.collection = collection;
this.options = options;
this.distinctFields = distinctFields;
this.fields = fields;
}

@Override
Expand All @@ -102,38 +103,47 @@ public TerminatingFindByQuery<T> matching(final Query query) {
scanCons = scanConsistency;
}
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanCons, scope, collection,
options, distinctFields);
options, distinctFields, fields);
}

@Override
@Deprecated
public FindByQueryInScope<T> consistentWith(final QueryScanConsistency scanConsistency) {
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields);
collection, options, distinctFields, fields);
}

@Override
public FindByQueryConsistentWith<T> withConsistency(final QueryScanConsistency scanConsistency) {
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields);
collection, options, distinctFields, fields);
}

@Override
public <R> FindByQueryWithConsistency<R> as(final Class<R> returnType) {
Assert.notNull(returnType, "returnType must not be null!");
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields);
collection, options, distinctFields, fields);
}

@Override
public FindByQueryWithProjection<T> project(String[] fields) {
Assert.notNull(fields, "Fields must not be null");
Assert.isNull(distinctFields, "only one of project(fields) and distinct(distinctFields) can be specified");
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, fields);
}

@Override
public FindByQueryWithProjection<T> distinct(final String[] distinctFields) {
Assert.notNull(distinctFields, "distinctFields must not be null!");
Assert.notNull(distinctFields, "distinctFields must not be null");
Assert.isNull(fields, "only one of project(fields) and distinct(distinctFields) can be specified");
// Coming from an annotation, this cannot be null.
// But a non-null but empty distinctFields means distinct on all fields
// So to indicate do not use distinct, we use {"-"} from the annotation, and here we change it to null.
String[] dFields = distinctFields.length == 1 && "-".equals(distinctFields[0]) ? null : distinctFields;
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, dFields);
collection, options, dFields, fields);
}

@Override
Expand All @@ -144,8 +154,8 @@ public Stream<T> stream() {
@Override
public long count() {
Long l = reactiveSupport.count().block();
if ( l == null ){
throw new CouchbaseQueryExecutionException("count query did not return a count : "+query.export());
if (l == null) {
throw new CouchbaseQueryExecutionException("count query did not return a count : " + query.export());
}
return l;
}
Expand All @@ -159,19 +169,19 @@ public boolean exists() {
public TerminatingFindByQuery<T> withOptions(final QueryOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields);
collection, options, distinctFields, fields);
}

@Override
public FindByQueryInCollection<T> inScope(final String scope) {
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields);
collection, options, distinctFields, fields);
}

@Override
public FindByQueryWithConsistency<T> inCollection(final String collection) {
return new ExecutableFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields);
collection, options, distinctFields, fields);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,12 +201,30 @@ interface FindByQueryWithProjection<T> extends FindByQueryWithConsistency<T> {
<R> FindByQueryWithConsistency<R> as(Class<R> returnType);
}

/**
* Fluent method to specify fields to project.
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithProjecting<T> extends FindByQueryWithProjection<T> {

/**
* Define the target type fields should be mapped to. <br />
* Skip this step if you are anyway only interested in the original domain type.
*
* @param fields to project
* @return new instance of {@link FindByQueryWithConsistency}.
* @throws IllegalArgumentException if returnType is {@literal null}.
*/
FindByQueryWithProjection<T> project(String[] fields);
}

/**
* Fluent method to specify DISTINCT fields
*
* @param <T> the entity type to use for the results.
*/
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjection<T>, WithDistinct<T> {
interface FindByQueryWithDistinct<T> extends FindByQueryWithProjecting<T>, WithDistinct<T> {

/**
* Finds the distinct values for a specified {@literal field} across a single {@link } or view.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public ReactiveFindByQueryOperationSupport(final ReactiveCouchbaseTemplate templ
@Override
public <T> ReactiveFindByQuery<T> findByQuery(final Class<T> domainType) {
return new ReactiveFindByQuerySupport<>(template, domainType, domainType, ALL_QUERY, null, null, null, null, null,
template.support());
null, template.support());
}

static class ReactiveFindByQuerySupport<T> implements ReactiveFindByQuery<T> {
Expand All @@ -63,12 +63,13 @@ static class ReactiveFindByQuerySupport<T> implements ReactiveFindByQuery<T> {
private final String collection;
private final String scope;
private final String[] distinctFields;
private final String[] fields;
private final QueryOptions options;
private final ReactiveTemplateSupport support;

ReactiveFindByQuerySupport(final ReactiveCouchbaseTemplate template, final Class<?> domainType,
final Class<T> returnType, final Query query, final QueryScanConsistency scanConsistency, final String scope,
final String collection, final QueryOptions options, final String[] distinctFields,
final String collection, final QueryOptions options, final String[] distinctFields, final String[] fields,
final ReactiveTemplateSupport support) {
Assert.notNull(domainType, "domainType must not be null!");
Assert.notNull(returnType, "returnType must not be null!");
Expand All @@ -81,6 +82,7 @@ static class ReactiveFindByQuerySupport<T> implements ReactiveFindByQuery<T> {
this.collection = collection;
this.options = options;
this.distinctFields = distinctFields;
this.fields = fields;
this.support = support;
}

Expand All @@ -94,57 +96,65 @@ public FindByQueryWithQuery<T> matching(Query query) {
scanCons = scanConsistency;
}
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanCons, scope, collection,
options, distinctFields, support);
options, distinctFields, fields, support);
}

@Override
public TerminatingFindByQuery<T> withOptions(final QueryOptions options) {
Assert.notNull(options, "Options must not be null.");
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, support);
collection, options, distinctFields, fields, support);
}

@Override
public FindByQueryInCollection<T> inScope(final String scope) {
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, support);
collection, options, distinctFields, fields, support);
}

@Override
public FindByQueryWithConsistency<T> inCollection(final String collection) {
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, support);
collection, options, distinctFields, fields, support);
}

@Override
@Deprecated
public FindByQueryConsistentWith<T> consistentWith(QueryScanConsistency scanConsistency) {
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, support);
collection, options, distinctFields, fields, support);
}

@Override
public FindByQueryWithConsistency<T> withConsistency(QueryScanConsistency scanConsistency) {
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, support);
collection, options, distinctFields, fields, support);
}

@Override
public <R> FindByQueryWithConsistency<R> as(Class<R> returnType) {
Assert.notNull(returnType, "returnType must not be null!");
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, support);
collection, options, distinctFields, fields, support);
}

@Override
public FindByQueryWithProjection<T> project(String[] fields) {
Assert.notNull(fields, "Fields must not be null");
Assert.isNull(distinctFields, "only one of project(fields) and distinct(distinctFields) can be specified");
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, distinctFields, fields, support);
}

@Override
public FindByQueryWithDistinct<T> distinct(final String[] distinctFields) {
Assert.notNull(distinctFields, "distinctFields must not be null!");
Assert.notNull(distinctFields, "distinctFields must not be null");
Assert.isNull(fields, "only one of project(fields) and distinct(distinctFields) can be specified");
// Coming from an annotation, this cannot be null.
// But a non-null but empty distinctFields means distinct on all fields
// So to indicate do not use distinct, we use {"-"} from the annotation, and here we change it to null.
String[] dFields = distinctFields.length == 1 && "-".equals(distinctFields[0]) ? null : distinctFields;
return new ReactiveFindByQuerySupport<>(template, domainType, returnType, query, scanConsistency, scope,
collection, options, dFields, support);
collection, options, dFields, fields, support);
}

@Override
Expand Down Expand Up @@ -228,7 +238,7 @@ public Mono<Boolean> exists() {

private String assembleEntityQuery(final boolean count, String[] distinctFields, String collection) {
return query.toN1qlSelectString(template, collection, this.domainType, this.returnType, count,
query.getDistinctFields() != null ? query.getDistinctFields() : distinctFields);
query.getDistinctFields() != null ? query.getDistinctFields() : distinctFields, fields);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -320,13 +320,13 @@ public String export(int[]... paramIndexPtrHolder) { // used only by tests
}

public String toN1qlSelectString(ReactiveCouchbaseTemplate template, Class domainClass, boolean isCount) {
return toN1qlSelectString(template, null, domainClass, null, isCount, null);
return toN1qlSelectString(template, null, domainClass, null, isCount, null, null);
}

public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String collectionName, Class domainClass,
Class returnClass, boolean isCount, String[] distinctFields) {
Class returnClass, boolean isCount, String[] distinctFields, String[] fields) {
StringBasedN1qlQueryParser.N1qlSpelValues n1ql = getN1qlSpelValues(template, collectionName, domainClass,
returnClass, isCount, distinctFields);
returnClass, isCount, distinctFields, fields);
final StringBuilder statement = new StringBuilder();
appendString(statement, n1ql.selectEntity); // select ...
appendWhereString(statement, n1ql.filter); // typeKey = typeValue
Expand All @@ -340,7 +340,7 @@ public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String coll

public String toN1qlRemoveString(ReactiveCouchbaseTemplate template, String collectionName, Class domainClass) {
StringBasedN1qlQueryParser.N1qlSpelValues n1ql = getN1qlSpelValues(template, collectionName, domainClass, null,
false, null);
false, null, null);
final StringBuilder statement = new StringBuilder();
appendString(statement, n1ql.delete); // delete ...
appendWhereString(statement, n1ql.filter); // typeKey = typeValue
Expand All @@ -350,7 +350,7 @@ public String toN1qlRemoveString(ReactiveCouchbaseTemplate template, String coll
}

StringBasedN1qlQueryParser.N1qlSpelValues getN1qlSpelValues(ReactiveCouchbaseTemplate template, String collectionName,
Class domainClass, Class returnClass, boolean isCount, String[] distinctFields) {
Class domainClass, Class returnClass, boolean isCount, String[] distinctFields, String[] fields) {
String typeKey = template.getConverter().getTypeKey();
final CouchbasePersistentEntity<?> persistentEntity = template.getConverter().getMappingContext()
.getRequiredPersistentEntity(domainClass);
Expand All @@ -363,7 +363,7 @@ StringBasedN1qlQueryParser.N1qlSpelValues getN1qlSpelValues(ReactiveCouchbaseTem
}

StringBasedN1qlQueryParser sbnqp = new StringBasedN1qlQueryParser(template.getBucketName(), collectionName,
template.getConverter(), domainClass, returnClass, typeKey, typeValue, distinctFields);
template.getConverter(), domainClass, returnClass, typeKey, typeValue, isCount, distinctFields, fields);
return isCount ? sbnqp.getCountContext() : sbnqp.getStatementContext();
}

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

import java.util.Locale;

import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
import org.springframework.data.couchbase.core.support.TemplateUtils;

import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.json.JsonValue;
import org.springframework.data.couchbase.core.support.TemplateUtils;

import java.util.Locale;

/**
* Query created from the string in @Query annotation in the repository interface.
Expand Down Expand Up @@ -56,7 +56,7 @@ private void appendInlineN1qlStatement(final StringBuilder sb) {

@Override
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String collection, Class domainClass,
Class resultClass, boolean isCount, String[] distinctFields) {
Class resultClass, boolean isCount, String[] distinctFields, String[] fields) {
final StringBuilder statement = new StringBuilder();
boolean makeCount = isCount && inlineN1qlQuery != null
&& !inlineN1qlQuery.toLowerCase(Locale.ROOT).contains("count(");
Expand Down Expand Up @@ -95,6 +95,6 @@ public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String coll
*/
@Override
public String toN1qlRemoveString(ReactiveCouchbaseTemplate template, String collectionName, Class domainClass) {
return toN1qlSelectString(template, collectionName, domainClass, domainClass, false, null);
return toN1qlSelectString(template, collectionName, domainClass, domainClass, false, null, null);
}
}
Loading