Add n1ql.scope and n1ql.collection spel expressions for @Query.

This adds n1ql.scope and n1ql.collection spel expressions for
@Query so that n1ql.bucket doesn't need to be overloaded with
the collection name. This will make the bucket name available
in n1q.bucket.

This is a breaking change as queries that used n1ql.bucket to
get the collection name (especially in the case of pre-scope-
and-collection @Queries that (a) referenced n1ql.bucket instead
of referencing other spel expressions that include the
bucket/collection name; and (b)  still worked after the
repository was moved from a bucket onto a collection by
virtue of n1ql.bucket having the value of the collection in
such instances.

Closes #1445.
This commit is contained in:
Michael Reiche
2022-06-14 14:33:21 -07:00
parent 1db04d2cf5
commit c6fa4ee041
16 changed files with 314 additions and 170 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2021 the original author or authors
* Copyright 2012-2022 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.
@@ -20,9 +20,11 @@ import java.util.List;
import java.util.stream.Stream;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.couchbase.repository.Collection;
import org.springframework.data.couchbase.repository.CouchbaseRepository;
import org.springframework.data.couchbase.repository.Query;
import org.springframework.data.couchbase.repository.ScanConsistency;
import org.springframework.data.couchbase.repository.Scope;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@@ -59,6 +61,11 @@ public interface UserRepository extends CouchbaseRepository<User, String> {
List<User> findByVersionEqualsAndFirstnameEquals(Long version, String firstname);
@Query("#{#n1ql.selectEntity}|#{#n1ql.filter}|#{#n1ql.bucket}|#{#n1ql.scope}|#{#n1ql.collection}")
@Scope("thisScope")
@Collection("thisCollection")
List<User> spelTests();
// simulate a slow operation
@Cacheable("mySpringCache")
default List<User> getByFirstname(String firstname) {

View File

@@ -66,9 +66,9 @@ import com.couchbase.client.java.query.QueryScanConsistency;
* @author Michael Nitschinger
* @author Michael Reiche
*/
@SpringJUnitConfig(StringN1qlQueryCreatorTests.Config.class)
@SpringJUnitConfig(StringN1qlQueryCreatorIntegrationTests.Config.class)
@IgnoreWhen(clusterTypes = ClusterType.MOCKED)
class StringN1qlQueryCreatorTests extends ClusterAwareIntegrationTests {
class StringN1qlQueryCreatorIntegrationTests extends ClusterAwareIntegrationTests {
MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> context;
CouchbaseConverter converter;
@@ -98,8 +98,8 @@ class StringN1qlQueryCreatorTests extends ClusterAwareIntegrationTests {
converter.getMappingContext());
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method), "Continental"),
queryMethod, converter, config().bucketname(), new SpelExpressionParser(),
QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
queryMethod, converter, new SpelExpressionParser(), QueryMethodEvaluationContextProvider.DEFAULT,
namedQueries);
Query query = creator.createQuery();
@@ -131,8 +131,8 @@ class StringN1qlQueryCreatorTests extends ClusterAwareIntegrationTests {
converter.getMappingContext());
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method), "Continental"),
queryMethod, converter, config().bucketname(), new SpelExpressionParser(),
QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
queryMethod, converter, new SpelExpressionParser(), QueryMethodEvaluationContextProvider.DEFAULT,
namedQueries);
Query query = creator.createQuery();

View File

