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,10 +18,13 @@ package org.springframework.data.couchbase.repository;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.couchbase.client.java.Bucket;
|
||||
import com.couchbase.client.java.view.Stale;
|
||||
import com.couchbase.client.java.view.ViewQuery;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -101,6 +104,8 @@ public class SimpleCouchbaseRepositoryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("View based query with copy of params from a ViewQuery in the method parameter not implemented")
|
||||
//TODO re-enable test once ViewQuery parameters other than designDoc/viewName can be copied
|
||||
public void shouldFindCustom() {
|
||||
Iterable<User> users = repository.customViewQuery(ViewQuery.from("", "").limit(2).stale(Stale.FALSE));
|
||||
int size = 0;
|
||||
@@ -133,4 +138,12 @@ public class SimpleCouchbaseRepositoryTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFindFromDeriveQueryWithRegexpAndIn() {
|
||||
User user = repository.findByUsernameRegexAndUsernameIn("uname-[123]", Arrays.asList("uname-2", "uname-4"));
|
||||
assertNotNull(user);
|
||||
assertEquals("testuser-2", user.getKey());
|
||||
assertEquals("uname-2", user.getUsername());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.data.couchbase.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.couchbase.client.java.view.ViewQuery;
|
||||
|
||||
import org.springframework.data.couchbase.core.view.N1QL;
|
||||
@@ -35,4 +37,7 @@ public interface UserRepository extends CouchbaseRepository<User, String> {
|
||||
@N1QL("SELECT * FROM $BUCKET$ WHERE username = $1")
|
||||
User findByUsernameBadSelect(String username);
|
||||
|
||||
@N1QL
|
||||
User findByUsernameRegexAndUsernameIn(String regex, List<String> sample);
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -4,68 +4,28 @@ import static com.couchbase.client.java.query.Select.select;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.couchbase.client.java.document.json.JsonArray;
|
||||
import com.couchbase.client.java.document.json.JsonObject;
|
||||
import com.couchbase.client.java.query.ParametrizedQuery;
|
||||
import com.couchbase.client.java.query.PreparedQuery;
|
||||
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.Select;
|
||||
import com.couchbase.client.java.query.SimpleQuery;
|
||||
import com.couchbase.client.java.query.Statement;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.data.couchbase.UnitTestApplicationConfig;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.util.ReflectionUtils;
|
||||
|
||||
public class AbstractN1qlBasedQueryTest {
|
||||
|
||||
@Test
|
||||
public void testQueryPlanShouldProducePreparedQuery() throws Exception {
|
||||
Statement st = select("*");
|
||||
QueryPlan plan = new QueryPlan(JsonObject.create());
|
||||
List<Object> params = new ArrayList<Object>(2);
|
||||
params.add("test");
|
||||
params.add(plan);
|
||||
|
||||
Query query = AbstractN1qlBasedQuery.buildQuery(st, params.iterator());
|
||||
JsonObject queryObject = query.n1ql();
|
||||
assertTrue(query instanceof PreparedQuery);
|
||||
assertEquals(plan, query.statement());
|
||||
assertNull(query.params());
|
||||
assertNotNull(queryObject.get("args"));
|
||||
assertEquals(1, queryObject.getArray("args").size());
|
||||
assertEquals("test", queryObject.getArray("args").getString(0));
|
||||
|
||||
params.add(QueryParams.build());
|
||||
query = AbstractN1qlBasedQuery.buildQuery(st, params.iterator());
|
||||
assertNotNull(query.params());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyArgumentsShouldProduceSimpleQuery() throws Exception {
|
||||
Statement st = select("*");
|
||||
List<Object> params = Collections.emptyList();
|
||||
|
||||
Query query = AbstractN1qlBasedQuery.buildQuery(st, params.iterator());
|
||||
Query query = AbstractN1qlBasedQuery.buildQuery(st, JsonArray.empty());
|
||||
JsonObject queryObject = query.n1ql();
|
||||
|
||||
assertTrue(query instanceof SimpleQuery);
|
||||
assertEquals(st.toString(), query.statement().toString());
|
||||
assertNull(query.params());
|
||||
@@ -73,53 +33,36 @@ public class AbstractN1qlBasedQueryTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnlyQueryParamsShouldProduceSimpleQuery() {
|
||||
Statement st = select("*");
|
||||
QueryParams queryParams = QueryParams.build().scanWait(1, TimeUnit.DAYS);
|
||||
List<Object> params = new ArrayList<Object>(1);
|
||||
params.add(queryParams);
|
||||
|
||||
Query query = AbstractN1qlBasedQuery.buildQuery(st, params.iterator());
|
||||
JsonObject queryObject = query.n1ql();
|
||||
assertTrue(query instanceof SimpleQuery);
|
||||
assertEquals(st.toString(), query.statement().toString());
|
||||
assertEquals(queryParams, query.params());
|
||||
assertFalse(queryObject.containsKey("args"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleArgumentsShouldProduceParametrizedQuery() throws Exception {
|
||||
public void testSimpleArgumentShouldProduceParametrizedQuery() throws Exception {
|
||||
Statement st = select("*");
|
||||
List<Object> params = new ArrayList<Object>(2);
|
||||
params.add(123L);
|
||||
params.add("test");
|
||||
|
||||
Query query = AbstractN1qlBasedQuery.buildQuery(st, params.iterator());
|
||||
JsonArray placeholderValues = JsonArray.from(params);
|
||||
Query query = AbstractN1qlBasedQuery.buildQuery(st, placeholderValues);
|
||||
JsonObject queryObject = query.n1ql();
|
||||
|
||||
assertTrue(query instanceof ParametrizedQuery);
|
||||
assertEquals(st.toString(), query.statement().toString());
|
||||
assertNull(query.params());
|
||||
assertTrue(queryObject.containsKey("args"));
|
||||
JsonArray args = queryObject.getArray("args");
|
||||
assertEquals(2, args.size());
|
||||
assertEquals(123L, args.get(0));
|
||||
assertEquals("test", args.get(1));
|
||||
assertEquals(1, args.size());
|
||||
assertEquals("test", args.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleArgumentsAndQueryParamsShouldProduceParametrizedQuery() throws Exception {
|
||||
public void testMultipleArgumentsShouldProduceParametrizedQuery() throws Exception {
|
||||
Statement st = select("*");
|
||||
QueryParams queryParams = QueryParams.build().withContextId("toto");
|
||||
List<Object> params = new ArrayList<Object>(2);
|
||||
params.add(123L);
|
||||
params.add(queryParams);
|
||||
params.add("test");
|
||||
|
||||
Query query = AbstractN1qlBasedQuery.buildQuery(st, params.iterator());
|
||||
JsonArray placeholderValues = JsonArray.from(params);
|
||||
Query query = AbstractN1qlBasedQuery.buildQuery(st, placeholderValues);
|
||||
JsonObject queryObject = query.n1ql();
|
||||
|
||||
assertTrue(query instanceof ParametrizedQuery);
|
||||
assertEquals(st.toString(), query.statement().toString());
|
||||
assertEquals(queryParams, query.params());
|
||||
assertNull(query.params());
|
||||
assertTrue(queryObject.containsKey("args"));
|
||||
JsonArray args = queryObject.getArray("args");
|
||||
assertEquals(2, args.size());
|
||||
|
||||
@@ -0,0 +1,489 @@
|
||||
package org.springframework.data.couchbase.repository.query;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.springframework.data.couchbase.repository.query.N1qlQueryCreator.createExpression;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.couchbase.client.java.query.dsl.Expression;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
|
||||
public class N1qlQueryCreatorTest {
|
||||
|
||||
//==== The tests below check mapping between a Part.Type and the corresponding N1QL expression ====
|
||||
|
||||
@Test
|
||||
public void testBETWEEN() throws Exception {
|
||||
Part.Type keyword = Part.Type.BETWEEN;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", "b", 1, 2, "c", "d").iterator();
|
||||
String expected = "doc.field BETWEEN \"a\" AND \"b\"";
|
||||
String expectedNum = "doc.field BETWEEN 1 AND 2";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) BETWEEN LOWER(\"c\") AND LOWER(\"d\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIS_NOT_NULL() throws Exception {
|
||||
Part.Type keyword = Part.Type.IS_NOT_NULL;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field IS NOT NULL";
|
||||
String expectedNum = "doc.field IS NOT NULL";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) IS NOT NULL";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIS_NULL() throws Exception {
|
||||
Part.Type keyword = Part.Type.IS_NULL;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field IS NULL";
|
||||
String expectedNum = "doc.field IS NULL";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) IS NULL";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLESS_THAN() throws Exception {
|
||||
Part.Type keyword = Part.Type.LESS_THAN;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field < \"a\"";
|
||||
String expectedNum = "doc.field < 1";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) < LOWER(\"b\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLESS_THAN_EQUAL() throws Exception {
|
||||
Part.Type keyword = Part.Type.LESS_THAN_EQUAL;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field <= \"a\"";
|
||||
String expectedNum = "doc.field <= 1";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) <= LOWER(\"b\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGREATER_THAN() throws Exception {
|
||||
Part.Type keyword = Part.Type.GREATER_THAN;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field > \"a\"";
|
||||
String expectedNum = "doc.field > 1";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) > LOWER(\"b\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGREATER_THAN_EQUAL() throws Exception {
|
||||
Part.Type keyword = Part.Type.GREATER_THAN_EQUAL;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field >= \"a\"";
|
||||
String expectedNum = "doc.field >= 1";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) >= LOWER(\"b\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBEFORE() throws Exception {
|
||||
Part.Type keyword = Part.Type.BEFORE;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field < \"a\"";
|
||||
String expectedNum = "doc.field < 1";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) < LOWER(\"b\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAFTER() throws Exception {
|
||||
Part.Type keyword = Part.Type.AFTER;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field > \"a\"";
|
||||
String expectedNum = "doc.field > 1";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) > LOWER(\"b\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNOT_LIKE() throws Exception {
|
||||
Part.Type keyword = Part.Type.NOT_LIKE;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field NOT LIKE \"a\"";
|
||||
String expectedNum = "doc.field NOT LIKE 1";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) NOT LIKE LOWER(\"b\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLIKE() throws Exception {
|
||||
Part.Type keyword = Part.Type.LIKE;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field LIKE \"a\"";
|
||||
String expectedNum = "doc.field LIKE 1";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) LIKE LOWER(\"b\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSTARTING_WITH() throws Exception {
|
||||
Part.Type keyword = Part.Type.STARTING_WITH;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field LIKE \"a%\"";
|
||||
String expectedNum = "doc.field LIKE 1";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) LIKE LOWER(\"b%\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testENDING_WITH() throws Exception {
|
||||
Part.Type keyword = Part.Type.ENDING_WITH;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field LIKE \"%a\"";
|
||||
String expectedNum = "doc.field LIKE 1";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) LIKE LOWER(\"%b\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNOT_CONTAINING() throws Exception {
|
||||
Part.Type keyword = Part.Type.NOT_CONTAINING;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field NOT LIKE \"%a%\"";
|
||||
String expectedNum = "doc.field NOT LIKE 1";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) NOT LIKE LOWER(\"%b%\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCONTAINING() throws Exception {
|
||||
Part.Type keyword = Part.Type.CONTAINING;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field LIKE \"%a%\"";
|
||||
String expectedNum = "doc.field LIKE 1";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) LIKE LOWER(\"%b%\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNOT_IN() throws Exception {
|
||||
Part.Type keyword = Part.Type.NOT_IN;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field NOT IN [\"a\"]";
|
||||
String expectedNum = "doc.field NOT IN [1]";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) NOT IN [\"b\"]";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNOTINwithCollection() throws Exception {
|
||||
Part.Type keyword = Part.Type.NOT_IN;
|
||||
List<Object> val1 = Arrays.<Object>asList("av1", "av2");
|
||||
List<Object> val2 = Arrays.<Object>asList("bv1", "bv2");
|
||||
Iterator<Object> values = Arrays.<Object>asList(val1, val2).iterator();
|
||||
String expected = "doc.field NOT IN [\"av1\",\"av2\"]";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) NOT IN [\"bv1\",\"bv2\"]";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNOTINwithArray() throws Exception {
|
||||
Part.Type keyword = Part.Type.NOT_IN;
|
||||
String[] val1 = {"av1", "av2"};
|
||||
String[] val2 = {"bv1", "bv2"};
|
||||
Iterator<Object> values = Arrays.<Object>asList(val1, val2).iterator();
|
||||
String expected = "doc.field NOT IN [\"av1\",\"av2\"]";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) NOT IN [\"bv1\",\"bv2\"]";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIN() throws Exception {
|
||||
Part.Type keyword = Part.Type.IN;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field IN [\"a\"]";
|
||||
String expectedNum = "doc.field IN [1]";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) IN [\"b\"]";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testINwithCollection() throws Exception {
|
||||
Part.Type keyword = Part.Type.IN;
|
||||
List<Object> val1 = Arrays.<Object>asList("av1", "av2");
|
||||
List<Object> val2 = Arrays.<Object>asList("bv1", "bv2");
|
||||
Iterator<Object> values = Arrays.<Object>asList(val1, val2).iterator();
|
||||
String expected = "doc.field IN [\"av1\",\"av2\"]";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) IN [\"bv1\",\"bv2\"]";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testINwithArray() throws Exception {
|
||||
Part.Type keyword = Part.Type.IN;
|
||||
String[] val1 = {"av1", "av2"};
|
||||
String[] val2 = {"bv1", "bv2"};
|
||||
Iterator<Object> values = Arrays.<Object>asList(val1, val2).iterator();
|
||||
String expected = "doc.field IN [\"av1\",\"av2\"]";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) IN [\"bv1\",\"bv2\"]";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testNEAR() throws Exception {
|
||||
Part.Type keyword = Part.Type.NEAR;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", true, values);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testWITHIN() throws Exception {
|
||||
Part.Type keyword = Part.Type.WITHIN;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testREGEX() throws Exception {
|
||||
Part.Type keyword = Part.Type.REGEX;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "REGEXP_LIKE(doc.field, \"a\")";
|
||||
String expectedNum = "REGEXP_LIKE(doc.field, \"1\")";
|
||||
String expectedIgnoreCase = "REGEXP_LIKE(doc.field, \"b\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEXISTS() throws Exception {
|
||||
Part.Type keyword = Part.Type.EXISTS;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field IS NOT MISSING";
|
||||
String expectedNum = "doc.field IS NOT MISSING";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) IS NOT MISSING";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTRUE() throws Exception {
|
||||
Part.Type keyword = Part.Type.TRUE;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field = TRUE";
|
||||
String expectedNum = "doc.field = TRUE";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) = TRUE";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFALSE() throws Exception {
|
||||
Part.Type keyword = Part.Type.FALSE;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field = FALSE";
|
||||
String expectedNum = "doc.field = FALSE";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) = FALSE";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNEGATING_SIMPLE_PROPERTY() throws Exception {
|
||||
Part.Type keyword = Part.Type.NEGATING_SIMPLE_PROPERTY;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field != \"a\"";
|
||||
String expectedNum = "doc.field != 1";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) != LOWER(\"b\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSIMPLE_PROPERTY() throws Exception {
|
||||
Part.Type keyword = Part.Type.SIMPLE_PROPERTY;
|
||||
Iterator<Object> values = Arrays.<Object>asList("a", 1, "b").iterator();
|
||||
String expected = "doc.field = \"a\"";
|
||||
String expectedNum = "doc.field = 1";
|
||||
String expectedIgnoreCase = "LOWER(doc.field) = LOWER(\"b\")";
|
||||
|
||||
Expression exp = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expNum = createExpression(keyword, "doc.field", false, values);
|
||||
Expression expIgnoreCase = createExpression(keyword, "doc.field", true, values);
|
||||
|
||||
assertEquals(expected, exp.toString());
|
||||
assertEquals(expectedNum, expNum.toString());
|
||||
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user