DATACOUCH-137 - queryDerivation for N1QL annotated methods.
Abstracted the N1QL-based variants in AbstractN1qlBasedQuery. This implementation doesn't recognize QueryParams or QueryPlan anymore in the method parameters so that the method signatures are not store-dependent. Added query derivation from PartTree to N1QL query, using N1qlQueryCreator. Unit tested the mapping between a Part.Type and the corresponding N1QL Expression.
This commit is contained in:
@@ -18,6 +18,8 @@ package org.springframework.data.couchbase.config;
|
||||
|
||||
import org.springframework.beans.factory.xml.NamespaceHandler;
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
import org.springframework.data.couchbase.repository.config.CouchbaseRepositoryConfigurationExtension;
|
||||
import org.springframework.data.repository.config.RepositoryBeanDefinitionParser;
|
||||
|
||||
/**
|
||||
* {@link NamespaceHandler} for Couchbase configuration.
|
||||
@@ -33,7 +35,8 @@ public class CouchbaseNamespaceHandler extends NamespaceHandlerSupport {
|
||||
* Register bean definition parsers in the namespace handler.
|
||||
*/
|
||||
public final void init() {
|
||||
//TODO repositories (CouchbaseRepositoryConfigurationExtension and RepositoryBeanDefinitionParser)
|
||||
CouchbaseRepositoryConfigurationExtension extension = new CouchbaseRepositoryConfigurationExtension();
|
||||
registerBeanDefinitionParser("repositories", new RepositoryBeanDefinitionParser(extension));
|
||||
registerBeanDefinitionParser("env", new CouchbaseEnvironmentParser());
|
||||
registerBeanDefinitionParser("cluster", new CouchbaseClusterParser());
|
||||
registerBeanDefinitionParser("bucket", new CouchbaseBucketParser());
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package org.springframework.data.couchbase.core.mapping;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.context.PersistentPropertyPath;
|
||||
|
||||
/**
|
||||
* Represents a property part of an entity that needs to be persisted.
|
||||
@@ -32,4 +34,16 @@ public interface CouchbasePersistentProperty extends PersistentProperty<Couchbas
|
||||
*/
|
||||
String getFieldName();
|
||||
|
||||
/**
|
||||
* A converter that can be used to extract the {@link #getFieldName() fieldName}, eg. when one wants
|
||||
* a path from {@link PersistentPropertyPath#toDotPath(Converter)} made of field names.
|
||||
*/
|
||||
Converter<? super CouchbasePersistentProperty,String> FIELD_NAME = new Converter<CouchbasePersistentProperty, String>() {
|
||||
@Override
|
||||
public String convert(CouchbasePersistentProperty source) {
|
||||
return source.getFieldName();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -16,13 +16,10 @@
|
||||
|
||||
package org.springframework.data.couchbase.repository.query;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.couchbase.client.java.document.json.JsonArray;
|
||||
import com.couchbase.client.java.query.Query;
|
||||
import com.couchbase.client.java.query.QueryParams;
|
||||
import com.couchbase.client.java.query.QueryPlan;
|
||||
import com.couchbase.client.java.query.Statement;
|
||||
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
@@ -38,7 +35,7 @@ import org.springframework.data.util.StreamUtils;
|
||||
*/
|
||||
public abstract class AbstractN1qlBasedQuery implements RepositoryQuery {
|
||||
|
||||
private final CouchbaseQueryMethod queryMethod;
|
||||
protected final CouchbaseQueryMethod queryMethod;
|
||||
private final CouchbaseOperations couchbaseOperations;
|
||||
|
||||
protected AbstractN1qlBasedQuery(CouchbaseQueryMethod queryMethod, CouchbaseOperations couchbaseOperations) {
|
||||
@@ -46,45 +43,28 @@ public abstract class AbstractN1qlBasedQuery implements RepositoryQuery {
|
||||
this.couchbaseOperations = couchbaseOperations;
|
||||
}
|
||||
|
||||
protected abstract Statement getStatement();
|
||||
protected abstract Statement getStatement(ParameterAccessor accessor);
|
||||
|
||||
protected abstract JsonArray getPlaceholderValues(ParameterAccessor accessor);
|
||||
|
||||
@Override
|
||||
public Object execute(Object[] parameters) {
|
||||
Statement statement = getStatement();
|
||||
ParameterAccessor accessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
|
||||
Statement statement = getStatement(accessor);
|
||||
JsonArray queryPlaceholderValues = getPlaceholderValues(accessor);
|
||||
|
||||
ParameterAccessor parameterAccessor = new ParametersParameterAccessor(queryMethod.getParameters(), parameters);
|
||||
Query query = buildQuery(statement, parameterAccessor.iterator());
|
||||
Query query = buildQuery(statement, queryPlaceholderValues);
|
||||
return executeDependingOnType(query, queryMethod, queryMethod.isPageQuery(), queryMethod.isModifyingQuery(),
|
||||
queryMethod.isSliceQuery());
|
||||
}
|
||||
|
||||
protected static Query buildQuery(Statement statement, Iterator<Object> paramIterator) {
|
||||
JsonArray queryValues = JsonArray.create();
|
||||
QueryParams queryParams = null;
|
||||
QueryPlan preparedPayload = null;
|
||||
|
||||
while (paramIterator.hasNext()) {
|
||||
Object next = paramIterator.next();
|
||||
if (next instanceof QueryParams) {
|
||||
queryParams = (QueryParams) next;
|
||||
}
|
||||
else if (next instanceof QueryPlan) {
|
||||
preparedPayload = (QueryPlan) next;
|
||||
}
|
||||
else {
|
||||
queryValues.add(next);
|
||||
}
|
||||
}
|
||||
|
||||
protected static Query buildQuery(Statement statement, JsonArray queryPlaceholderValues) {
|
||||
Query query;
|
||||
if (preparedPayload != null) {
|
||||
query = Query.prepared(preparedPayload, queryValues.isEmpty() ? null : queryValues, queryParams);
|
||||
}
|
||||
else if (!queryValues.isEmpty()) {
|
||||
query = Query.parametrized(statement, queryValues, queryParams);
|
||||
if (!queryPlaceholderValues.isEmpty()) {
|
||||
query = Query.parametrized(statement, queryPlaceholderValues);
|
||||
}
|
||||
else {
|
||||
query = Query.simple(statement, queryParams);
|
||||
query = Query.simple(statement);
|
||||
}
|
||||
|
||||
return query;
|
||||
@@ -123,4 +103,8 @@ public abstract class AbstractN1qlBasedQuery implements RepositoryQuery {
|
||||
public CouchbaseQueryMethod getQueryMethod() {
|
||||
return this.queryMethod;
|
||||
}
|
||||
|
||||
protected CouchbaseOperations getCouchbaseOperations() {
|
||||
return this.couchbaseOperations;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.data.couchbase.repository.query;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
|
||||
@@ -26,8 +28,6 @@ import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Represents a query method with couchbase extensions, allowing to discover
|
||||
* if View-based query or N1QL-based query must be used.
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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
|
||||
*
|
||||
* http://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.repository.query;
|
||||
|
||||
import static com.couchbase.client.java.query.dsl.Expression.s;
|
||||
import static com.couchbase.client.java.query.dsl.Expression.x;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.couchbase.client.java.document.json.JsonArray;
|
||||
import com.couchbase.client.java.query.dsl.Expression;
|
||||
import com.couchbase.client.java.query.dsl.functions.StringFunctions;
|
||||
import com.couchbase.client.java.query.dsl.path.LimitPath;
|
||||
import com.couchbase.client.java.query.dsl.path.OrderByPath;
|
||||
import com.couchbase.client.java.query.dsl.path.WherePath;
|
||||
|
||||
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.context.PersistentPropertyPath;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
/**
|
||||
* This {@link AbstractQueryCreator Query Creator} is responsible for parsing a {@link PartTree} (representing
|
||||
* a method name) into the WHERE clause of a N1QL query.
|
||||
* <p>
|
||||
* In the following, "field" represents the path in JSON deduced from the part of the method name. "a" and "b" represent
|
||||
* the values of next consumed method parameters. "array" represent a {@link JsonArray} constructed from the next method
|
||||
* parameter value (if a collection or array, contained values are used to fill the array, otherwise it's a single item
|
||||
* array).
|
||||
* <br/>
|
||||
* Here are the {@link Part.Type} supported (<code>field</code>:
|
||||
* <ul>
|
||||
* <li><b>BETWEEN:</b> field BETWEEN a AND b</li>
|
||||
* <li><b>IS_NOT_NULL:</b> field IS NOT NULL</li>
|
||||
* <li><b>IS_NULL:</b> field IS NULL</li>
|
||||
* <li><b>NEGATING_SIMPLE_PROPERTY:</b> - field != a</li>
|
||||
* <li><b>SIMPLE_PROPERTY:</b> - field = a</li>
|
||||
* <li><b>LESS_THAN:</b> field < a</li>
|
||||
* <li><b>LESS_THAN_EQUAL:</b> field <= a</li>
|
||||
* <li><b>GREATER_THAN_EQUAL:</b> field >= a</li>
|
||||
* <li><b>GREATER_THAN:</b> field > a</li>
|
||||
* <li><b>BEFORE:</b> field < a</li>
|
||||
* <li><b>AFTER:</b> field > a</li>
|
||||
* <li><b>NOT_LIKE:</b> field NOT LIKE "a" - a should be a String containing % and _ (matching n and 1 characters)</li>
|
||||
* <li><b>LIKE:</b> field LIKE "a" - a should be a String containing % and _ (matching n and 1 characters)</li>
|
||||
* <li><b>STARTING_WITH:</b> field LIKE "a%" - a should be a String prefix</li>
|
||||
* <li><b>ENDING_WITH:</b> field LIKE "%a" - a should be a String suffix</li>
|
||||
* <li><b>NOT_CONTAINING:</b> field NOT LIKE "%a%" - a should be a String</li>
|
||||
* <li><b>CONTAINING:</b> field LIKE "%a%" - a should be a String</li>
|
||||
* <li><b>NOT_IN:</b> field NOT IN array - note that the next parameter value (or its children if a collection/array)
|
||||
* should be compatible for storage in a {@link JsonArray})</li>
|
||||
* <li><b>IN:</b> field IN array - note that the next parameter value (or its children if a collection/array) should
|
||||
* be compatible for storage in a {@link JsonArray})</li>
|
||||
* <li><b>TRUE:</b> field = TRUE</li>
|
||||
* <li><b>FALSE:</b> field = FALSE</li>
|
||||
* <li><b>REGEX:</b> REGEXP_LIKE(field, "a") - note that the ignoreCase is ignored here, a is a regular expression
|
||||
* in String form</li>
|
||||
* <li><b>EXISTS:</b> field IS NOT MISSING - used to verify that the JSON contains this attribute</li>
|
||||
* </ul>
|
||||
* <br/>
|
||||
* The following are not supported and will throw an {@link IllegalArgumentException} if encountered:
|
||||
* <ul>
|
||||
* <li><b>NEAR, WITHIN:</b> geospatial is not supported in N1QL as of now</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
public class N1qlQueryCreator extends AbstractQueryCreator<LimitPath, Expression> {
|
||||
|
||||
private final ParameterAccessor accessor;
|
||||
private final WherePath selectFrom;
|
||||
private final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> context;
|
||||
|
||||
public N1qlQueryCreator(PartTree tree, ParameterAccessor parameters, WherePath selectFrom,
|
||||
CouchbaseConverter converter) {
|
||||
super(tree, parameters);
|
||||
this.accessor = parameters;
|
||||
this.selectFrom = selectFrom;
|
||||
this.context = converter.getMappingContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Expression create(Part part, Iterator<Object> iterator) {
|
||||
PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
|
||||
return prepareExpression(part, path, iterator);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Expression and(Part part, Expression base, Iterator<Object> iterator) {
|
||||
if (base == null) {
|
||||
return create(part, iterator);
|
||||
}
|
||||
|
||||
return base.and(create(part, iterator));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Expression or(Expression base, Expression criteria) {
|
||||
return base.or(criteria);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LimitPath complete(Expression criteria, Sort sort) {
|
||||
OrderByPath selectFromWhere = selectFrom.where(criteria);
|
||||
|
||||
if (sort != null) {
|
||||
List<com.couchbase.client.java.query.dsl.Sort> cbSortList = new ArrayList<com.couchbase.client.java.query.dsl.Sort>();
|
||||
for (Sort.Order order : sort) {
|
||||
if (order.isAscending()) {
|
||||
cbSortList.add(com.couchbase.client.java.query.dsl.Sort.asc(order.getProperty()));
|
||||
} else {
|
||||
cbSortList.add(com.couchbase.client.java.query.dsl.Sort.desc(order.getProperty()));
|
||||
}
|
||||
}
|
||||
com.couchbase.client.java.query.dsl.Sort[] cbSorts =
|
||||
cbSortList.toArray(new com.couchbase.client.java.query.dsl.Sort[cbSortList.size()]);
|
||||
return selectFromWhere.orderBy(cbSorts);
|
||||
}
|
||||
|
||||
return selectFrom.where(criteria);
|
||||
}
|
||||
|
||||
protected static Expression prepareExpression(Part part, PersistentPropertyPath<CouchbasePersistentProperty> path,
|
||||
Iterator<Object> parameterValues) {
|
||||
//FIXME use conversions for the parameters and types
|
||||
//get the whole doted path with fieldNames instead of potentially wrong propNames
|
||||
String fieldNamePath = path.toDotPath(CouchbasePersistentProperty.FIELD_NAME);
|
||||
|
||||
//deal with ignore case
|
||||
boolean ignoreCase = false;
|
||||
Class<?> leafType = path.getLeafProperty().getType();
|
||||
boolean isString = leafType == String.class;
|
||||
if (part.shouldIgnoreCase() == Part.IgnoreCaseType.WHEN_POSSIBLE) {
|
||||
ignoreCase = isString;
|
||||
} else if (part.shouldIgnoreCase() == Part.IgnoreCaseType.ALWAYS) {
|
||||
if (!isString) {
|
||||
throw new IllegalArgumentException(String.format("Part %s must be of type String but was %s", fieldNamePath, leafType));
|
||||
}
|
||||
ignoreCase = true;
|
||||
}
|
||||
|
||||
return createExpression(part.getType(), fieldNamePath, ignoreCase, parameterValues);
|
||||
}
|
||||
|
||||
protected static Expression createExpression(Part.Type partType, String fieldNamePath, boolean ignoreCase, Iterator<Object> parameterValues) {
|
||||
//create the left hand side of the expression, taking ignoreCase into account
|
||||
Expression left = ignoreCase ? StringFunctions.lower(x(fieldNamePath)) : x(fieldNamePath);
|
||||
|
||||
switch (partType) {
|
||||
case BETWEEN:
|
||||
return left.between(leftAndRight(parameterValues, ignoreCase));
|
||||
case IS_NOT_NULL:
|
||||
return left.isNotNull();
|
||||
case IS_NULL:
|
||||
return left.isNull();
|
||||
case NEGATING_SIMPLE_PROPERTY:
|
||||
return left.ne(right(parameterValues, ignoreCase));
|
||||
case SIMPLE_PROPERTY:
|
||||
return left.eq(right(parameterValues, ignoreCase));
|
||||
case BEFORE:
|
||||
case LESS_THAN:
|
||||
return left.lt(right(parameterValues, ignoreCase));
|
||||
case LESS_THAN_EQUAL:
|
||||
return left.lte(right(parameterValues, ignoreCase));
|
||||
case GREATER_THAN_EQUAL:
|
||||
return left.gte(right(parameterValues, ignoreCase));
|
||||
case AFTER:
|
||||
case GREATER_THAN:
|
||||
return left.gt(right(parameterValues, ignoreCase));
|
||||
case NOT_LIKE:
|
||||
return left.notLike(right(parameterValues, ignoreCase));
|
||||
case LIKE:
|
||||
return left.like(right(parameterValues, ignoreCase));
|
||||
case STARTING_WITH:
|
||||
return left.like(like(parameterValues, ignoreCase, false, true));
|
||||
case ENDING_WITH:
|
||||
return left.like(like(parameterValues, ignoreCase, true, false));
|
||||
case NOT_CONTAINING:
|
||||
return left.notLike(like(parameterValues, ignoreCase, true, true));
|
||||
case CONTAINING:
|
||||
return left.like(like(parameterValues, ignoreCase, true, true));
|
||||
case NOT_IN:
|
||||
return left.notIn(rightArray(parameterValues));
|
||||
case IN:
|
||||
return left.in(rightArray(parameterValues));
|
||||
case TRUE:
|
||||
return left.eq(true);
|
||||
case FALSE:
|
||||
return left.eq(false);
|
||||
case REGEX:
|
||||
return regexp(fieldNamePath, parameterValues);
|
||||
case EXISTS:
|
||||
return left.isNotMissing();
|
||||
case WITHIN:
|
||||
case NEAR:
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported keyword");
|
||||
}
|
||||
}
|
||||
|
||||
protected static Expression regexp(String left, Iterator<Object> parameterValues) {
|
||||
//TODO migrate to using the Functions util class when 2.0-dp2 / 2.0 GA
|
||||
Object next = parameterValues.next();
|
||||
String pattern;
|
||||
if (next == null) {
|
||||
pattern = "";
|
||||
} else {
|
||||
pattern = String.valueOf(next);
|
||||
}
|
||||
return x("REGEXP_LIKE(" + left + ", \"" + pattern + "\")");
|
||||
}
|
||||
|
||||
protected static Expression leftAndRight(Iterator<Object> parameterValues, boolean ignoreCase) {
|
||||
return right(parameterValues, ignoreCase).and(right(parameterValues, ignoreCase));
|
||||
}
|
||||
|
||||
protected static Expression like(Iterator<Object> parameterValues, boolean ignoreCase,
|
||||
boolean anyPrefix, boolean anySuffix) {
|
||||
Object next = parameterValues.next();
|
||||
if (next == null) {
|
||||
return Expression.NULL();
|
||||
}
|
||||
|
||||
Expression converted;
|
||||
if (next instanceof String) {
|
||||
String pattern = (String) next;
|
||||
if (anyPrefix) {
|
||||
pattern = "%" + pattern;
|
||||
}
|
||||
if (anySuffix) {
|
||||
pattern = pattern + "%";
|
||||
}
|
||||
converted = s(pattern);
|
||||
|
||||
} else {
|
||||
converted = x(String.valueOf(next));
|
||||
}
|
||||
|
||||
if (ignoreCase) {
|
||||
return StringFunctions.lower(converted);
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
protected static Expression right(Iterator<Object> parameterValues, boolean ignoreCase) {
|
||||
Object next = parameterValues.next();
|
||||
if (next == null) {
|
||||
return Expression.NULL();
|
||||
}
|
||||
|
||||
Expression converted;
|
||||
if (next instanceof String) {
|
||||
converted = s((String) next);
|
||||
} else {
|
||||
converted = x(String.valueOf(next));
|
||||
}
|
||||
|
||||
if (ignoreCase) {
|
||||
return StringFunctions.lower(converted);
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
protected static JsonArray rightArray(Iterator<Object> parameterValues) {
|
||||
Object next = parameterValues.next();
|
||||
|
||||
Object[] values;
|
||||
if (next instanceof Collection) {
|
||||
values = ((Collection<?>) next).toArray();
|
||||
} else if (next.getClass().isArray()) {
|
||||
values = (Object[]) next;
|
||||
} else {
|
||||
values = new Object[] {next};
|
||||
}
|
||||
return JsonArray.from(values);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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
|
||||
*
|
||||
* http://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.repository.query;
|
||||
|
||||
import static com.couchbase.client.java.query.Select.select;
|
||||
import static com.couchbase.client.java.query.dsl.Expression.i;
|
||||
import static com.couchbase.client.java.query.dsl.Expression.path;
|
||||
import static com.couchbase.client.java.query.dsl.functions.AggregateFunctions.count;
|
||||
import static com.couchbase.client.java.query.dsl.functions.MetaFunctions.meta;
|
||||
|
||||
import com.couchbase.client.java.document.json.JsonArray;
|
||||
import com.couchbase.client.java.query.Statement;
|
||||
import com.couchbase.client.java.query.dsl.Expression;
|
||||
import com.couchbase.client.java.query.dsl.path.FromPath;
|
||||
import com.couchbase.client.java.query.dsl.path.LimitPath;
|
||||
import com.couchbase.client.java.query.dsl.path.WherePath;
|
||||
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
public class PartTreeN1qlBasedQuery extends AbstractN1qlBasedQuery {
|
||||
|
||||
private final PartTree partTree;
|
||||
|
||||
public PartTreeN1qlBasedQuery(CouchbaseQueryMethod queryMethod, CouchbaseOperations couchbaseOperations) {
|
||||
super(queryMethod, couchbaseOperations);
|
||||
this.partTree = new PartTree(queryMethod.getName(), queryMethod.getEntityInformation().getJavaType());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JsonArray getPlaceholderValues(ParameterAccessor accessor) {
|
||||
return JsonArray.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Statement getStatement(ParameterAccessor accessor) {
|
||||
Expression bucket = i(getCouchbaseOperations().getCouchbaseBucket().name());
|
||||
Expression metaId = path(meta(bucket), "id").as(CouchbaseOperations.SELECT_ID);
|
||||
Expression metaCas = path(meta(bucket), "cas").as(CouchbaseOperations.SELECT_CAS);
|
||||
|
||||
FromPath select;
|
||||
if (partTree.isCountProjection()) {
|
||||
select = select(count("*"));
|
||||
} else {
|
||||
select = select(metaId, metaCas, path(bucket, "*"));
|
||||
}
|
||||
WherePath selectFrom = select.from(bucket);
|
||||
|
||||
N1qlQueryCreator queryCreator = new N1qlQueryCreator(partTree, accessor, selectFrom, getCouchbaseOperations().getConverter());
|
||||
LimitPath selectFromWhereOrderBy = queryCreator.createQuery();
|
||||
|
||||
if (partTree.isLimiting()) {
|
||||
return selectFromWhereOrderBy.limit(partTree.getMaxResults());
|
||||
} else {
|
||||
return selectFromWhereOrderBy;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
package org.springframework.data.couchbase.repository.query;
|
||||
|
||||
import com.couchbase.client.java.document.json.JsonArray;
|
||||
import com.couchbase.client.java.query.Query;
|
||||
import com.couchbase.client.java.query.Statement;
|
||||
|
||||
import org.springframework.data.couchbase.core.CouchbaseOperations;
|
||||
import org.springframework.data.repository.query.ParameterAccessor;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
|
||||
/**
|
||||
@@ -63,7 +65,17 @@ public class StringN1qlBasedQuery extends AbstractN1qlBasedQuery {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement getStatement() {
|
||||
protected JsonArray getPlaceholderValues(ParameterAccessor accessor) {
|
||||
JsonArray values = JsonArray.create();
|
||||
for (Object value : accessor) {
|
||||
values.add(value);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement getStatement(ParameterAccessor accessor) {
|
||||
return this.statement;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
|
||||
import org.springframework.data.couchbase.repository.query.CouchbaseEntityInformation;
|
||||
import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod;
|
||||
import org.springframework.data.couchbase.repository.query.PartTreeN1qlBasedQuery;
|
||||
import org.springframework.data.couchbase.repository.query.StringN1qlBasedQuery;
|
||||
import org.springframework.data.couchbase.repository.query.ViewBasedCouchbaseQuery;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
@@ -32,6 +33,7 @@ import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.query.EvaluationContextProvider;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -121,7 +123,7 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected QueryLookupStrategy getQueryLookupStrategy(QueryLookupStrategy.Key key) {
|
||||
protected QueryLookupStrategy getQueryLookupStrategy(QueryLookupStrategy.Key key, EvaluationContextProvider contextProvider) {
|
||||
return new CouchbaseQueryLookupStrategy();
|
||||
}
|
||||
|
||||
@@ -141,8 +143,7 @@ public class CouchbaseRepositoryFactory extends RepositoryFactorySupport {
|
||||
String namedQuery = namedQueries.getQuery(namedQueryName);
|
||||
return new StringN1qlBasedQuery(namedQuery, queryMethod, couchbaseOperations);
|
||||
} else {
|
||||
//FIXME return new PartBasedN1qlQuery(queryMethod, couchbaseOperations, namedQueries);
|
||||
return new StringN1qlBasedQuery("SELECT 1", queryMethod, couchbaseOperations);
|
||||
return new PartTreeN1qlBasedQuery(queryMethod, couchbaseOperations);
|
||||
}
|
||||
}
|
||||
return new ViewBasedCouchbaseQuery(queryMethod, couchbaseOperations);
|
||||
|
||||
Reference in New Issue
Block a user