@@ -84,13 +84,15 @@ class StringN1qlQueryCreatorMockedTests extends ClusterAwareIntegrationTests {
converter.getMappingContext());
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method), "Oliver", "Twist"),
queryMethod, converter, "travel-sample", new SpelExpressionParser(),
QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
queryMethod, converter, new SpelExpressionParser(), QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
Query query = creator.createQuery();
assertEquals(
"SELECT `_class`, META(`travel-sample`).`cas` AS __cas, `createdBy`, `createdDate`, `lastModifiedBy`, `lastModifiedDate`, META(`travel-sample`).`id` AS __id, `firstname`, `lastname`, `subtype` FROM `travel-sample` where `_class` = \"abstractuser\" and firstname = $1 and lastname = $2",
query.toN1qlSelectString(couchbaseTemplate.reactive(), User.class, false));
"SELECT `_class`, META(`" + bucketName()
+ "`).`cas` AS __cas, `createdBy`, `createdDate`, `lastModifiedBy`, `lastModifiedDate`, META(`"
+ bucketName() + "`).`id` AS __id, `firstname`, `lastname`, `subtype` FROM `" + bucketName()
+ "` where `_class` = \"abstractuser\" and firstname = $1 and lastname = $2",
query.toN1qlSelectString(couchbaseTemplate.reactive(), null, null, User.class, User.class, false, null, null));
}
@Test
@@ -103,13 +105,15 @@ class StringN1qlQueryCreatorMockedTests extends ClusterAwareIntegrationTests {
converter.getMappingContext());
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method), "Oliver", "Twist"),
queryMethod, converter, "travel-sample", new SpelExpressionParser(),
QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
queryMethod, converter, new SpelExpressionParser(), QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
Query query = creator.createQuery();
assertEquals(
"SELECT `_class`, META(`travel-sample`).`cas` AS __cas, `createdBy`, `createdDate`, `lastModifiedBy`, `lastModifiedDate`, META(`travel-sample`).`id` AS __id, `firstname`, `lastname`, `subtype` FROM `travel-sample` where `_class` = \"abstractuser\" and (firstname = $first or lastname = $last)",
query.toN1qlSelectString(couchbaseTemplate.reactive(), User.class, false));
"SELECT `_class`, META(`" + bucketName()
+ "`).`cas` AS __cas, `createdBy`, `createdDate`, `lastModifiedBy`, `lastModifiedDate`, META(`"
+ bucketName() + "`).`id` AS __id, `firstname`, `lastname`, `subtype` FROM `" + bucketName()
+ "` where `_class` = \"abstractuser\" and (firstname = $first or lastname = $last)",
query.toN1qlSelectString(couchbaseTemplate.reactive(), null, null, User.class, User.class, false, null, null));
}
@Test
@@ -123,8 +127,8 @@ class StringN1qlQueryCreatorMockedTests extends ClusterAwareIntegrationTests {
try {
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method), "Oliver"),
queryMethod, converter, "travel-sample", new SpelExpressionParser(),
QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
queryMethod, converter, new SpelExpressionParser(), QueryMethodEvaluationContextProvider.DEFAULT,
namedQueries);
} catch (IllegalArgumentException e) {
return;
}
@@ -141,14 +145,33 @@ class StringN1qlQueryCreatorMockedTests extends ClusterAwareIntegrationTests {
try {
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method), "Oliver"),
queryMethod, converter, "travel-sample", new SpelExpressionParser(),
QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
queryMethod, converter, new SpelExpressionParser(), QueryMethodEvaluationContextProvider.DEFAULT,
namedQueries);
} catch (IllegalArgumentException e) {
return;
}
fail("should have failed with IllegalArgumentException: query has no inline Query or named Query not found");
}
@Test
void spelTests() throws Exception {
String input = "spelTests";
Method method = UserRepository.class.getMethod(input);
CouchbaseQueryMethod queryMethod = new CouchbaseQueryMethod(method,
new DefaultRepositoryMetadata(UserRepository.class), new SpelAwareProxyProjectionFactory(),
converter.getMappingContext());
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method)), queryMethod,
converter, new SpelExpressionParser(), QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
Query query = creator.createQuery();
String s = query.toN1qlSelectString(couchbaseTemplate.reactive(), "myScope", "myCollection", User.class, null,
false, null, null);
System.out.println("query: " + s);
}
private ParameterAccessor getAccessor(Parameters<?, ?> params, Object... values) {
return new ParametersParameterAccessor(params, values);
}