diff --git a/src/integration/java/org/springframework/data/couchbase/repository/N1qlCouchbaseRepositoryTests.java b/src/integration/java/org/springframework/data/couchbase/repository/N1qlCouchbaseRepositoryTests.java
index 697878d6..caeff2d5 100644
--- a/src/integration/java/org/springframework/data/couchbase/repository/N1qlCouchbaseRepositoryTests.java
+++ b/src/integration/java/org/springframework/data/couchbase/repository/N1qlCouchbaseRepositoryTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2013 the original author or authors.
+ * Copyright 2013-2017 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.
@@ -45,6 +45,7 @@ import java.util.List;
* This tests PaginAndSortingRepository features in the Couchbase connector.
*
* @author Simon Baslé
+ * @author Subhashni Balakrishnan
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = IntegrationTestApplicationConfig.class)
@@ -185,4 +186,12 @@ public class N1qlCouchbaseRepositoryTests {
Sort sort = new Sort(Sort.Direction.DESC, "attendees");
List parties = partyRepository.findParties(sort);
}
+
+ @Test
+ public void testDeleteQuery() {
+ partyRepository.save(new Party("testDeleteQuery", "delete", "delete", null, 0, null));
+ List partyList = partyRepository.removeByDescriptionOrName("delete", "delete");
+ assertTrue(partyList.size() == 1);
+ }
+
}
diff --git a/src/integration/java/org/springframework/data/couchbase/repository/N1qlPlaceholderTests.java b/src/integration/java/org/springframework/data/couchbase/repository/N1qlPlaceholderTests.java
index 79aa9b5f..d103c55d 100644
--- a/src/integration/java/org/springframework/data/couchbase/repository/N1qlPlaceholderTests.java
+++ b/src/integration/java/org/springframework/data/couchbase/repository/N1qlPlaceholderTests.java
@@ -120,4 +120,19 @@ public class N1qlPlaceholderTests {
"one over the other in findAllWithMixedParamsInQuery", e.getMessage());
}
}
+
+ @Test
+ public void deleteQueryTest() {
+ String included = "90";
+ String excluded = "New Year";
+ int max = 200;
+ List result = partyRepository.removeWithPositionalParams(excluded, included, max);
+
+ assertEquals(10, result.size());
+ for (Party party : result) {
+ assertTrue(party.getDescription().contains(included));
+ assertFalse(party.getDescription().contains(excluded));
+ assertTrue(party.getAttendees() < max);
+ }
+ }
}
diff --git a/src/integration/java/org/springframework/data/couchbase/repository/PartyRepository.java b/src/integration/java/org/springframework/data/couchbase/repository/PartyRepository.java
index dcec677b..e3b1dbeb 100644
--- a/src/integration/java/org/springframework/data/couchbase/repository/PartyRepository.java
+++ b/src/integration/java/org/springframework/data/couchbase/repository/PartyRepository.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2017 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;
import java.util.Date;
@@ -14,6 +30,7 @@ import org.springframework.data.repository.query.Param;
/**
* @author Simon Baslé
+ * @author Subhashni Balakrishnan
*/
@ViewIndexed(designDoc = "party", viewName = "all")
@N1qlSecondaryIndexed(indexName = "party")
@@ -59,10 +76,15 @@ public interface PartyRepository extends CouchbaseRepository {
" AND `desc` NOT LIKE '%' || $1 || '%'")
List findAllWithPositionalParams(String ex, String inc, long minimumAttendees);
+ @Query("#{#n1ql.delete} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $2 || '%' AND attendees < $3" +
+ " AND `desc` NOT LIKE '%' || $1 || '%' #{#n1ql.returning}")
+ List removeWithPositionalParams(String ex, String inc, long minimumAttendees);
+
@Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter} AND `desc` LIKE '%' || $2 || '%' AND attendees >= $3" +
" AND `desc` NOT LIKE '%' || $1 || '%' AND `desc` != \"this is \\\"$excluded\\\"\"")
List findAllWithPositionalParamsAndQuotedNamedParams(@Param("excluded") String ex, @Param("included") String inc, @Param("min") long min);
List findByDescriptionOrName(String description, String name);
+ List removeByDescriptionOrName(String description, String name);
}
diff --git a/src/main/asciidoc/repository.adoc b/src/main/asciidoc/repository.adoc
index 9064d4b4..cba88abd 100644
--- a/src/main/asciidoc/repository.adoc
+++ b/src/main/asciidoc/repository.adoc
@@ -113,7 +113,8 @@ While the exposed methods provide you with a great variety of access patterns, v
=== N1QL based querying
As of version `4.0`, Couchbase Server ships with a new query language called `N1QL`. In `Spring-Data-Couchbase 2.0`, N1QL is the default way of doing queries and will allow you to fully derive queries from a method name.
-Prerequisite is to have a N1QL-compatible cluster and to have created a PRIMARY INDEX on the bucket where the entities will be stored.
+Prerequisite is to have a N1QL-compatible cluster and to have created a PRIMARY INDEX on the bucket where the entities will be stored. DML queries
+are supported from Couchbase server version `4.1`.
WARNING: If it is detected at configuration time that the cluster doesn't support N1QL while there are `@Query` annotated methods or non-annotated methods in your repository interface, a `UnsupportedCouchbaseFeatureException` will be thrown.
@@ -142,6 +143,8 @@ A few N1QL-specific values are provided through SpEL:
- `#n1ql.filter` in the WHERE clause adds a criteria matching the entity type with the field that Spring Data uses to store type information.
- `#n1ql.bucket` will be replaced by the name of the bucket the entity is stored in, escaped in backticks.
- `#n1ql.fields` will be replaced by the list of fields (eg. for a SELECT clause) necessary to reconstruct the entity.
+ - `#n1ql.delete` will be replaced by the `delete from` statement.
+ - `#n1ql.returning` will be replaced by returning clause needed for reconstructing entity.
IMPORTANT: We recommend that you always use the `selectEntity` SpEL and a WHERE clause with a `filter` SpEL (since otherwise your query could be impacted by entities from other repositories).
@@ -206,6 +209,15 @@ This could be useful to craft a query according to the role of the connected use
"role = '?#{hasRole('ROLE_ADMIN') ? 'public_admin' : 'admin'}'")
List findAllAdmins(); //only ROLE_ADMIN users will see hidden admins
----
+
+Delete query example:
+[source,java]
+----
+@Query("#{#n1ql.delete} WHERE #{#n1ql.filter} AND " +
+"username = $1 #{#n1ql.returning}")
+UserInfo removeUser(String username);
+----
+
****
The second method uses Spring-Data's query derivation mechanism to build a N1QL query from the method name and parameters. This will produce a query looking like this: `SELECT ... FROM ... WHERE firstName = "valueOfFnameAtRuntime"`. You can combine these criteria, even do a count with a name like `countByFirstname` or a limit with a name like `findFirst3ByLastname`...
diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/AbstractN1qlBasedQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/AbstractN1qlBasedQuery.java
index 38c512c8..b5d15ecd 100644
--- a/src/main/java/org/springframework/data/couchbase/repository/query/AbstractN1qlBasedQuery.java
+++ b/src/main/java/org/springframework/data/couchbase/repository/query/AbstractN1qlBasedQuery.java
@@ -123,10 +123,6 @@ public abstract class AbstractN1qlBasedQuery implements RepositoryQuery {
protected Object executeDependingOnType(N1qlQuery query, N1qlQuery countQuery, QueryMethod queryMethod,
Pageable pageable, Class> typeToRead) {
- if (queryMethod.isModifyingQuery()) {
- throw new UnsupportedOperationException("Modifying queries not yet supported");
- }
-
if (queryMethod.isPageQuery()) {
return executePaged(query, countQuery, pageable, typeToRead);
} else if (queryMethod.isSliceQuery()) {
diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/ConvertingIterator.java b/src/main/java/org/springframework/data/couchbase/repository/query/ConvertingIterator.java
index af00dae3..7c2bd27f 100644
--- a/src/main/java/org/springframework/data/couchbase/repository/query/ConvertingIterator.java
+++ b/src/main/java/org/springframework/data/couchbase/repository/query/ConvertingIterator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2012-2015 the original author or authors
+ * Copyright 2012-2017 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.
@@ -23,8 +23,11 @@ import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
/**
* An {@link Iterator Iterator<Object>} that {@link CouchbaseConverter#convertForWriteIfNeeded(Object) converts}
* values to their stored Class if warranted.
+ *
+ * @author Simon Baslé
+ * @author Subhashni Balakrishnan
*/
-class ConvertingIterator implements Iterator
*
* @author Simon Baslé
+ * @author Subhashni Balakrishnan
* @author Mark Paluch
*/
public class N1qlQueryCreator extends AbstractQueryCreator {
@@ -108,7 +101,7 @@ public class N1qlQueryCreator extends AbstractQueryCreator iterator) {
- return prepareExpression(part, iterator);
+ return N1qlQueryCreatorUtils.prepareExpression(converter, part, iterator);
}
@Override
@@ -144,161 +137,4 @@ public class N1qlQueryCreator extends AbstractQueryCreator iterator) {
- PersistentPropertyPath path = N1qlUtils.getPathWithAlternativeFieldNames(
- this.converter, part.getProperty());
- ConvertingIterator parameterValues = new ConvertingIterator(iterator, converter);
-
- //get the whole doted path with fieldNames instead of potentially wrong propNames
- String fieldNamePath = N1qlUtils.getDottedPathWithAlternativeFieldNames(path);
-
- //deal with ignore case
- boolean ignoreCase = false;
- Class> leafType = converter.getWriteClassFor(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 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 in N1QL query derivation");
- }
- }
-
- protected static Expression regexp(String left, Iterator parameterValues) {
- Object next = parameterValues.next();
-
- String pattern;
- if (next == null) {
- pattern = "";
- } else {
- pattern = String.valueOf(next);
- }
- return PatternMatchingFunctions.regexpLike(left, pattern);
- }
-
- protected static Expression leftAndRight(Iterator parameterValues, boolean ignoreCase) {
- return right(parameterValues, ignoreCase).and(right(parameterValues, ignoreCase));
- }
-
- protected static Expression like(Iterator 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 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 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);
- }
-
}
diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/PartTreeN1qlBasedQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/PartTreeN1qlBasedQuery.java
index 61bf88a4..708bce6b 100644
--- a/src/main/java/org/springframework/data/couchbase/repository/query/PartTreeN1qlBasedQuery.java
+++ b/src/main/java/org/springframework/data/couchbase/repository/query/PartTreeN1qlBasedQuery.java
@@ -16,9 +16,11 @@
package org.springframework.data.couchbase.repository.query;
+import static com.couchbase.client.java.query.Delete.deleteFrom;
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.functions.AggregateFunctions.count;
+import static org.springframework.data.couchbase.repository.query.support.N1qlUtils.createReturningExpressionForDelete;
import com.couchbase.client.java.document.json.JsonArray;
import com.couchbase.client.java.document.json.JsonValue;
import com.couchbase.client.java.query.Statement;
@@ -26,7 +28,8 @@ 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 com.couchbase.client.java.query.dsl.path.MutateLimitPath;
+import com.couchbase.client.java.query.dsl.path.DeleteUsePath;
import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.couchbase.repository.query.support.N1qlUtils;
import org.springframework.data.domain.Pageable;
@@ -71,30 +74,40 @@ public class PartTreeN1qlBasedQuery extends AbstractN1qlBasedQuery {
String bucketName = getCouchbaseOperations().getCouchbaseBucket().name();
Expression bucket = N1qlUtils.escapedBucket(bucketName);
- FromPath select;
- if (partTree.isCountProjection()) {
- select = select(count("*"));
+ if (partTree.isDelete()) {
+ DeleteUsePath deleteUsePath = deleteFrom(bucket);
+ N1qlMutateQueryCreator queryCreator = new N1qlMutateQueryCreator(partTree, accessor, deleteUsePath, getCouchbaseOperations().getConverter(), getQueryMethod());
+ MutateLimitPath mutateFromWhereOrderBy = queryCreator.createQuery();
+ if (partTree.isLimiting()) {
+ return mutateFromWhereOrderBy.limit(partTree.getMaxResults());
+ } else {
+ return mutateFromWhereOrderBy.returning(createReturningExpressionForDelete(bucketName));
+ }
} else {
+ FromPath select;
+ if (partTree.isCountProjection()) {
+ select = select(count("*"));
+ } else {
select = N1qlUtils.createSelectClauseForEntity(bucketName, returnedType, this.getCouchbaseOperations().getConverter());
- }
- WherePath selectFrom = select.from(bucket);
+ }
+ WherePath selectFrom = select.from(bucket);
+ N1qlQueryCreator queryCreator = new N1qlQueryCreator(partTree, accessor, selectFrom,
+ getCouchbaseOperations().getConverter(), getQueryMethod());
+ LimitPath selectFromWhereOrderBy = queryCreator.createQuery();
- N1qlQueryCreator queryCreator = new N1qlQueryCreator(partTree, accessor, selectFrom,
- getCouchbaseOperations().getConverter(), getQueryMethod());
- LimitPath selectFromWhereOrderBy = queryCreator.createQuery();
-
- if (queryMethod.isPageQuery()) {
- Pageable pageable = accessor.getPageable();
- Assert.notNull(pageable, "Pageable must not be null!");
- return selectFromWhereOrderBy.limit(pageable.getPageSize()).offset(Math.toIntExact(pageable.getOffset()));
- } else if (queryMethod.isSliceQuery() && accessor.getPageable().isPaged()) {
- Pageable pageable = accessor.getPageable();
- Assert.notNull(pageable, "Pageable must not be null!");
- return selectFromWhereOrderBy.limit(pageable.getPageSize() + 1).offset(Math.toIntExact(pageable.getOffset()));
- } else if (partTree.isLimiting()) {
- return selectFromWhereOrderBy.limit(partTree.getMaxResults());
- } else {
- return selectFromWhereOrderBy;
+ if (queryMethod.isPageQuery()) {
+ Pageable pageable = accessor.getPageable();
+ Assert.notNull(pageable, "Pageable must not be null!");
+ return selectFromWhereOrderBy.limit(pageable.getPageSize()).offset(Math.toIntExact(pageable.getOffset()));
+ } else if (queryMethod.isSliceQuery() && accessor.getPageable().isPaged()) {
+ Pageable pageable = accessor.getPageable();
+ Assert.notNull(pageable, "Pageable must not be null!");
+ return selectFromWhereOrderBy.limit(pageable.getPageSize() + 1).offset(Math.toIntExact(pageable.getOffset()));
+ } else if (partTree.isLimiting()) {
+ return selectFromWhereOrderBy.limit(partTree.getMaxResults());
+ } else {
+ return selectFromWhereOrderBy;
+ }
}
}
diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java b/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java
index 1c158c53..bcb4a816 100644
--- a/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java
+++ b/src/main/java/org/springframework/data/couchbase/repository/query/StringBasedN1qlQueryParser.java
@@ -15,6 +15,8 @@
*/
package org.springframework.data.couchbase.repository.query;
+import static com.couchbase.client.java.query.Delete.deleteFrom;
+import static com.couchbase.client.java.query.dsl.Expression.i;
import static org.springframework.data.couchbase.core.support.TemplateUtils.*;
import java.util.ArrayList;
@@ -27,6 +29,7 @@ import com.couchbase.client.java.document.json.JsonArray;
import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.document.json.JsonValue;
import org.slf4j.LoggerFactory;
+import org.springframework.data.couchbase.repository.query.support.N1qlUtils;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.QueryMethod;
@@ -71,6 +74,22 @@ public class StringBasedN1qlQueryParser {
*/
public static final String SPEL_FILTER = "#" + SPEL_PREFIX + ".filter";
+ /**
+ * Use this variable in a SpEL expression in a {@link org.springframework.data.couchbase.core.query.Query @Query}
+ * annotation's inline statement. This will be replaced by the correct delete expression needed
+ * Eg. "#{{@value SPEL_DELETE}} WHERE test = true".
+ * Note this only makes sense once, as the beginning of the statement.
+ */
+ public static final String SPEL_DELETE = "#" + SPEL_PREFIX + ".delete";
+
+ /**
+ * Use this variable in a SpEL expression in a {@link org.springframework.data.couchbase.core.query.Query @Query}
+ * annotation's inline statement. This will be replaced by the correct returning clause needed
+ * for entity mapping. Eg. "#{{@value SPEL_RETURNING}} WHERE test = true".
+ * Note this only makes sense once, as the beginning of the statement.
+ */
+ public static final String SPEL_RETURNING = "#" + SPEL_PREFIX + ".returning";
+
/** regexp that detect $named placeholder (starts with a letter, then alphanum chars) */
public static final Pattern NAMED_PLACEHOLDER_PATTERN = Pattern.compile("\\W(\\$\\p{Alpha}\\p{Alnum}*)\\b");
@@ -116,10 +135,12 @@ public class StringBasedN1qlQueryParser {
} else {
selectEntity = "SELECT " + entity + ", " + b + ".* FROM " + b;
}
-
String typeSelection = "`" + typeField + "` = \"" + typeValue.getName() + "\"";
- return new N1qlSpelValues(selectEntity, entity, b, typeSelection);
+ String delete = deleteFrom(i(bucketName)).toString();
+ String returning = " returning " + N1qlUtils.createReturningExpressionForDelete(bucketName).toString();
+
+ return new N1qlSpelValues(selectEntity, entity, b, typeSelection, delete, returning);
}
//this static method can be used to test the parsing behavior for Couchbase specific spel variables
@@ -265,17 +286,30 @@ public class StringBasedN1qlQueryParser {
public final String bucket;
/**
- * #{{@value SPEL_FILTER}.
+ * #{{@value SPEL_FILTER}}.
* filter will be replaced by an expression allowing to select only entries matching the entity in a WHERE clause.
*/
public final String filter;
- public N1qlSpelValues(String selectClause, String entityFields, String bucket, String filter) {
+ /**
+ * #{{@value SPEL_DELETE}}.
+ * delete will be replaced by a delete expression.
+ */
+ public final String delete;
+
+ /**
+ * #{{@value SPEL_RETURNING}}.
+ * returning will be replaced by a returning expression allowing to return the entity and meta information on deletes.
+ */
+ public final String returning;
+
+ public N1qlSpelValues(String selectClause, String entityFields, String bucket, String filter, String delete, String returning) {
this.selectEntity = selectClause;
this.fields = entityFields;
this.bucket = bucket;
this.filter = filter;
+ this.delete = delete;
+ this.returning = returning;
}
}
-
}
diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/StringN1qlBasedQuery.java b/src/main/java/org/springframework/data/couchbase/repository/query/StringN1qlBasedQuery.java
index 1bb92121..9cecde15 100644
--- a/src/main/java/org/springframework/data/couchbase/repository/query/StringN1qlBasedQuery.java
+++ b/src/main/java/org/springframework/data/couchbase/repository/query/StringN1qlBasedQuery.java
@@ -16,6 +16,13 @@
package org.springframework.data.couchbase.repository.query;
+import static com.couchbase.client.java.query.Delete.deleteFrom;
+import static com.couchbase.client.java.query.dsl.Expression.i;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import com.couchbase.client.java.document.json.JsonValue;
import com.couchbase.client.java.query.N1qlQuery;
diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/support/N1qlQueryCreatorUtils.java b/src/main/java/org/springframework/data/couchbase/repository/query/support/N1qlQueryCreatorUtils.java
new file mode 100644
index 00000000..b6f196c5
--- /dev/null
+++ b/src/main/java/org/springframework/data/couchbase/repository/query/support/N1qlQueryCreatorUtils.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2017 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.support;
+
+import static com.couchbase.client.java.query.dsl.Expression.*;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import com.couchbase.client.java.document.json.JsonArray;
+import com.couchbase.client.java.query.dsl.Expression;
+import com.couchbase.client.java.query.dsl.functions.PatternMatchingFunctions;
+import com.couchbase.client.java.query.dsl.functions.StringFunctions;
+import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
+import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
+import org.springframework.data.couchbase.repository.query.ConvertingIterator;
+import org.springframework.data.mapping.context.PersistentPropertyPath;
+import org.springframework.data.repository.query.parser.Part;
+
+/**
+ * Utils for creating part tree expressions
+ *
+ * @author Subhashni Balakrishnan
+ */
+public class N1qlQueryCreatorUtils {
+ public static Expression prepareExpression(CouchbaseConverter converter, Part part, Iterator iterator) {
+ PersistentPropertyPath path = N1qlUtils.getPathWithAlternativeFieldNames(
+ converter, part.getProperty());
+ ConvertingIterator parameterValues = new ConvertingIterator(iterator, converter);
+
+ //get the whole doted path with fieldNames instead of potentially wrong propNames
+ String fieldNamePath = N1qlUtils.getDottedPathWithAlternativeFieldNames(path);
+
+ //deal with ignore case
+ boolean ignoreCase = false;
+ Class> leafType = converter.getWriteClassFor(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);
+ }
+
+
+ public static Expression createExpression(Part.Type partType, String fieldNamePath, boolean ignoreCase, Iterator 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 in N1QL query derivation");
+ }
+ }
+
+
+ protected static Expression regexp(String left, Iterator parameterValues) {
+ Object next = parameterValues.next();
+
+ String pattern;
+ if (next == null) {
+ pattern = "";
+ } else {
+ pattern = String.valueOf(next);
+ }
+ return PatternMatchingFunctions.regexpLike(left, pattern);
+ }
+
+ protected static Expression leftAndRight(Iterator parameterValues, boolean ignoreCase) {
+ return right(parameterValues, ignoreCase).and(right(parameterValues, ignoreCase));
+ }
+
+ protected static Expression like(Iterator 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 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 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);
+ }
+}
diff --git a/src/main/java/org/springframework/data/couchbase/repository/query/support/N1qlUtils.java b/src/main/java/org/springframework/data/couchbase/repository/query/support/N1qlUtils.java
index 49f07f1d..7bc10fee 100644
--- a/src/main/java/org/springframework/data/couchbase/repository/query/support/N1qlUtils.java
+++ b/src/main/java/org/springframework/data/couchbase/repository/query/support/N1qlUtils.java
@@ -123,6 +123,32 @@ public class N1qlUtils {
return select(propertiesExp);
}
+ /**
+ * Creates the returning clause for N1ql deletes with all attributes of the entity and meta information
+ *
+ * @param bucketName the bucket that stores the entity documents (will be escaped).
+ * @return the needed returning clause of the statement.
+ */
+ public static Expression createReturningExpressionForDelete(String bucketName) {
+ Expression fullEntity = path(i(bucketName), "*");
+ Expression metaId = path(meta(i(bucketName)), "id").as(SELECT_ID);
+ Expression metaCas = path(meta(i(bucketName)), "cas").as(SELECT_CAS);
+ List expList = new ArrayList();
+ expList.add(fullEntity);
+ expList.add(metaId);
+ expList.add(metaCas);
+
+ StringBuilder sb = new StringBuilder();
+ for (Expression exp: expList) {
+ if (sb.length() != 0) {
+ sb.append(", ");
+ }
+ sb.append(exp.toString());
+ }
+
+ return x(sb.toString());
+ }
+
/**
* Produce a {@link Statement} that corresponds to the SELECT clause for looking for Spring Data entities
* stored in Couchbase. Notably it will select the content of the document AND its id and cas.
diff --git a/src/test/java/org/springframework/data/couchbase/repository/query/AbstractN1qlBasedQueryTest.java b/src/test/java/org/springframework/data/couchbase/repository/query/AbstractN1qlBasedQueryTest.java
index 80d64cef..a08e0663 100644
--- a/src/test/java/org/springframework/data/couchbase/repository/query/AbstractN1qlBasedQueryTest.java
+++ b/src/test/java/org/springframework/data/couchbase/repository/query/AbstractN1qlBasedQueryTest.java
@@ -222,28 +222,6 @@ public class AbstractN1qlBasedQueryTest {
verify(mock, never()).executeSingleProjection(any(N1qlQuery.class));
}
- @Test
- public void shouldThrowWhenModifyingType() throws Exception {
-
- Method method = SampleRepository.class.getMethod("modifyingMethod");
- CouchbaseQueryMethod queryMethod = spy(new CouchbaseQueryMethod(method, metadata, projectionFactory, context));
- when(queryMethod.isModifyingQuery()).thenReturn(true);
-
- N1qlQuery query = Mockito.mock(N1qlQuery.class);
- Pageable pageable = Mockito.mock(Pageable.class);
- AbstractN1qlBasedQuery mock = mock(AbstractN1qlBasedQuery.class);
- when(mock.executeDependingOnType(any(N1qlQuery.class), any(N1qlQuery.class), any(QueryMethod.class), any(Pageable.class),
- any(Class.class))).thenCallRealMethod();
-
- try { mock.executeDependingOnType(query, query, queryMethod, pageable, Sample.class); fail(); } catch (UnsupportedOperationException e) { }
- verify(mock, never()).executeCollection(any(N1qlQuery.class), any(Class.class));
- verify(mock, never()).executeEntity(any(N1qlQuery.class), any(Class.class));
- verify(mock, never()).executeStream(any(N1qlQuery.class), any(Class.class));
- verify(mock, never()).executePaged(any(N1qlQuery.class), any(N1qlQuery.class), any(Pageable.class), any(Class.class));
- verify(mock, never()).executeSliced(any(N1qlQuery.class), any(N1qlQuery.class), any(Pageable.class), any(Class.class));
- verify(mock, never()).executeSingleProjection(any(N1qlQuery.class));
- }
-
@Test
public void shouldExecuteSingleProjectionWhenRandomObjectReturnType() throws Exception {
diff --git a/src/test/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreatorTest.java b/src/test/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreatorTest.java
index 2877c330..3108bb8a 100644
--- a/src/test/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreatorTest.java
+++ b/src/test/java/org/springframework/data/couchbase/repository/query/N1qlQueryCreatorTest.java
@@ -1,13 +1,16 @@
package org.springframework.data.couchbase.repository.query;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import static org.springframework.data.couchbase.repository.query.N1qlQueryCreator.*;
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.couchbase.repository.query.support.N1qlQueryCreatorUtils;
import org.springframework.data.repository.query.parser.Part;
import com.couchbase.client.java.query.dsl.Expression;
@@ -24,9 +27,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -41,9 +44,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -58,9 +61,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -75,9 +78,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -92,9 +95,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -109,9 +112,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -126,9 +129,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -143,9 +146,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -160,9 +163,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -177,9 +180,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -194,9 +197,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -211,9 +214,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -228,9 +231,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -245,9 +248,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -262,9 +265,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -279,9 +282,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -297,8 +300,8 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
@@ -313,8 +316,8 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
@@ -328,9 +331,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -346,8 +349,8 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
@@ -362,8 +365,8 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedIgnoreCase, expIgnoreCase.toString());
@@ -374,7 +377,7 @@ public class N1qlQueryCreatorTest {
Part.Type keyword = Part.Type.NEAR;
Iterator values = Arrays.asList("a", 1, "b").iterator();
- Expression exp = createExpression(keyword, "doc.field", true, values);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
}
@Test(expected = IllegalArgumentException.class)
@@ -382,7 +385,7 @@ public class N1qlQueryCreatorTest {
Part.Type keyword = Part.Type.WITHIN;
Iterator values = Arrays.asList("a", 1, "b").iterator();
- Expression exp = createExpression(keyword, "doc.field", false, values);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
}
@Test
@@ -393,9 +396,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -410,9 +413,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -427,9 +430,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -444,9 +447,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -461,9 +464,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
@@ -478,9 +481,9 @@ public class N1qlQueryCreatorTest {
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);
+ Expression exp = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expNum = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", false, values);
+ Expression expIgnoreCase = N1qlQueryCreatorUtils.createExpression(keyword, "doc.field", true, values);
assertEquals(expected, exp.toString());
assertEquals(expectedNum, expNum.toString());
diff --git a/src/test/java/org/springframework/data/couchbase/repository/query/StringN1QlBasedQueryTest.java b/src/test/java/org/springframework/data/couchbase/repository/query/StringN1QlBasedQueryTest.java
index a532813b..ab6a9eb5 100644
--- a/src/test/java/org/springframework/data/couchbase/repository/query/StringN1QlBasedQueryTest.java
+++ b/src/test/java/org/springframework/data/couchbase/repository/query/StringN1QlBasedQueryTest.java
@@ -63,4 +63,25 @@ public class StringN1QlBasedQueryTest {
assertEquals("SELECT COUNT(*) AS " + CountFragment.COUNT_ALIAS + " FROM `B` WHERE true", parsed);
}
+
+ @Test
+ public void testDeletePlaceholder() throws Exception {
+ String statement = spel(SPEL_DELETE) + " WHERE test = 1 AND " + spel(SPEL_FILTER);
+ String parsed = new StringBasedN1qlQueryParser(statement, null, "B", "_class", String.class)
+ .doParse(SPEL_PARSER, SPEL_EVALUATION_CONTEXT, true);
+
+ assertEquals("DELETE FROM `B` WHERE test = 1 AND `_class` = "
+ + "\"java.lang.String\"", parsed);
+ }
+
+ @Test
+ public void testReturningPlaceholder() throws Exception {
+ String statement = spel(SPEL_DELETE) + " WHERE test = 1 AND " + spel(SPEL_FILTER) + spel(SPEL_RETURNING) ;
+ String parsed = new StringBasedN1qlQueryParser(statement, null, "B", "_class", String.class)
+ .doParse(SPEL_PARSER, SPEL_EVALUATION_CONTEXT, true);
+
+ assertEquals("DELETE FROM `B` WHERE test = 1 AND `_class` = "
+ + "\"java.lang.String\" returning `B`.*, META(`B`).id AS _ID, META(`B`).cas AS _CAS", parsed);
+ }
+
}
\ No newline at end of file