Add collections support to N1qlJoin. (#1333)
The scope for the entity can come from an option, a method annotation, an annotation on the repository or an annotation on the entity class. All these possibilities are handle by PseudoArgs in the OperationSupport implementation. That scope/collection are passed into decodeEntity(). The scope/collection of the child can only come from an annotation on the entity class. The scope/collection of the parent and child are uses as follows: 1) Both the parent and the chold have non-default collections It's possible that the scope for the parent was set with an annotation on a repository method, the entity class or the repository class or a query option. Since there is no means to set the scope of the child class by the method, repository class or query option (only the annotation) we assume that the (possibly) dynamic scope of the entity would be a better choice as it is logical to put collections to be joined in the same scope. 2) The parent has a collection (and therefore a scope as well), but the child does not have a collection. Use the lhScope and lhCollection for the entity. The child is just the bucket. 3) The parent does not have a collection (or scope), but child does have a collection. Using the same (default) scope for the child would mean specifying a non-default collection in a default scope - which is not allowed. So use the scope and collection from the child class. 4) Neither have collections, just use the bucket. Closes #1325.
This commit is contained in:
committed by
Michael Reiche
parent
b04b95a027
commit
2e49741680
@@ -85,7 +85,7 @@ class CouchbaseTemplateSupport implements ApplicationContextAware, TemplateSuppo
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T decodeEntity(String id, String source, long cas, Class<T> entityClass) {
|
||||
public <T> T decodeEntity(String id, String source, long cas, Class<T> entityClass, String scope, String collection) {
|
||||
final CouchbaseDocument converted = new CouchbaseDocument(id);
|
||||
converted.setId(id);
|
||||
CouchbasePersistentEntity persistentEntity = couldBePersistentEntity(entityClass);
|
||||
@@ -110,9 +110,10 @@ class CouchbaseTemplateSupport implements ApplicationContextAware, TemplateSuppo
|
||||
if (persistentEntity.getVersionProperty() != null) {
|
||||
accessor.setProperty(persistentEntity.getVersionProperty(), cas);
|
||||
}
|
||||
N1qlJoinResolver.handleProperties(persistentEntity, accessor, template.reactive(), id);
|
||||
N1qlJoinResolver.handleProperties(persistentEntity, accessor, template.reactive(), id, scope, collection);
|
||||
return accessor.getBean();
|
||||
}
|
||||
|
||||
CouchbasePersistentEntity couldBePersistentEntity(Class<?> entityClass) {
|
||||
if (ClassUtils.isPrimitiveOrWrapper(entityClass) || entityClass == String.class) {
|
||||
return null;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors.
|
||||
* Copyright 2021-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.
|
||||
@@ -40,8 +40,9 @@ public class NonReactiveSupportWrapper implements ReactiveTemplateSupport {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> decodeEntity(String id, String source, long cas, Class<T> entityClass) {
|
||||
return Mono.fromSupplier(() -> support.decodeEntity(id, source, cas, entityClass));
|
||||
public <T> Mono<T> decodeEntity(String id, String source, long cas, Class<T> entityClass, String scope,
|
||||
String collection) {
|
||||
return Mono.fromSupplier(() -> support.decodeEntity(id, source, cas, entityClass, scope, collection));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -85,7 +85,8 @@ class ReactiveCouchbaseTemplateSupport implements ApplicationContextAware, React
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Mono<T> decodeEntity(String id, String source, long cas, Class<T> entityClass) {
|
||||
public <T> Mono<T> decodeEntity(String id, String source, long cas, Class<T> entityClass, String scope,
|
||||
String collection) {
|
||||
return Mono.fromSupplier(() -> {
|
||||
final CouchbaseDocument converted = new CouchbaseDocument(id);
|
||||
converted.setId(id);
|
||||
@@ -112,7 +113,7 @@ class ReactiveCouchbaseTemplateSupport implements ApplicationContextAware, React
|
||||
if (persistentEntity.getVersionProperty() != null) {
|
||||
accessor.setProperty(persistentEntity.getVersionProperty(), cas);
|
||||
}
|
||||
N1qlJoinResolver.handleProperties(persistentEntity, accessor, template, id);
|
||||
N1qlJoinResolver.handleProperties(persistentEntity, accessor, template, id, scope, collection);
|
||||
return accessor.getBean();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -133,7 +133,7 @@ public class ReactiveFindByAnalyticsOperationSupport implements ReactiveFindByAn
|
||||
cas = row.getLong(TemplateUtils.SELECT_CAS);
|
||||
row.removeKey(TemplateUtils.SELECT_ID);
|
||||
row.removeKey(TemplateUtils.SELECT_CAS);
|
||||
return support.decodeEntity(id, row.toString(), cas, returnType);
|
||||
return support.decodeEntity(id, row.toString(), cas, returnType, null, null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -90,8 +90,8 @@ public class ReactiveFindByIdOperationSupport implements ReactiveFindByIdOperati
|
||||
} else {
|
||||
return reactive.get(docId, (GetOptions) pArgs.getOptions());
|
||||
}
|
||||
}).flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), domainType))
|
||||
.onErrorResume(throwable -> {
|
||||
}).flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), domainType,
|
||||
pArgs.getScope(), pArgs.getCollection())).onErrorResume(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
if (throwable instanceof DocumentNotFoundException) {
|
||||
return Mono.empty();
|
||||
|
||||
@@ -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.
|
||||
@@ -202,7 +202,7 @@ public class ReactiveFindByQueryOperationSupport implements ReactiveFindByQueryO
|
||||
row.removeKey(TemplateUtils.SELECT_ID);
|
||||
row.removeKey(TemplateUtils.SELECT_CAS);
|
||||
}
|
||||
return support.decodeEntity(id, row.toString(), cas, returnType);
|
||||
return support.decodeEntity(id, row.toString(), cas, returnType, pArgs.getScope(), pArgs.getCollection());
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -77,7 +77,8 @@ public class ReactiveFindFromReplicasByIdOperationSupport implements ReactiveFin
|
||||
return Mono.just(id)
|
||||
.flatMap(docId -> template.getCouchbaseClientFactory().withScope(pArgs.getScope())
|
||||
.getCollection(pArgs.getCollection()).reactive().getAnyReplica(docId, pArgs.getOptions()))
|
||||
.flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), returnType))
|
||||
.flatMap(result -> support.decodeEntity(id, result.contentAs(String.class), result.cas(), returnType,
|
||||
pArgs.getScope(), pArgs.getCollection()))
|
||||
.onErrorMap(throwable -> {
|
||||
if (throwable instanceof RuntimeException) {
|
||||
return template.potentiallyConvertRuntimeException((RuntimeException) throwable);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
* Copyright 2021-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,11 +20,15 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
|
||||
import org.springframework.data.couchbase.core.mapping.event.CouchbaseMappingEvent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
public interface ReactiveTemplateSupport {
|
||||
|
||||
Mono<CouchbaseDocument> encodeEntity(Object entityToEncode);
|
||||
|
||||
<T> Mono<T> decodeEntity(String id, String source, long cas, Class<T> entityClass);
|
||||
<T> Mono<T> decodeEntity(String id, String source, long cas, Class<T> entityClass, String scope, String collection);
|
||||
|
||||
<T> Mono<T> applyUpdatedCas(T entity, CouchbaseDocument converted, long cas);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
* Copyright 2021-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.
|
||||
@@ -18,11 +18,15 @@ package org.springframework.data.couchbase.core;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
|
||||
import org.springframework.data.couchbase.core.mapping.event.CouchbaseMappingEvent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
public interface TemplateSupport {
|
||||
|
||||
CouchbaseDocument encodeEntity(Object entityToEncode);
|
||||
|
||||
<T> T decodeEntity(String id, String source, long cas, Class<T> entityClass);
|
||||
<T> T decodeEntity(String id, String source, long cas, Class<T> entityClass, String scope, String collection);
|
||||
|
||||
<T> T applyUpdatedCas(T entity, CouchbaseDocument converted, long cas);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2021 the original author or authors
|
||||
* Copyright 2018-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.
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
package org.springframework.data.couchbase.core.convert.join;
|
||||
|
||||
import static org.springframework.data.couchbase.core.query.N1QLExpression.i;
|
||||
import static org.springframework.data.couchbase.core.query.N1QLExpression.x;
|
||||
import static org.springframework.data.couchbase.core.support.TemplateUtils.SELECT_CAS;
|
||||
import static org.springframework.data.couchbase.core.support.TemplateUtils.SELECT_ID;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
@@ -33,13 +36,17 @@ import org.springframework.data.couchbase.core.query.HashSide;
|
||||
import org.springframework.data.couchbase.core.query.N1QLExpression;
|
||||
import org.springframework.data.couchbase.core.query.N1QLQuery;
|
||||
import org.springframework.data.couchbase.core.query.N1qlJoin;
|
||||
import org.springframework.data.couchbase.core.query.OptionsBuilder;
|
||||
import org.springframework.data.couchbase.core.query.Query;
|
||||
import org.springframework.data.couchbase.repository.Collection;
|
||||
import org.springframework.data.couchbase.repository.Scope;
|
||||
import org.springframework.data.couchbase.repository.query.StringBasedN1qlQueryParser;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.couchbase.client.core.io.CollectionIdentifier;
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
|
||||
/**
|
||||
@@ -50,7 +57,7 @@ import com.couchbase.client.java.query.QueryOptions;
|
||||
public class N1qlJoinResolver {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(N1qlJoinResolver.class);
|
||||
|
||||
public static String buildQuery(ReactiveCouchbaseTemplate template, String collectionName,
|
||||
public static <L, R> String buildQuery(ReactiveCouchbaseTemplate template, String scope, String collection,
|
||||
N1qlJoinResolverParameters parameters) {
|
||||
String joinType = "JOIN";
|
||||
String selectEntity = "SELECT META(rks).id AS " + SELECT_ID + ", META(rks).cas AS " + SELECT_CAS + ", (rks).* ";
|
||||
@@ -61,14 +68,16 @@ public class N1qlJoinResolver {
|
||||
}
|
||||
String useLKS = useLKSBuilder.length() > 0 ? "USE " + useLKSBuilder.toString() + " " : "";
|
||||
|
||||
String from = "FROM `" + template.getBucketName() + "` lks " + useLKS + joinType + " `" + template.getBucketName()
|
||||
+ "` rks";
|
||||
KeySpacePair keySpacePair = getKeySpacePair(template.getBucketName(), scope, collection, parameters);
|
||||
|
||||
StringBasedN1qlQueryParser.N1qlSpelValues n1qlL = Query.getN1qlSpelValues(template, collectionName,
|
||||
String from = "FROM " + keySpacePair.lhs.keyspace + " lks " + useLKS + joinType + " " + keySpacePair.rhs.keyspace
|
||||
+ " rks";
|
||||
|
||||
StringBasedN1qlQueryParser.N1qlSpelValues n1qlL = Query.getN1qlSpelValues(template, keySpacePair.lhs.collection,
|
||||
parameters.getEntityTypeInfo().getType(), parameters.getEntityTypeInfo().getType(), false, null, null);
|
||||
String onLks = "lks." + n1qlL.filter;
|
||||
|
||||
StringBasedN1qlQueryParser.N1qlSpelValues n1qlR = Query.getN1qlSpelValues(template, collectionName,
|
||||
StringBasedN1qlQueryParser.N1qlSpelValues n1qlR = Query.getN1qlSpelValues(template, keySpacePair.rhs.collection,
|
||||
parameters.getAssociatedEntityTypeInfo().getType(), parameters.getAssociatedEntityTypeInfo().getType(), false,
|
||||
null, null);
|
||||
String onRks = "rks." + n1qlR.filter;
|
||||
@@ -111,10 +120,92 @@ public class N1qlJoinResolver {
|
||||
return statementSb.toString();
|
||||
}
|
||||
|
||||
public static <R> List<R> doResolve(ReactiveCouchbaseTemplate template, String collectionName,
|
||||
static KeySpacePair getKeySpacePair(String bucketName, String scope, String collection,
|
||||
N1qlJoinResolverParameters parameters) {
|
||||
Class<?> lhsClass = parameters.getEntityTypeInfo().getActualType().getType();
|
||||
String lhScope = scope != null ? scope : getScope(lhsClass);
|
||||
String lhCollection = collection != null ? collection : getCollection(lhsClass);
|
||||
Class<?> rhsClass = parameters.getAssociatedEntityTypeInfo().getActualType().getType();
|
||||
String rhScope = getScope(rhsClass);
|
||||
String rhCollection = getCollection(rhsClass);
|
||||
if (lhCollection != null && rhCollection != null) {
|
||||
// they both have non-default collections
|
||||
// It's possible that the scope for the lhs was set with an annotation on a repository method,
|
||||
// the entity class or the repository class or a query option. Since there is no means to set
|
||||
// the scope of the associated class by the method, repository class or query option (only
|
||||
// the annotation) we assume that the (possibly) dynamic scope of the entity would be a better
|
||||
// choice as it is logical to put collections to be joined in the same scope. Note that lhScope
|
||||
// is used for both keyspaces.
|
||||
return new KeySpacePair(lhCollection, x(i(bucketName) + "." + i(lhScope) + "." + i(lhCollection)), //
|
||||
rhCollection, x(i(bucketName) + "." + i(lhScope) + "." + i(rhCollection)));
|
||||
} else if (lhCollection != null && rhCollection == null) {
|
||||
// the lhs has a collection (and therefore a scope as well), but the rhs does not have a collection.
|
||||
// Use the lhScope and lhCollection for the entity. The rhs is just the bucket.
|
||||
return new KeySpacePair(lhCollection, x(i(bucketName) + "." + i(lhScope) + "." + i(lhCollection)), //
|
||||
null, i(bucketName));
|
||||
} else if (lhCollection != null && rhCollection == null) {
|
||||
// the lhs does not have a collection (or scope), but rhs does have a collection
|
||||
// Using the same (default) scope for the rhs would mean specifying a
|
||||
// non-default collection in a default scope - which is not allowed.
|
||||
// So use the scope and collection from the associated class.
|
||||
return new KeySpacePair(null, i(bucketName), //
|
||||
rhCollection, x(i(bucketName) + "." + i(rhScope) + "." + i(rhCollection)));
|
||||
} else { // neither have collections, just use the bucket.
|
||||
return new KeySpacePair(null, i(bucketName), null, i(bucketName));
|
||||
}
|
||||
}
|
||||
|
||||
static class KeySpacePair {
|
||||
KeySpaceInfo lhs;
|
||||
KeySpaceInfo rhs;
|
||||
|
||||
public KeySpacePair(String lhsCollection, N1QLExpression lhsKeyspace, String rhsCollection,
|
||||
N1QLExpression rhsKeyspace) {
|
||||
this.lhs = new KeySpaceInfo(lhsCollection, lhsKeyspace);
|
||||
this.rhs = new KeySpaceInfo(rhsCollection, rhsKeyspace);
|
||||
}
|
||||
|
||||
static class KeySpaceInfo {
|
||||
String collection;
|
||||
N1QLExpression keyspace;
|
||||
|
||||
public KeySpaceInfo(String collection, N1QLExpression keyspace) {
|
||||
this.collection = collection;
|
||||
this.keyspace = keyspace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* from CouchbaseQueryMethod.getCollection()
|
||||
*
|
||||
* @param targetClass
|
||||
* @return
|
||||
*/
|
||||
static String getCollection(Class<?> targetClass) {
|
||||
// Could try the repository method, then the targetClass, then the repository class, then the entity class
|
||||
// but we don't have the repository method nor the repositoryMetdata at this point.
|
||||
AnnotatedElement[] annotated = new AnnotatedElement[] { targetClass };
|
||||
return OptionsBuilder.annotationString(Collection.class, CollectionIdentifier.DEFAULT_COLLECTION, annotated);
|
||||
}
|
||||
|
||||
/**
|
||||
* from CouchbaseQueryMethod.getScope()
|
||||
*
|
||||
* @param targetClass
|
||||
* @return
|
||||
*/
|
||||
static String getScope(Class<?> targetClass) {
|
||||
// Could try the repository method, then the targetClass, then the repository class, then the entity class
|
||||
// but we don't have the repository method nor the repositoryMetdata at this point.
|
||||
AnnotatedElement[] annotated = new AnnotatedElement[] { targetClass };
|
||||
return OptionsBuilder.annotationString(Scope.class, CollectionIdentifier.DEFAULT_SCOPE, annotated);
|
||||
}
|
||||
|
||||
public static <R> List<R> doResolve(ReactiveCouchbaseTemplate template, String scopeName, String collectionName,
|
||||
N1qlJoinResolverParameters parameters, Class<R> associatedEntityClass) {
|
||||
|
||||
String statement = buildQuery(template, collectionName, parameters);
|
||||
String statement = buildQuery(template, scopeName, collectionName, parameters);
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Join query executed " + statement);
|
||||
@@ -130,20 +221,24 @@ public class N1qlJoinResolver {
|
||||
}
|
||||
|
||||
public static void handleProperties(CouchbasePersistentEntity<?> persistentEntity,
|
||||
ConvertingPropertyAccessor<?> accessor, ReactiveCouchbaseTemplate template, String id) {
|
||||
ConvertingPropertyAccessor<?> accessor, ReactiveCouchbaseTemplate template, String id, String scope,
|
||||
String collection) {
|
||||
persistentEntity.doWithProperties((PropertyHandler<CouchbasePersistentProperty>) prop -> {
|
||||
if (prop.isAnnotationPresent(N1qlJoin.class)) {
|
||||
N1qlJoin definition = prop.findAnnotation(N1qlJoin.class);
|
||||
TypeInformation type = prop.getTypeInformation().getActualType();
|
||||
Class clazz = type.getType();
|
||||
N1qlJoinResolver.N1qlJoinResolverParameters parameters = new N1qlJoinResolver.N1qlJoinResolverParameters(
|
||||
definition, id, persistentEntity.getTypeInformation(), type);
|
||||
definition, id, persistentEntity.getTypeInformation(), type, scope, collection);
|
||||
if (N1qlJoinResolver.isLazyJoin(definition)) {
|
||||
N1qlJoinResolver.N1qlJoinProxy proxy = new N1qlJoinResolver.N1qlJoinProxy(template, parameters);
|
||||
accessor.setProperty(prop,
|
||||
java.lang.reflect.Proxy.newProxyInstance(List.class.getClassLoader(), new Class[] { List.class }, proxy));
|
||||
} else {
|
||||
accessor.setProperty(prop, N1qlJoinResolver.doResolve(template, null, parameters, clazz));
|
||||
// clazz needs to be passes instead of just using
|
||||
// parameters.associatedType.getTypeInformation().getActualType().getType
|
||||
// to keep the compiler happy for the call template.findByQuery(associatedEntityClass)
|
||||
accessor.setProperty(prop, N1qlJoinResolver.doResolve(template, scope, collection, parameters, clazz));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -152,6 +247,7 @@ public class N1qlJoinResolver {
|
||||
static public class N1qlJoinProxy implements InvocationHandler {
|
||||
private final ReactiveCouchbaseTemplate reactiveTemplate;
|
||||
private final String collectionName = null;
|
||||
private final String scopeName = null;
|
||||
private final N1qlJoinResolverParameters params;
|
||||
private List<?> resolved = null;
|
||||
|
||||
@@ -163,8 +259,8 @@ public class N1qlJoinResolver {
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if (this.resolved == null) {
|
||||
this.resolved = doResolve(this.reactiveTemplate, collectionName, this.params,
|
||||
this.params.associatedEntityTypeInfo.getType());
|
||||
this.resolved = doResolve(this.reactiveTemplate, this.params.getScopeName(), this.params.getCollectionName(),
|
||||
this.params, this.params.associatedEntityTypeInfo.getType());
|
||||
}
|
||||
return method.invoke(this.resolved, args);
|
||||
}
|
||||
@@ -175,9 +271,11 @@ public class N1qlJoinResolver {
|
||||
private String lksId;
|
||||
private TypeInformation<?> entityTypeInfo;
|
||||
private TypeInformation<?> associatedEntityTypeInfo;
|
||||
private String scopeName;
|
||||
private String collectionName;
|
||||
|
||||
public N1qlJoinResolverParameters(N1qlJoin joinDefinition, String lksId, TypeInformation<?> entityTypeInfo,
|
||||
TypeInformation<?> associatedEntityTypeInfo) {
|
||||
TypeInformation<?> associatedEntityTypeInfo, String scopeName, String collectionName) {
|
||||
Assert.notNull(joinDefinition, "The join definition is required");
|
||||
Assert.notNull(entityTypeInfo, "The entity type information is required");
|
||||
Assert.notNull(associatedEntityTypeInfo, "The associated entity type information is required");
|
||||
@@ -186,6 +284,8 @@ public class N1qlJoinResolver {
|
||||
this.lksId = lksId;
|
||||
this.entityTypeInfo = entityTypeInfo;
|
||||
this.associatedEntityTypeInfo = associatedEntityTypeInfo;
|
||||
this.scopeName = scopeName;
|
||||
this.collectionName = collectionName;
|
||||
}
|
||||
|
||||
public N1qlJoin getJoinDefinition() {
|
||||
@@ -203,5 +303,13 @@ public class N1qlJoinResolver {
|
||||
public TypeInformation getAssociatedEntityTypeInfo() {
|
||||
return associatedEntityTypeInfo;
|
||||
}
|
||||
|
||||
public String getScopeName() {
|
||||
return scopeName;
|
||||
}
|
||||
|
||||
public String getCollectionName() {
|
||||
return collectionName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2020-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://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.domain;
|
||||
|
||||
import org.springframework.data.couchbase.repository.Collection;
|
||||
import org.springframework.data.couchbase.repository.Scope;
|
||||
|
||||
/**
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
@Scope("dummy_scope") // set to non-existing scope. To use, scope must be determined by other means
|
||||
// a different collection
|
||||
@Collection("my_collection2")
|
||||
public class AddressAnnotated extends Address {}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2020-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://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.domain;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.data.annotation.TypeAlias;
|
||||
import org.springframework.data.couchbase.core.mapping.Document;
|
||||
import org.springframework.data.couchbase.core.mapping.Field;
|
||||
import org.springframework.data.couchbase.core.query.FetchType;
|
||||
import org.springframework.data.couchbase.core.query.N1qlJoin;
|
||||
import org.springframework.data.couchbase.repository.Collection;
|
||||
import org.springframework.data.couchbase.repository.Scope;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* UserSubmissionAnnotated entity for tests
|
||||
*
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
@Data
|
||||
@Document
|
||||
@TypeAlias("user")
|
||||
@Scope("my_scope")
|
||||
@Collection("my_collection")
|
||||
public class UserSubmissionAnnotated extends ComparableEntity {
|
||||
private String id;
|
||||
private String username;
|
||||
private String email;
|
||||
private String password;
|
||||
private List<String> roles;
|
||||
@N1qlJoin(on = "meta(lks).id=rks.parentId", fetchType = FetchType.IMMEDIATE) List<AddressAnnotated> otherAddresses;
|
||||
private Address address;
|
||||
private int credits;
|
||||
private List<Submission> submissions;
|
||||
private List<Course> courses;
|
||||
|
||||
public void setSubmissions(List<Submission> submissions) {
|
||||
this.submissions = submissions;
|
||||
}
|
||||
|
||||
public void setCourses(List<Course> courses) {
|
||||
this.courses = courses;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://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.domain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.couchbase.repository.ScanConsistency;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
|
||||
/**
|
||||
* UserSubmissionAnnotatedRepository for tests
|
||||
*
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
@Repository
|
||||
public interface UserSubmissionAnnotatedRepository extends PagingAndSortingRepository<UserSubmissionAnnotated, String> {
|
||||
|
||||
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
|
||||
List<UserSubmissionAnnotated> findByUsername(String username);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2020-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://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.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.annotation.TypeAlias;
|
||||
import org.springframework.data.couchbase.core.mapping.Document;
|
||||
import org.springframework.data.couchbase.core.query.FetchType;
|
||||
import org.springframework.data.couchbase.core.query.N1qlJoin;
|
||||
import org.springframework.data.couchbase.repository.Collection;
|
||||
|
||||
/**
|
||||
* UserSubmissionAnnotated entity for tests
|
||||
*
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
@Data
|
||||
@Document
|
||||
// there is no @Scope annotation on this entity
|
||||
@Collection("my_collection")
|
||||
@TypeAlias("user")
|
||||
public class UserSubmissionUnannotated extends ComparableEntity {
|
||||
private String id;
|
||||
private String username;
|
||||
private String email;
|
||||
private String password;
|
||||
private List<String> roles;
|
||||
@N1qlJoin(on = "meta(lks).id=rks.parentId", fetchType = FetchType.IMMEDIATE) List<AddressAnnotated> otherAddresses;
|
||||
private Address address;
|
||||
private int credits;
|
||||
private List<Submission> submissions;
|
||||
private List<Course> courses;
|
||||
|
||||
public void setSubmissions(List<Submission> submissions) {
|
||||
this.submissions = submissions;
|
||||
}
|
||||
|
||||
public void setCourses(List<Course> courses) {
|
||||
this.courses = courses;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2020-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://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.domain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.couchbase.repository.ScanConsistency;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
|
||||
/**
|
||||
* UserSubmissionAnnotatedRepository for tests
|
||||
*
|
||||
* @author Michael Reiche
|
||||
*/
|
||||
@Repository
|
||||
public interface UserSubmissionUnannotatedRepository
|
||||
extends PagingAndSortingRepository<UserSubmissionUnannotated, String> {
|
||||
|
||||
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
|
||||
List<UserSubmissionUnannotated> findByUsername(String username);
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.data.couchbase.repository;
|
||||
|
||||
|
||||
|
||||
import static com.couchbase.client.java.query.QueryScanConsistency.REQUEST_PLUS;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
||||
@@ -903,9 +903,6 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
|
||||
@Test
|
||||
void findPlusN1qlJoin() throws Exception {
|
||||
|
||||
// needs an index for this N1ql Join
|
||||
// create index ix2 on my_bucket(parent_id) where `_class` = 'org.springframework.data.couchbase.domain.Address';
|
||||
|
||||
UserSubmission user = new UserSubmission();
|
||||
user.setId(UUID.randomUUID().toString());
|
||||
user.setUsername("dave");
|
||||
@@ -944,7 +941,8 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
|
||||
}
|
||||
|
||||
couchbaseTemplate.removeById(Address.class)
|
||||
.all(Arrays.asList(address1.getId(), address2.getId(), address3.getId(), user.getId()));
|
||||
.all(Arrays.asList(address1.getId(), address2.getId(), address3.getId()));
|
||||
couchbaseTemplate.removeById(UserSubmission.class).one(user.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -19,7 +19,9 @@ import static com.couchbase.client.core.io.CollectionIdentifier.DEFAULT_SCOPE;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
@@ -30,12 +32,18 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.dao.DataRetrievalFailureException;
|
||||
import org.springframework.data.couchbase.domain.Address;
|
||||
import org.springframework.data.couchbase.domain.AddressAnnotated;
|
||||
import org.springframework.data.couchbase.domain.Airport;
|
||||
import org.springframework.data.couchbase.domain.AirportRepository;
|
||||
import org.springframework.data.couchbase.domain.Config;
|
||||
import org.springframework.data.couchbase.domain.User;
|
||||
import org.springframework.data.couchbase.domain.UserCol;
|
||||
import org.springframework.data.couchbase.domain.UserColRepository;
|
||||
import org.springframework.data.couchbase.domain.UserSubmissionAnnotated;
|
||||
import org.springframework.data.couchbase.domain.UserSubmissionAnnotatedRepository;
|
||||
import org.springframework.data.couchbase.domain.UserSubmissionUnannotated;
|
||||
import org.springframework.data.couchbase.domain.UserSubmissionUnannotatedRepository;
|
||||
import org.springframework.data.couchbase.util.Capabilities;
|
||||
import org.springframework.data.couchbase.util.ClusterType;
|
||||
import org.springframework.data.couchbase.util.CollectionAwareIntegrationTests;
|
||||
@@ -50,8 +58,10 @@ import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
@IgnoreWhen(missesCapabilities = { Capabilities.QUERY, Capabilities.COLLECTIONS }, clusterTypes = ClusterType.MOCKED)
|
||||
public class CouchbaseRepositoryQueryCollectionIntegrationTests extends CollectionAwareIntegrationTests {
|
||||
|
||||
@Autowired AirportRepository airportRepository;
|
||||
@Autowired UserColRepository userColRepository;
|
||||
@Autowired AirportRepository airportRepository; // initialized in beforeEach()
|
||||
@Autowired UserColRepository userColRepository; // initialized in beforeEach()
|
||||
@Autowired UserSubmissionAnnotatedRepository userSubmissionAnnotatedRepository; // initialized in beforeEach()
|
||||
@Autowired UserSubmissionUnannotatedRepository userSubmissionUnannotatedRepository; // initialized in beforeEach()
|
||||
|
||||
@BeforeAll
|
||||
public static void beforeAll() {
|
||||
@@ -80,6 +90,10 @@ public class CouchbaseRepositoryQueryCollectionIntegrationTests extends Collecti
|
||||
// seems that @Autowired is not adequate, so ...
|
||||
airportRepository = (AirportRepository) ac.getBean("airportRepository");
|
||||
userColRepository = (UserColRepository) ac.getBean("userColRepository");
|
||||
userSubmissionAnnotatedRepository = (UserSubmissionAnnotatedRepository) ac
|
||||
.getBean("userSubmissionAnnotatedRepository");
|
||||
userSubmissionUnannotatedRepository = (UserSubmissionUnannotatedRepository) ac
|
||||
.getBean("userSubmissionUnannotatedRepository");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@@ -233,4 +247,116 @@ public class CouchbaseRepositoryQueryCollectionIntegrationTests extends Collecti
|
||||
} catch (DataRetrievalFailureException drfe) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void findPlusN1qlJoinBothAnnotated() throws Exception {
|
||||
|
||||
// UserSubmissionAnnotated has scope=my_scope, collection=my_collection
|
||||
UserSubmissionAnnotated user = new UserSubmissionAnnotated();
|
||||
user.setId(UUID.randomUUID().toString());
|
||||
user.setUsername("dave");
|
||||
user = userSubmissionAnnotatedRepository.save(user);
|
||||
|
||||
// AddressesAnnotated has scope=dummy_scope, collection=my_collection2
|
||||
// scope must be explicitly set on template insertById, findByQuery and removeById
|
||||
// For userSubmissionAnnotatedRepository.findByUsername(), scope will be taken from UserSubmissionAnnotated
|
||||
AddressAnnotated address1 = new AddressAnnotated();
|
||||
address1.setId(UUID.randomUUID().toString());
|
||||
address1.setStreet("3250 Olcott Street");
|
||||
address1.setParentId(user.getId());
|
||||
AddressAnnotated address2 = new AddressAnnotated();
|
||||
address2.setId(UUID.randomUUID().toString());
|
||||
address2.setStreet("148 Castro Street");
|
||||
address2.setParentId(user.getId());
|
||||
AddressAnnotated address3 = new AddressAnnotated();
|
||||
address3.setId(UUID.randomUUID().toString());
|
||||
address3.setStreet("123 Sesame Street");
|
||||
address3.setParentId(UUID.randomUUID().toString()); // does not belong to user
|
||||
|
||||
try {
|
||||
|
||||
address1 = couchbaseTemplate.insertById(AddressAnnotated.class).inScope(scopeName).one(address1);
|
||||
address2 = couchbaseTemplate.insertById(AddressAnnotated.class).inScope(scopeName).one(address2);
|
||||
address3 = couchbaseTemplate.insertById(AddressAnnotated.class).inScope(scopeName).one(address3);
|
||||
couchbaseTemplate.findByQuery(AddressAnnotated.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
|
||||
.inScope(scopeName).all();
|
||||
|
||||
// scope for AddressesAnnotated in N1qlJoin comes from userSubmissionAnnotatedRepository.
|
||||
List<UserSubmissionAnnotated> users = userSubmissionAnnotatedRepository.findByUsername(user.getUsername());
|
||||
assertEquals(2, users.get(0).getOtherAddresses().size());
|
||||
for (Address a : users.get(0).getOtherAddresses()) {
|
||||
if (!(a.getStreet().equals(address1.getStreet()) || a.getStreet().equals(address2.getStreet()))) {
|
||||
throw new Exception("street does not match : " + a);
|
||||
}
|
||||
}
|
||||
|
||||
UserSubmissionAnnotated foundUser = userSubmissionAnnotatedRepository.findById(user.getId()).get();
|
||||
assertEquals(2, foundUser.getOtherAddresses().size());
|
||||
for (Address a : foundUser.getOtherAddresses()) {
|
||||
if (!(a.getStreet().equals(address1.getStreet()) || a.getStreet().equals(address2.getStreet()))) {
|
||||
throw new Exception("street does not match : " + a);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
couchbaseTemplate.removeById(AddressAnnotated.class).inScope(scopeName)
|
||||
.all(Arrays.asList(address1.getId(), address2.getId(), address3.getId()));
|
||||
couchbaseTemplate.removeById(UserSubmissionAnnotated.class).one(user.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void findPlusN1qlJoinUnannotated() throws Exception {
|
||||
// UserSubmissionAnnotated has scope=my_scope, collection=my_collection
|
||||
UserSubmissionUnannotated user = new UserSubmissionUnannotated();
|
||||
user.setId(UUID.randomUUID().toString());
|
||||
user.setUsername("dave");
|
||||
user = userSubmissionUnannotatedRepository.save(user);
|
||||
|
||||
// AddressesAnnotated has scope=dummy_scope, collection=my_collection2
|
||||
// scope must be explicitly set on template insertById, findByQuery and removeById
|
||||
// For userSubmissionAnnotatedRepository.findByUsername(), scope will be taken from UserSubmissionAnnotated
|
||||
AddressAnnotated address1 = new AddressAnnotated();
|
||||
address1.setId(UUID.randomUUID().toString());
|
||||
address1.setStreet("3250 Olcott Street");
|
||||
address1.setParentId(user.getId());
|
||||
AddressAnnotated address2 = new AddressAnnotated();
|
||||
address2.setId(UUID.randomUUID().toString());
|
||||
address2.setStreet("148 Castro Street");
|
||||
address2.setParentId(user.getId());
|
||||
AddressAnnotated address3 = new AddressAnnotated();
|
||||
address3.setId(UUID.randomUUID().toString());
|
||||
address3.setStreet("123 Sesame Street");
|
||||
address3.setParentId(UUID.randomUUID().toString()); // does not belong to user
|
||||
|
||||
try {
|
||||
|
||||
address1 = couchbaseTemplate.insertById(AddressAnnotated.class).inScope(scopeName).one(address1);
|
||||
address2 = couchbaseTemplate.insertById(AddressAnnotated.class).inScope(scopeName).one(address2);
|
||||
address3 = couchbaseTemplate.insertById(AddressAnnotated.class).inScope(scopeName).one(address3);
|
||||
couchbaseTemplate.findByQuery(AddressAnnotated.class).withConsistency(QueryScanConsistency.REQUEST_PLUS)
|
||||
.inScope(scopeName).all();
|
||||
|
||||
// scope for AddressesAnnotated in N1qlJoin comes from userSubmissionAnnotatedRepository.
|
||||
List<UserSubmissionUnannotated> users = userSubmissionUnannotatedRepository.findByUsername(user.getUsername());
|
||||
assertEquals(2, users.get(0).getOtherAddresses().size());
|
||||
for (Address a : users.get(0).getOtherAddresses()) {
|
||||
if (!(a.getStreet().equals(address1.getStreet()) || a.getStreet().equals(address2.getStreet()))) {
|
||||
throw new Exception("street does not match : " + a);
|
||||
}
|
||||
}
|
||||
|
||||
UserSubmissionUnannotated foundUser = userSubmissionUnannotatedRepository.findById(user.getId()).get();
|
||||
assertEquals(2, foundUser.getOtherAddresses().size());
|
||||
for (Address a : foundUser.getOtherAddresses()) {
|
||||
if (!(a.getStreet().equals(address1.getStreet()) || a.getStreet().equals(address2.getStreet()))) {
|
||||
throw new Exception("street does not match : " + a);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
couchbaseTemplate.removeById(AddressAnnotated.class).inScope(scopeName)
|
||||
.all(Arrays.asList(address1.getId(), address2.getId(), address3.getId()));
|
||||
couchbaseTemplate.removeById(UserSubmissionUnannotated.class).one(user.getId());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 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.
|
||||
@@ -51,7 +51,7 @@ import com.couchbase.client.java.manager.query.CreateQueryIndexOptions;
|
||||
public abstract class ClusterAwareIntegrationTests {
|
||||
|
||||
private static TestClusterConfig testClusterConfig;
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ClusterAwareIntegrationTests.class);
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger(ClusterAwareIntegrationTests.class);
|
||||
|
||||
@BeforeAll
|
||||
static void setup(TestClusterConfig config) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2021 the original author or authors
|
||||
* Copyright 2021-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.
|
||||
@@ -19,8 +19,8 @@ import static org.springframework.data.couchbase.config.BeanNames.COUCHBASE_TEMP
|
||||
import static org.springframework.data.couchbase.config.BeanNames.REACTIVE_COUCHBASE_TEMPLATE;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
@@ -30,14 +30,13 @@ import org.springframework.data.couchbase.core.CouchbaseTemplate;
|
||||
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
|
||||
import org.springframework.data.couchbase.domain.Config;
|
||||
|
||||
import com.couchbase.client.core.error.IndexExistsException;
|
||||
import com.couchbase.client.core.service.ServiceType;
|
||||
import com.couchbase.client.java.Bucket;
|
||||
import com.couchbase.client.java.Cluster;
|
||||
import com.couchbase.client.java.ClusterOptions;
|
||||
import com.couchbase.client.java.env.ClusterEnvironment;
|
||||
import com.couchbase.client.java.manager.collection.CollectionManager;
|
||||
import com.couchbase.client.java.manager.collection.CollectionSpec;
|
||||
import com.couchbase.client.java.manager.collection.ScopeSpec;
|
||||
|
||||
/**
|
||||
* Provides Collection support for integration tests
|
||||
@@ -49,6 +48,7 @@ public class CollectionAwareIntegrationTests extends JavaIntegrationTests {
|
||||
public static String scopeName = "my_scope";// + randomString();
|
||||
public static String otherScope = "other_scope";
|
||||
public static String collectionName = "my_collection";// + randomString();
|
||||
public static String collectionName2 = "my_collection2";// + randomString();
|
||||
public static String otherCollection = "other_collection";// + randomString();
|
||||
|
||||
@BeforeAll
|
||||
@@ -64,11 +64,25 @@ public class CollectionAwareIntegrationTests extends JavaIntegrationTests {
|
||||
CollectionManager collectionManager = bucket.collections();
|
||||
|
||||
setupScopeCollection(cluster, scopeName, collectionName, collectionManager);
|
||||
setupScopeCollection(cluster, scopeName, collectionName2, collectionManager);
|
||||
|
||||
if (otherScope != null || otherCollection != null) {
|
||||
// afterAll should be undoing the creation of scope etc
|
||||
setupScopeCollection(cluster, otherScope, otherCollection, collectionManager);
|
||||
}
|
||||
|
||||
try {
|
||||
// needs an index for this N1ql Join
|
||||
// create index ix2 on my_bucket(parent_id) where `_class` = 'org.springframework.data.couchbase.domain.Address';
|
||||
|
||||
List<String> fieldList = new ArrayList<>();
|
||||
fieldList.add("parentId");
|
||||
cluster.query("CREATE INDEX `parent_idx` ON default:" + bucketName() + "." + scopeName + "." + collectionName2
|
||||
+ "(parentId)");
|
||||
} catch (IndexExistsException ife) {
|
||||
LOGGER.warn("IndexFailureException occurred - ignoring: ", ife.toString());
|
||||
}
|
||||
|
||||
Config.setScopeName(scopeName);
|
||||
ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
|
||||
// the Config class has been modified, these need to be loaded again
|
||||
|
||||
Reference in New Issue
Block a user