Remove template dependency from string n1ql, also fix StringN1ql tests.

Closes #1470.
This commit is contained in:
Michael Reiche
2022-06-17 12:19:08 -07:00
parent 30a0b8858f
commit dbd431332b
8 changed files with 99 additions and 137 deletions

View File

@@ -239,7 +239,8 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
}
private String assembleEntityQuery(final boolean count, String[] distinctFields, String scope, String collection) {
return query.toN1qlSelectString(template, scope, collection, this.domainType, this.returnType, count,
return query.toN1qlSelectString(template.getConverter(), template.getBucketName(), scope, collection,
this.domainType, this.returnType, count,
query.getDistinctFields() != null ? query.getDistinctFields() : distinctFields, fields);
}
}

View File

@@ -122,7 +122,8 @@ public class ReactiveRemoveByQueryOperationSupport implements ReactiveRemoveByQu
}
private String assembleDeleteQuery(String scope, String collection) {
return query.toN1qlRemoveString(template, scope, collection, this.domainType);
return query.toN1qlRemoveString(template.getConverter(), template.getBucketName(), scope, collection,
this.domainType);
}
@Override

View File

@@ -74,12 +74,12 @@ public class N1qlJoinResolver {
String from = "FROM " + keySpacePair.lhs.keyspace + " lks " + useLKS + joinType + " " + keySpacePair.rhs.keyspace
+ " rks";
StringBasedN1qlQueryParser.N1qlSpelValues n1qlL = Query.getN1qlSpelValues(template, null,
StringBasedN1qlQueryParser.N1qlSpelValues n1qlL = Query.getN1qlSpelValues(template.getConverter(), null, scope,
keySpacePair.lhs.collection, parameters.getEntityTypeInfo().getType(), parameters.getEntityTypeInfo().getType(),
false, null, null);
String onLks = "lks." + n1qlL.filter;
StringBasedN1qlQueryParser.N1qlSpelValues n1qlR = Query.getN1qlSpelValues(template, null,
StringBasedN1qlQueryParser.N1qlSpelValues n1qlR = Query.getN1qlSpelValues(template.getConverter(), null, scope,
keySpacePair.rhs.collection, parameters.getAssociatedEntityTypeInfo().getType(),
parameters.getAssociatedEntityTypeInfo().getType(), false, null, null);
String onRks = "rks." + n1qlR.filter;

View File

@@ -15,7 +15,7 @@
*/
package org.springframework.data.couchbase.core.query;
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.query.QueryOptions;
@@ -48,8 +48,9 @@ public class N1QLQuery extends Query {
}
@Override
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String scopeName, String collectionName,
Class domainClass, Class returnClass, boolean isCount, String[] distinctFields, String[] fields) {
public String toN1qlSelectString(CouchbaseConverter template, String bucketName, String scopeName,
String collectionName, Class domainClass, Class returnClass, boolean isCount, String[] distinctFields,
String[] fields) {
return expression.toString();
}
}

View File

@@ -343,17 +343,19 @@ public class Query {
*/
@Deprecated
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, Class domainClass, boolean isCount) {
return toN1qlSelectString(template, null, null, domainClass, null, isCount, null, null);
return toN1qlSelectString(template.getConverter(), template.getBucketName(), null, null, domainClass, null, isCount,
null, null);
}
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String scopeName, String collectionName,
Class domainClass, Class returnClass, boolean isCount, String[] distinctFields, String[] fields) {
StringBasedN1qlQueryParser.N1qlSpelValues n1ql = getN1qlSpelValues(template, scopeName, collectionName, domainClass,
returnClass, isCount, distinctFields, fields);
public String toN1qlSelectString(CouchbaseConverter converter, String bucketName, String scopeName,
String collectionName, Class domainClass, Class returnClass, boolean isCount, String[] distinctFields,
String[] fields) {
StringBasedN1qlQueryParser.N1qlSpelValues n1ql = getN1qlSpelValues(converter, bucketName, scopeName, collectionName,
domainClass, returnClass, isCount, distinctFields, fields);
final StringBuilder statement = new StringBuilder();
appendString(statement, n1ql.selectEntity); // select ...
appendWhereString(statement, n1ql.filter); // typeKey = typeValue
appendWhere(statement, new int[] { 0 }, template.getConverter()); // criteria on this Query
appendWhere(statement, new int[] { 0 }, converter); // criteria on this Query
if (!isCount) {
appendSort(statement);
appendSkipAndLimit(statement);
@@ -361,35 +363,34 @@ public class Query {
return statement.toString();
}
public String toN1qlRemoveString(ReactiveCouchbaseTemplate template, String scopeName, String collectionName,
Class domainClass) {
StringBasedN1qlQueryParser.N1qlSpelValues n1ql = getN1qlSpelValues(template, scopeName, collectionName, domainClass,
null, false, null, null);
public String toN1qlRemoveString(CouchbaseConverter converter, String bucketName, String scopeName,
String collectionName, Class domainClass) {
StringBasedN1qlQueryParser.N1qlSpelValues n1ql = getN1qlSpelValues(converter, bucketName, scopeName, collectionName,
domainClass, null, false, null, null);
final StringBuilder statement = new StringBuilder();
appendString(statement, n1ql.delete); // delete ...
appendWhereString(statement, n1ql.filter); // typeKey = typeValue
appendWhere(statement, null, template.getConverter()); // criteria on this Query
appendWhere(statement, null, converter); // criteria on this Query
appendString(statement, n1ql.returning);
return statement.toString();
}
public static StringBasedN1qlQueryParser.N1qlSpelValues getN1qlSpelValues(ReactiveCouchbaseTemplate template,
String scopeName, String collectionName, Class domainClass, Class returnClass, boolean isCount,
public static StringBasedN1qlQueryParser.N1qlSpelValues getN1qlSpelValues(CouchbaseConverter converter,
String bucketName, String scopeName, String collectionName, Class domainClass, Class returnClass, boolean isCount,
String[] distinctFields, String[] fields) {
String typeKey = template.getConverter().getTypeKey();
final CouchbasePersistentEntity<?> persistentEntity = template.getConverter().getMappingContext()
String typeKey = converter.getTypeKey();
final CouchbasePersistentEntity<?> persistentEntity = converter.getMappingContext()
.getRequiredPersistentEntity(domainClass);
MappingCouchbaseEntityInformation<?, Object> info = new MappingCouchbaseEntityInformation<>(persistentEntity);
String typeValue = info.getJavaType().getName();
TypeInformation<?> typeInfo = ClassTypeInformation.from(info.getJavaType());
Alias alias = template.getConverter().getTypeAlias(typeInfo);
Alias alias = converter.getTypeAlias(typeInfo);
if (alias != null && alias.isPresent()) {
typeValue = alias.toString();
}
StringBasedN1qlQueryParser sbnqp = new StringBasedN1qlQueryParser(template.getBucketName(), scopeName,
collectionName, template.getConverter(), domainClass, returnClass, typeKey, typeValue, isCount, distinctFields,
fields);
StringBasedN1qlQueryParser sbnqp = new StringBasedN1qlQueryParser(bucketName, scopeName, collectionName, converter,
domainClass, returnClass, typeKey, typeValue, isCount, distinctFields, fields);
return sbnqp.getStatementContext();
}

View File

@@ -17,7 +17,7 @@ package org.springframework.data.couchbase.core.query;
import java.util.Locale;
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
import org.springframework.data.couchbase.core.support.TemplateUtils;
import org.springframework.data.couchbase.repository.query.CouchbaseQueryMethod;
@@ -66,10 +66,10 @@ public class StringQuery extends Query {
}
@Override
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String scope, String collection,
public String toN1qlSelectString(CouchbaseConverter converter, String bucketName, String scope, String collection,
Class domainClass, Class resultClass, boolean isCount, String[] distinctFields, String[] fields) {
StringBasedN1qlQueryParser parser = getStringN1qlQueryParser(template, scope, collection, domainClass,
StringBasedN1qlQueryParser parser = getStringN1qlQueryParser(converter, bucketName, scope, collection, domainClass,
distinctFields, fields);
N1QLExpression parsedExpression = parser.getExpression(inlineN1qlQuery, queryMethod, parameterAccessor,
@@ -100,7 +100,7 @@ public class StringQuery extends Query {
} else { // named parameters or no parameters, no index required
paramIndexPtr = new int[] { -1 };
}
appendWhere(statement, paramIndexPtr, template.getConverter()); // criteria on this Query - should be empty for
appendWhere(statement, paramIndexPtr, converter); // criteria on this Query - should be empty for
if (!isCount) {
appendSort(statement);
appendSkipAndLimit(statement);
@@ -111,22 +111,22 @@ public class StringQuery extends Query {
return statement.toString();
}
private StringBasedN1qlQueryParser getStringN1qlQueryParser(ReactiveCouchbaseTemplate template, String scopeName,
String collectionName, Class domainClass, String[] distinctFields, String[] fields) {
String typeKey = template.getConverter().getTypeKey();
final CouchbasePersistentEntity<?> persistentEntity = template.getConverter().getMappingContext()
private StringBasedN1qlQueryParser getStringN1qlQueryParser(CouchbaseConverter converter, String bucketName,
String scopeName, String collectionName, Class domainClass, String[] distinctFields, String[] fields) {
String typeKey = converter.getTypeKey();
final CouchbasePersistentEntity<?> persistentEntity = converter.getMappingContext()
.getRequiredPersistentEntity(domainClass);
MappingCouchbaseEntityInformation<?, Object> info = new MappingCouchbaseEntityInformation<>(persistentEntity);
String typeValue = info.getJavaType().getName();
TypeInformation<?> typeInfo = ClassTypeInformation.from(info.getJavaType());
Alias alias = template.getConverter().getTypeAlias(typeInfo);
Alias alias = converter.getTypeAlias(typeInfo);
if (alias != null && alias.isPresent()) {
typeValue = alias.toString();
}
// there are no options for distinct and fields for @Query
StringBasedN1qlQueryParser sbnqp = new StringBasedN1qlQueryParser(inlineN1qlQuery, queryMethod,
template.getBucketName(), scopeName, collectionName, template.getConverter(), typeKey, typeValue,
parameterAccessor, new SpelExpressionParser(), evaluationContextProvider);
StringBasedN1qlQueryParser sbnqp = new StringBasedN1qlQueryParser(inlineN1qlQuery, queryMethod, bucketName,
scopeName, collectionName, converter, typeKey, typeValue, parameterAccessor, new SpelExpressionParser(),
evaluationContextProvider);
return sbnqp;
}
@@ -139,8 +139,9 @@ public class StringQuery extends Query {
* @param domainClass
*/
@Override
public String toN1qlRemoveString(ReactiveCouchbaseTemplate template, String scopeName, String collectionName,
Class domainClass) {
return toN1qlSelectString(template, scopeName, collectionName, domainClass, domainClass, false, null, null);
public String toN1qlRemoveString(CouchbaseConverter converter, String bucketName, String scopeName,
String collectionName, Class domainClass) {
return toN1qlSelectString(converter, bucketName, scopeName, collectionName, domainClass, domainClass, false, null,
null);
}
}

View File

@@ -17,18 +17,12 @@ package org.springframework.data.couchbase.repository.query;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMPLATE;
import java.lang.reflect.Method;
import java.util.Properties;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.config.AbstractCouchbaseConfiguration;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.data.couchbase.core.convert.MappingCouchbaseConverter;
import org.springframework.data.couchbase.core.mapping.CouchbaseMappingContext;
@@ -37,8 +31,6 @@ import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProper
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.domain.User;
import org.springframework.data.couchbase.domain.UserRepository;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
import org.springframework.data.couchbase.util.ClusterAwareIntegrationTests;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.core.NamedQueries;
@@ -51,69 +43,20 @@ import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import com.couchbase.client.core.deps.io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import com.couchbase.client.core.env.SecurityConfig;
import com.couchbase.client.java.env.ClusterEnvironment;
/**
* @author Michael Nitschinger
* @author Michael Reiche
*/
class StringN1qlQueryCreatorMockedTests extends ClusterAwareIntegrationTests {
class StringN1qlQueryCreatorTests {
MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> context;
CouchbaseConverter converter;
CouchbaseTemplate couchbaseTemplate;
static NamedQueries namedQueries = new PropertiesBasedNamedQueries(new Properties());
@BeforeEach
public void beforeEach() {
context = new CouchbaseMappingContext();
converter = new MappingCouchbaseConverter(context);
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
couchbaseTemplate = (CouchbaseTemplate) ac.getBean(COUCHBASE_TEMPLATE);
}
@Test
void createsQueryCorrectly() throws Exception {
String input = "getByFirstnameAndLastname";
Method method = UserRepository.class.getMethod(input, String.class, String.class);
CouchbaseQueryMethod queryMethod = new CouchbaseQueryMethod(method,
new DefaultRepositoryMetadata(UserRepository.class), new SpelAwareProxyProjectionFactory(),
converter.getMappingContext());
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method), "Oliver", "Twist"),
queryMethod, converter, new SpelExpressionParser(), QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
Query query = creator.createQuery();
assertEquals(
"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
void createsQueryCorrectly2() throws Exception {
String input = "getByFirstnameOrLastname";
Method method = UserRepository.class.getMethod(input, String.class, String.class);
CouchbaseQueryMethod queryMethod = new CouchbaseQueryMethod(method,
new DefaultRepositoryMetadata(UserRepository.class), new SpelAwareProxyProjectionFactory(),
converter.getMappingContext());
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method), "Oliver", "Twist"),
queryMethod, converter, new SpelExpressionParser(), QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
Query query = creator.createQuery();
assertEquals(
"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
@@ -153,6 +96,48 @@ class StringN1qlQueryCreatorMockedTests extends ClusterAwareIntegrationTests {
fail("should have failed with IllegalArgumentException: query has no inline Query or named Query not found");
}
@Test
void createsQueryCorrectly() throws Exception {
String input = "getByFirstnameAndLastname";
Method method = UserRepository.class.getMethod(input, String.class, String.class);
CouchbaseQueryMethod queryMethod = new CouchbaseQueryMethod(method,
new DefaultRepositoryMetadata(UserRepository.class), new SpelAwareProxyProjectionFactory(),
converter.getMappingContext());
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method), "Oliver", "Twist"),
queryMethod, converter, new SpelExpressionParser(), QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
Query query = creator.createQuery();
assertEquals(
"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(converter, bucketName(), null, null, User.class, User.class, false, null, null));
}
@Test
void createsQueryCorrectly2() throws Exception {
String input = "getByFirstnameOrLastname";
Method method = UserRepository.class.getMethod(input, String.class, String.class);
CouchbaseQueryMethod queryMethod = new CouchbaseQueryMethod(method,
new DefaultRepositoryMetadata(UserRepository.class), new SpelAwareProxyProjectionFactory(),
converter.getMappingContext());
StringN1qlQueryCreator creator = new StringN1qlQueryCreator(getAccessor(getParameters(method), "Oliver", "Twist"),
queryMethod, converter, new SpelExpressionParser(), QueryMethodEvaluationContextProvider.DEFAULT, namedQueries);
Query query = creator.createQuery();
assertEquals(
"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(converter, bucketName(), null, null, User.class, User.class, false, null, null));
}
@Test
void spelTests() throws Exception {
String input = "spelTests";
@@ -166,10 +151,17 @@ class StringN1qlQueryCreatorMockedTests extends ClusterAwareIntegrationTests {
Query query = creator.createQuery();
String s = query.toN1qlSelectString(couchbaseTemplate.reactive(), "myScope", "myCollection", User.class, null,
false, null, null);
System.out.println("query: " + s);
assertEquals(
"SELECT `_class`, META(`myCollection`).`cas` AS __cas, `createdBy`, `createdDate`, "
+ "`lastModifiedBy`, `lastModifiedDate`, META(`myCollection`).`id` AS __id, `firstname`, "
+ "`lastname`, `subtype` FROM `myCollection`|`_class` = \"abstractuser\""
+ "|`myCollection`|`myScope`|`myCollection`",
query.toN1qlSelectString(converter, bucketName(), "myScope", "myCollection", User.class, null, false, null,
null));
}
private String bucketName() {
return "some_bucket";
}
private ParameterAccessor getAccessor(Parameters<?, ?> params, Object... values) {
@@ -180,37 +172,4 @@ class StringN1qlQueryCreatorMockedTests extends ClusterAwareIntegrationTests {
return new DefaultParameters(method);
}
@Configuration
@EnableCouchbaseRepositories("org.springframework.data.couchbase")
static class Config extends AbstractCouchbaseConfiguration {
@Override
public String getConnectionString() {
return connectionString();
}
@Override
public String getUserName() {
return config().adminUsername();
}
@Override
public String getPassword() {
return config().adminPassword();
}
@Override
public String getBucketName() {
return bucketName();
}
@Override
protected void configureEnvironment(ClusterEnvironment.Builder builder) {
if (config().isUsingCloud()) {
builder.securityConfig(
SecurityConfig.builder().trustManagerFactory(InsecureTrustManagerFactory.INSTANCE).enableTls(true));
}
}
}
}

View File

@@ -27,8 +27,6 @@ import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMP
import static org.springframework.data.couchbase.config.BeanNames.REACTIVE_COUCHBASE_TEMPLATE;
import static org.springframework.data.couchbase.util.Util.waitUntilCondition;
import com.couchbase.client.core.retry.BestEffortRetryStrategy;
import com.couchbase.client.core.retry.RetryStrategy;
import okhttp3.Credentials;
import okhttp3.FormBody;
import okhttp3.OkHttpClient;