Skip to content

DATACOUCH-1066 - Make QueryCriteria key a N1qlExpression. #1082

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
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
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors
* Copyright 2012-2021 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.
Expand All @@ -15,25 +15,29 @@
*/
package org.springframework.data.couchbase.core.query;

import static org.springframework.data.couchbase.core.query.N1QLExpression.x;

import java.util.ArrayList;
import java.util.Formatter;
import java.util.LinkedList;
import java.util.List;

import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.lang.Nullable;

import com.couchbase.client.core.error.InvalidArgumentException;
import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.json.JsonValue;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.lang.Nullable;

/**
* @author Michael Nitschinger
* @author Michael Reiche
* @author Mauro Monti
*/
public class QueryCriteria implements QueryCriteriaDefinition {

private final String key;
private final N1QLExpression key;
/**
* Holds the chain itself, the current operator being always the last one.
*/
Expand All @@ -46,16 +50,16 @@ public class QueryCriteria implements QueryCriteriaDefinition {
private Object[] value;
private String format;

QueryCriteria(List<QueryCriteria> chain, String key, Object[] value, ChainOperator chainOperator) {
QueryCriteria(List<QueryCriteria> chain, N1QLExpression key, Object[] value, ChainOperator chainOperator) {
this(chain, key, value, chainOperator, null, null);
}

QueryCriteria(List<QueryCriteria> chain, String key, Object value, ChainOperator chainOperator) {
QueryCriteria(List<QueryCriteria> chain, N1QLExpression key, Object value, ChainOperator chainOperator) {
this(chain, key, new Object[] { value }, chainOperator, null, null);
}

QueryCriteria(List<QueryCriteria> chain, String key, Object[] value, ChainOperator chainOperator, String operator,
String format) {
QueryCriteria(List<QueryCriteria> chain, N1QLExpression key, Object[] value, ChainOperator chainOperator,
String operator, String format) {
this.criteriaChain = chain;
criteriaChain.add(this);
this.key = key;
Expand All @@ -70,9 +74,16 @@ Object[] getValue() {
}

/**
* Static factory method to create a Criteria using the provided key.
* Static factory method to create a Criteria using the provided String key.
*/
public static QueryCriteria where(String key) {
return where(x(key));
}

/**
* Static factory method to create a Criteria using the provided N1QLExpression key.
*/
public static QueryCriteria where(N1QLExpression key) {
return new QueryCriteria(new ArrayList<>(), key, null, null);
}

Expand All @@ -83,21 +94,29 @@ private static QueryCriteria wrap(QueryCriteria criteria) {
}

public QueryCriteria and(String key) {
return and(x(key));
}

public QueryCriteria and(N1QLExpression key) {
return new QueryCriteria(this.criteriaChain, key, null, ChainOperator.AND);
}

public QueryCriteria and(QueryCriteria criteria) {
return new QueryCriteria(this.criteriaChain, null, criteria, ChainOperator.AND);
}

public QueryCriteria or(QueryCriteria criteria) {
return new QueryCriteria(this.criteriaChain, null, criteria, ChainOperator.OR);
public QueryCriteria or(String key) {
return or(x(key));
}

public QueryCriteria or(String key) {
public QueryCriteria or(N1QLExpression key) {
return new QueryCriteria(this.criteriaChain, key, null, ChainOperator.OR);
}

public QueryCriteria or(QueryCriteria criteria) {
return new QueryCriteria(this.criteriaChain, null, criteria, ChainOperator.OR);
}

public QueryCriteria eq(@Nullable Object o) {
return is(o);
}
Expand Down Expand Up @@ -343,7 +362,7 @@ public String export() { // used only by tests
*/
private StringBuilder exportSingle(StringBuilder sb, int[] paramIndexPtr, JsonValue parameters,
CouchbaseConverter converter) {
String fieldName = maybeBackTic(key);
String fieldName = key == null ? null : key.toString(); // maybeBackTic(key);
int valueLen = value == null ? 0 : value.length;
Object[] v = new Object[valueLen + 2];
v[0] = fieldName;
Expand Down Expand Up @@ -377,7 +396,7 @@ private StringBuilder exportSingle(StringBuilder sb, int[] paramIndexPtr, JsonVa
* @param parameters - parameters of the query. If operands are parameterized, their values are added to parameters
* @return string containing part of N1QL query
*/
private String maybeWrapValue(String key, Object value, int[] paramIndexPtr, JsonValue parameters,
private String maybeWrapValue(N1QLExpression key, Object value, int[] paramIndexPtr, JsonValue parameters,
CouchbaseConverter converter) {
if (paramIndexPtr != null) {
if (paramIndexPtr[0] >= 0) {
Expand All @@ -397,10 +416,10 @@ private String maybeWrapValue(String key, Object value, int[] paramIndexPtr, Jso
JsonObject params = (JsonObject) parameters;
// from StringBasedN1qlQueryParser.getNamedPlaceholderValues()
try {
params.put(key, convert(converter, value));
params.put(key.toString(), convert(converter, value));
} catch (InvalidArgumentException iae) {
if (value instanceof Object[]) {
params.put(key, JsonArray.from((Object[]) value));
params.put(key.toString(), JsonArray.from((Object[]) value));
} else {
throw iae;
}
Expand All @@ -416,7 +435,7 @@ private String maybeWrapValue(String key, Object value, int[] paramIndexPtr, Jso
} else if (value == null) {
return "null";
} else if (value instanceof Object[]) { // convert array into sequence of comma-separated values
StringBuffer l = new StringBuffer();
StringBuilder l = new StringBuilder();
l.append("[");
Object[] array = (Object[]) value;
for (int i = 0; i < array.length; i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ public Pageable first() {
return delegate.first();
}

@Override
public Pageable withPage(int i) {
return new CountPageable(delegate.withPage(i));
}

public boolean hasPrevious() {
return delegate.hasPrevious();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors
* Copyright 2012-2021 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.
Expand All @@ -15,13 +15,18 @@
*/
package org.springframework.data.couchbase.repository.query;

import static org.springframework.data.couchbase.core.query.N1QLExpression.i;
import static org.springframework.data.couchbase.core.query.N1QLExpression.meta;
import static org.springframework.data.couchbase.core.query.N1QLExpression.path;
import static org.springframework.data.couchbase.core.query.N1QLExpression.x;
import static org.springframework.data.couchbase.core.query.QueryCriteria.where;

import java.util.Iterator;

import org.springframework.core.convert.converter.Converter;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.query.N1QLExpression;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.QueryCriteria;
import org.springframework.data.domain.Sort;
Expand All @@ -36,33 +41,39 @@
/**
* @author Michael Nitschinger
* @author Michael Reiche
* @author Mauro Monti
*/
public class N1qlQueryCreator extends AbstractQueryCreator<Query, QueryCriteria> {

private static final String META_ID_PROPERTY = "id";
private static final String META_CAS_PROPERTY = "cas";

private final ParameterAccessor accessor;
private final MappingContext<?, CouchbasePersistentProperty> context;
private final QueryMethod queryMethod;
private final CouchbaseConverter converter;
private final String bucketName;

public N1qlQueryCreator(final PartTree tree, final ParameterAccessor accessor, QueryMethod queryMethod,
CouchbaseConverter converter) {
public N1qlQueryCreator(final PartTree tree, final ParameterAccessor accessor, final QueryMethod queryMethod,
final CouchbaseConverter converter, final String bucketName) {
super(tree, accessor);
this.accessor = accessor;
this.context = converter.getMappingContext();
this.queryMethod = queryMethod;
this.converter = converter;
this.bucketName = bucketName;
}

@Override
protected QueryCriteria create(final Part part, final Iterator<Object> iterator) {
PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
CouchbasePersistentProperty property = path.getLeafProperty();
return from(part, property, where(path.toDotPath(cvtr)), iterator);
return from(part, property, where(addMetaIfRequired(path, property)), iterator);
}

static Converter<? super CouchbasePersistentProperty, String> cvtr = (
source) -> new StringBuilder(source.getName().length() + 2).append('`').append(source.getName()).append('`')
.toString();
source) -> new StringBuilder(source.getFieldName().length() + 2).append('`').append(source.getFieldName())
.append('`').toString();

@Override
protected QueryCriteria and(final Part part, final QueryCriteria base, final Iterator<Object> iterator) {
Expand All @@ -73,7 +84,7 @@ protected QueryCriteria and(final Part part, final QueryCriteria base, final Ite
PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
CouchbasePersistentProperty property = path.getLeafProperty();

return from(part, property, base.and(path.toDotPath()), iterator);
return from(part, property, base.and(addMetaIfRequired(path, property)), iterator);
}

@Override
Expand Down Expand Up @@ -149,4 +160,16 @@ private QueryCriteria from(final Part part, final CouchbasePersistentProperty pr
}
}

private N1QLExpression addMetaIfRequired(
final PersistentPropertyPath<CouchbasePersistentProperty> persistentPropertyPath,
final CouchbasePersistentProperty property) {
if (property.isIdProperty()) {
return path(meta(i(bucketName)), i(META_ID_PROPERTY));
}
if (property.isVersionProperty()) {
return path(meta(i(bucketName)), i(META_CAS_PROPERTY));
}
return x(persistentPropertyPath.toDotPath(cvtr));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public Object execute(final Object[] parameters) {
SPEL_PARSER, evaluationContextProvider, namedQueries).createQuery();
} else {
final PartTree tree = new PartTree(queryMethod.getName(), domainClass);
query = new N1qlQueryCreator(tree, accessor, queryMethod, operations.getConverter()).createQuery();
query = new N1qlQueryCreator(tree, accessor, queryMethod, operations.getConverter(), operations.getBucketName()).createQuery();
}

ExecutableFindByQueryOperation.ExecutableFindByQuery<?> operation = (ExecutableFindByQueryOperation.ExecutableFindByQuery<?>) operations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ public PartTree getTree() {
@Override
protected Query createQuery(ParametersParameterAccessor accessor) {

N1qlQueryCreator creator = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter);
N1qlQueryCreator creator = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter,
getOperations().getBucketName());
Query query = creator.createQuery();

if (tree.isLimiting()) {
Expand All @@ -88,7 +89,8 @@ protected Query createQuery(ParametersParameterAccessor accessor) {
*/
@Override
protected Query createCountQuery(ParametersParameterAccessor accessor) {
return new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter).createQuery();
return new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter, getOperations().getBucketName())
.createQuery();
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ public PartTree getTree() {
@Override
protected Query createQuery(ParametersParameterAccessor accessor) {

N1qlQueryCreator creator = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter);
N1qlQueryCreator creator = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter,
getOperations().getBucketName());
Query query = creator.createQuery();

if (tree.isLimiting()) {
Expand All @@ -91,7 +92,8 @@ protected Query createQuery(ParametersParameterAccessor accessor) {
*/
@Override
protected Query createCountQuery(ParametersParameterAccessor accessor) {
Query query = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter).createQuery();
Query query = new N1qlQueryCreator(tree, accessor, getQueryMethod(), converter, getOperations().getBucketName())
.createQuery();
if (LOG.isDebugEnabled()) {
LOG.debug("Created query {} for * fields.", query.export());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ protected Query createQuery(ParametersParameterAccessor accessor) {
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(accessor, getQueryMethod(),
getOperations().getConverter(), getOperations().getBucketName(), expressionParser, evaluationContextProvider,
namedQueries);

Query query = creator.createQuery();

if (LOG.isDebugEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2020 the original author or authors
* Copyright 2021 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.
Expand All @@ -15,9 +15,11 @@
*/
package org.springframework.data.couchbase.repository.query;

import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.json.JsonValue;
import static org.springframework.data.couchbase.core.query.N1QLExpression.x;
import static org.springframework.data.couchbase.core.query.QueryCriteria.where;

import java.util.Iterator;

import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
import org.springframework.data.couchbase.core.query.N1QLExpression;
Expand All @@ -36,12 +38,13 @@
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.expression.spel.standard.SpelExpressionParser;

import java.util.Iterator;

import static org.springframework.data.couchbase.core.query.QueryCriteria.where;
import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.json.JsonValue;

/**
* @author Michael Reiche
* @author Mauro Monti
*/
public class StringN1qlQueryCreator extends AbstractQueryCreator<Query, QueryCriteria> {

Expand Down Expand Up @@ -98,10 +101,9 @@ protected String getTypeValue() {

@Override
protected QueryCriteria create(final Part part, final Iterator<Object> iterator) {
PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(
part.getProperty());
PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
CouchbasePersistentProperty property = path.getLeafProperty();
return from(part, property, where(path.toDotPath()), iterator);
return from(part, property, where(x(path.toDotPath())), iterator);
}

@Override
Expand All @@ -110,11 +112,10 @@ protected QueryCriteria and(final Part part, final QueryCriteria base, final Ite
return create(part, iterator);
}

PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(
part.getProperty());
PersistentPropertyPath<CouchbasePersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
CouchbasePersistentProperty property = path.getLeafProperty();

return from(part, property, base.and(path.toDotPath()), iterator);
return from(part, property, base.and(x(path.toDotPath())), iterator);
}

@Override
Expand All @@ -139,10 +140,10 @@ private QueryCriteria from(final Part part, final CouchbasePersistentProperty pr

final Part.Type type = part.getType();
switch (type) {
case SIMPLE_PROPERTY:
return criteria; // this will be the dummy from PartTree
default:
throw new IllegalArgumentException("Unsupported keyword!");
case SIMPLE_PROPERTY:
return criteria; // this will be the dummy from PartTree
default:
throw new IllegalArgumentException("Unsupported keyword!");
}
}

Expand Down
Loading