Support N1QL Join Annotations. (#1211)
* Support N1QL Join Annotations. Closes #1198.
This commit is contained in:
@@ -58,7 +58,7 @@ public class CouchbaseTemplate implements CouchbaseOperations, ApplicationContex
|
||||
final TranslationService translationService) {
|
||||
this.clientFactory = clientFactory;
|
||||
this.converter = converter;
|
||||
this.templateSupport = new CouchbaseTemplateSupport(converter, translationService);
|
||||
this.templateSupport = new CouchbaseTemplateSupport(this, converter, translationService);
|
||||
this.reactiveCouchbaseTemplate = new ReactiveCouchbaseTemplate(clientFactory, converter, translationService);
|
||||
|
||||
this.mappingContext = this.converter.getMappingContext();
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
|
||||
import org.springframework.data.couchbase.core.convert.join.N1qlJoinResolver;
|
||||
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
|
||||
@@ -53,13 +54,16 @@ class CouchbaseTemplateSupport implements ApplicationContextAware, TemplateSuppo
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CouchbaseTemplateSupport.class);
|
||||
|
||||
private final CouchbaseTemplate template;
|
||||
private final CouchbaseConverter converter;
|
||||
private final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
|
||||
private final TranslationService translationService;
|
||||
private EntityCallbacks entityCallbacks;
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
public CouchbaseTemplateSupport(final CouchbaseConverter converter, final TranslationService translationService) {
|
||||
public CouchbaseTemplateSupport(final CouchbaseTemplate template, final CouchbaseConverter converter,
|
||||
final TranslationService translationService) {
|
||||
this.template = template;
|
||||
this.converter = converter;
|
||||
this.mappingContext = converter.getMappingContext();
|
||||
this.translationService = translationService;
|
||||
@@ -91,6 +95,7 @@ class CouchbaseTemplateSupport implements ApplicationContextAware, TemplateSuppo
|
||||
if (persistentEntity.getVersionProperty() != null) {
|
||||
accessor.setProperty(persistentEntity.getVersionProperty(), cas);
|
||||
}
|
||||
N1qlJoinResolver.handleProperties(persistentEntity, accessor, template.reactive(), id);
|
||||
return accessor.getBean();
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public class ReactiveCouchbaseTemplate implements ReactiveCouchbaseOperations, A
|
||||
this.clientFactory = clientFactory;
|
||||
this.converter = converter;
|
||||
this.exceptionTranslator = clientFactory.getExceptionTranslator();
|
||||
this.templateSupport = new ReactiveCouchbaseTemplateSupport(converter, translationService);
|
||||
this.templateSupport = new ReactiveCouchbaseTemplateSupport(this, converter, translationService);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
|
||||
import org.springframework.data.couchbase.core.convert.join.N1qlJoinResolver;
|
||||
import org.springframework.data.couchbase.core.convert.translation.TranslationService;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
|
||||
@@ -52,14 +53,16 @@ class ReactiveCouchbaseTemplateSupport implements ApplicationContextAware, React
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ReactiveCouchbaseTemplateSupport.class);
|
||||
|
||||
private final ReactiveCouchbaseTemplate template;
|
||||
private final CouchbaseConverter converter;
|
||||
private final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
|
||||
private final TranslationService translationService;
|
||||
private ReactiveEntityCallbacks reactiveEntityCallbacks;
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
public ReactiveCouchbaseTemplateSupport(final CouchbaseConverter converter,
|
||||
public ReactiveCouchbaseTemplateSupport(final ReactiveCouchbaseTemplate template, final CouchbaseConverter converter,
|
||||
final TranslationService translationService) {
|
||||
this.template = template;
|
||||
this.converter = converter;
|
||||
this.mappingContext = converter.getMappingContext();
|
||||
this.translationService = translationService;
|
||||
@@ -92,6 +95,7 @@ class ReactiveCouchbaseTemplateSupport implements ApplicationContextAware, React
|
||||
if (persistentEntity.getVersionProperty() != null) {
|
||||
accessor.setProperty(persistentEntity.getVersionProperty(), cas);
|
||||
}
|
||||
N1qlJoinResolver.handleProperties(persistentEntity, accessor, template, id);
|
||||
return accessor.getBean();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2020 the original author or authors
|
||||
* Copyright 2018-2021 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,7 +16,8 @@
|
||||
|
||||
package org.springframework.data.couchbase.core.convert.join;
|
||||
|
||||
import static org.springframework.data.couchbase.core.support.TemplateUtils.*;
|
||||
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.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -24,13 +25,23 @@ import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.couchbase.core.CouchbaseTemplate;
|
||||
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentEntity;
|
||||
import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProperty;
|
||||
import org.springframework.data.couchbase.core.query.FetchType;
|
||||
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.Query;
|
||||
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.java.query.QueryOptions;
|
||||
|
||||
/**
|
||||
* N1qlJoinResolver resolves by converting the join definition to query statement and executing using CouchbaseTemplate
|
||||
*
|
||||
@@ -39,7 +50,8 @@ import org.springframework.util.Assert;
|
||||
public class N1qlJoinResolver {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(N1qlJoinResolver.class);
|
||||
|
||||
public static String buildQuery(CouchbaseTemplate template, N1qlJoinResolverParameters parameters) {
|
||||
public static String buildQuery(ReactiveCouchbaseTemplate template, String collectionName,
|
||||
N1qlJoinResolverParameters parameters) {
|
||||
String joinType = "JOIN";
|
||||
String selectEntity = "SELECT META(rks).id AS " + SELECT_ID + ", META(rks).cas AS " + SELECT_CAS + ", (rks).* ";
|
||||
|
||||
@@ -51,10 +63,15 @@ public class N1qlJoinResolver {
|
||||
|
||||
String from = "FROM `" + template.getBucketName() + "` lks " + useLKS + joinType + " `" + template.getBucketName()
|
||||
+ "` rks";
|
||||
String onLks = "lks." + template.getConverter().getTypeKey() + " = \""
|
||||
+ parameters.getEntityTypeInfo().getType().getName() + "\"";
|
||||
String onRks = "rks." + template.getConverter().getTypeKey() + " = \""
|
||||
+ parameters.getAssociatedEntityTypeInfo().getType().getName() + "\"";
|
||||
|
||||
StringBasedN1qlQueryParser.N1qlSpelValues n1qlL = Query.getN1qlSpelValues(template, collectionName,
|
||||
parameters.getEntityTypeInfo().getType(), parameters.getEntityTypeInfo().getType(), false, null, null);
|
||||
String onLks = "lks." + n1qlL.filter;
|
||||
|
||||
StringBasedN1qlQueryParser.N1qlSpelValues n1qlR = Query.getN1qlSpelValues(template, collectionName,
|
||||
parameters.getAssociatedEntityTypeInfo().getType(), parameters.getAssociatedEntityTypeInfo().getType(), false,
|
||||
null, null);
|
||||
String onRks = "rks." + n1qlR.filter;
|
||||
|
||||
StringBuilder useRKSBuilder = new StringBuilder();
|
||||
if (parameters.getJoinDefinition().rightIndex().length() > 0) {
|
||||
@@ -94,38 +111,60 @@ public class N1qlJoinResolver {
|
||||
return statementSb.toString();
|
||||
}
|
||||
|
||||
public static <R> List<R> doResolve(CouchbaseTemplate template, N1qlJoinResolverParameters parameters,
|
||||
Class<R> associatedEntityClass) {
|
||||
throw new UnsupportedOperationException();
|
||||
/*
|
||||
String statement = buildQuery(template, parameters);
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Join query executed " + statement);
|
||||
}
|
||||
|
||||
N1QLQuery query = new N1QLQuery(N1QLExpression.x(statement), QueryOptions.queryOptions());
|
||||
return template.findByN1QL(query, associatedEntityClass);*/
|
||||
public static <R> List<R> doResolve(ReactiveCouchbaseTemplate template, String collectionName,
|
||||
N1qlJoinResolverParameters parameters, Class<R> associatedEntityClass) {
|
||||
|
||||
String statement = buildQuery(template, collectionName, parameters);
|
||||
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Join query executed " + statement);
|
||||
}
|
||||
|
||||
N1QLQuery query = new N1QLQuery(N1QLExpression.x(statement), QueryOptions.queryOptions());
|
||||
List<R> result = template.findByQuery(associatedEntityClass).matching(query).all().collectList().block();
|
||||
return result.isEmpty() ? null : result;
|
||||
}
|
||||
|
||||
public static boolean isLazyJoin(N1qlJoin joinDefinition) {
|
||||
return joinDefinition.fetchType().equals(FetchType.LAZY);
|
||||
}
|
||||
|
||||
public static void handleProperties(CouchbasePersistentEntity<?> persistentEntity,
|
||||
ConvertingPropertyAccessor<?> accessor, ReactiveCouchbaseTemplate template, String id) {
|
||||
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);
|
||||
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));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static public class N1qlJoinProxy implements InvocationHandler {
|
||||
private final CouchbaseTemplate template;
|
||||
private final ReactiveCouchbaseTemplate reactiveTemplate;
|
||||
private final String collectionName = null;
|
||||
private final N1qlJoinResolverParameters params;
|
||||
private List<?> resolved = null;
|
||||
|
||||
public N1qlJoinProxy(CouchbaseTemplate template, N1qlJoinResolverParameters params) {
|
||||
this.template = template;
|
||||
public N1qlJoinProxy(ReactiveCouchbaseTemplate template, N1qlJoinResolverParameters params) {
|
||||
this.reactiveTemplate = template;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if (this.resolved == null) {
|
||||
this.resolved = doResolve(this.template, this.params, this.params.associatedEntityTypeInfo.getType());
|
||||
this.resolved = doResolve(this.reactiveTemplate, collectionName, this.params,
|
||||
this.params.associatedEntityTypeInfo.getType());
|
||||
}
|
||||
return method.invoke(this.resolved, args);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors
|
||||
* Copyright 2012-2021 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.
|
||||
@@ -15,10 +15,12 @@
|
||||
*/
|
||||
package org.springframework.data.couchbase.core.query;
|
||||
|
||||
import org.springframework.data.couchbase.core.ReactiveCouchbaseTemplate;
|
||||
|
||||
import com.couchbase.client.java.json.JsonObject;
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
|
||||
public class N1QLQuery {
|
||||
public class N1QLQuery extends Query {
|
||||
private N1QLExpression expression;
|
||||
private QueryOptions options;
|
||||
|
||||
@@ -45,4 +47,9 @@ public class N1QLQuery {
|
||||
return query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toN1qlSelectString(ReactiveCouchbaseTemplate template, String collectionName, Class domainClass,
|
||||
Class returnClass, boolean isCount, String[] distinctFields, String[] fields) {
|
||||
return expression.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,7 +349,8 @@ public class Query {
|
||||
return statement.toString();
|
||||
}
|
||||
|
||||
StringBasedN1qlQueryParser.N1qlSpelValues getN1qlSpelValues(ReactiveCouchbaseTemplate template, String collectionName,
|
||||
public static StringBasedN1qlQueryParser.N1qlSpelValues getN1qlSpelValues(
|
||||
ReactiveCouchbaseTemplate template, String collectionName,
|
||||
Class domainClass, Class returnClass, boolean isCount, String[] distinctFields, String[] fields) {
|
||||
String typeKey = template.getConverter().getTypeKey();
|
||||
final CouchbasePersistentEntity<?> persistentEntity = template.getConverter().getMappingContext()
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2020-2021 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.core.mapping.Document;
|
||||
@@ -7,6 +22,9 @@ public class Address extends ComparableEntity {
|
||||
|
||||
private String street;
|
||||
private String city;
|
||||
// for N1qlJoin
|
||||
private String id;
|
||||
private String parentId;
|
||||
|
||||
public Address() {}
|
||||
|
||||
@@ -26,4 +44,20 @@ public class Address extends ComparableEntity {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public String getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(String parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors
|
||||
* Copyright 2020-2021 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.
|
||||
@@ -17,12 +17,14 @@
|
||||
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.index.CompositeQueryIndex;
|
||||
import org.springframework.data.couchbase.core.mapping.Document;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import org.springframework.data.couchbase.core.query.FetchType;
|
||||
import org.springframework.data.couchbase.core.query.N1qlJoin;
|
||||
|
||||
/**
|
||||
* UserSubmission entity for tests
|
||||
@@ -40,6 +42,8 @@ public class UserSubmission extends ComparableEntity {
|
||||
private String password;
|
||||
private List<String> roles;
|
||||
private Address address;
|
||||
@N1qlJoin(on = "meta(lks).id=rks.parentId", fetchType = FetchType.IMMEDIATE) List<Address> otherAddresses;
|
||||
|
||||
private int credits;
|
||||
private List<Submission> submissions;
|
||||
private List<Course> courses;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2020 the original author or authors
|
||||
* Copyright 2020-2021 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,14 @@
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* UserSubmission Repository for tests
|
||||
*
|
||||
@@ -27,4 +32,7 @@ import org.springframework.stereotype.Repository;
|
||||
@Repository
|
||||
public interface UserSubmissionRepository extends PagingAndSortingRepository<UserSubmission, String> {
|
||||
|
||||
@ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS)
|
||||
List<UserSubmission> findByUsername(String username);
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
@@ -64,6 +65,8 @@ import org.springframework.data.couchbase.domain.PersonRepository;
|
||||
import org.springframework.data.couchbase.domain.User;
|
||||
import org.springframework.data.couchbase.domain.UserAnnotated;
|
||||
import org.springframework.data.couchbase.domain.UserRepository;
|
||||
import org.springframework.data.couchbase.domain.UserSubmission;
|
||||
import org.springframework.data.couchbase.domain.UserSubmissionRepository;
|
||||
import org.springframework.data.couchbase.domain.time.AuditingDateTimeProvider;
|
||||
import org.springframework.data.couchbase.repository.auditing.EnableCouchbaseAuditing;
|
||||
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
|
||||
@@ -83,13 +86,14 @@ import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import com.couchbase.client.core.error.AmbiguousTimeoutException;
|
||||
import com.couchbase.client.core.error.CouchbaseException;
|
||||
import com.couchbase.client.core.error.IndexExistsException;
|
||||
import com.couchbase.client.core.error.IndexFailureException;
|
||||
import com.couchbase.client.java.env.ClusterEnvironment;
|
||||
import com.couchbase.client.java.json.JsonArray;
|
||||
import com.couchbase.client.java.kv.GetResult;
|
||||
import com.couchbase.client.java.kv.MutationState;
|
||||
import com.couchbase.client.java.kv.UpsertOptions;
|
||||
import com.couchbase.client.java.manager.query.CreatePrimaryQueryIndexOptions;
|
||||
import com.couchbase.client.java.manager.query.CreateQueryIndexOptions;
|
||||
import com.couchbase.client.java.query.QueryOptions;
|
||||
import com.couchbase.client.java.query.QueryScanConsistency;
|
||||
|
||||
@@ -110,6 +114,8 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
|
||||
|
||||
@Autowired UserRepository userRepository;
|
||||
|
||||
@Autowired UserSubmissionRepository userSubmissionRepository;
|
||||
|
||||
@Autowired CouchbaseTemplate couchbaseTemplate;
|
||||
|
||||
String scopeName = "_default";
|
||||
@@ -117,11 +123,14 @@ public class CouchbaseRepositoryQueryIntegrationTests extends ClusterAwareIntegr
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEach() {
|
||||
try {
|
||||
clientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName());
|
||||
} catch (IndexExistsException ex) {
|
||||
// ignore, all good.
|
||||
}
|
||||
clientFactory.getCluster().queryIndexes().createPrimaryIndex(bucketName(),
|
||||
CreatePrimaryQueryIndexOptions.createPrimaryQueryIndexOptions().ignoreIfExists(true));
|
||||
// this is for the N1qlJoin test
|
||||
List<String> fieldList = new ArrayList<>();
|
||||
fieldList.add("parentId");
|
||||
clientFactory.getCluster().queryIndexes().createIndex(bucketName(), "parent_idx", fieldList,
|
||||
CreateQueryIndexOptions.createQueryIndexOptions().ignoreIfExists(true));
|
||||
// .with("_class", "org.springframework.data.couchbase.domain.Address"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -690,6 +699,53 @@ 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");
|
||||
user = couchbaseTemplate.insertById(UserSubmission.class).one(user);
|
||||
|
||||
Address address1 = new Address();
|
||||
address1.setId(UUID.randomUUID().toString());
|
||||
address1.setStreet("3250 Olcott Street");
|
||||
address1.setParentId(user.getId());
|
||||
Address address2 = new Address();
|
||||
address2.setId(UUID.randomUUID().toString());
|
||||
address2.setStreet("148 Castro Street");
|
||||
address2.setParentId(user.getId());
|
||||
Address address3 = new Address();
|
||||
address3.setId(UUID.randomUUID().toString());
|
||||
address3.setStreet("123 Sesame Street");
|
||||
address3.setParentId(UUID.randomUUID().toString()); // does not belong to user
|
||||
address1 = couchbaseTemplate.insertById(Address.class).one(address1);
|
||||
address2 = couchbaseTemplate.insertById(Address.class).one(address2);
|
||||
address3 = couchbaseTemplate.insertById(Address.class).one(address3);
|
||||
|
||||
List<UserSubmission> users = userSubmissionRepository.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);
|
||||
}
|
||||
}
|
||||
|
||||
UserSubmission foundUser = userSubmissionRepository.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);
|
||||
}
|
||||
}
|
||||
|
||||
couchbaseTemplate.removeById(Address.class)
|
||||
.all(Arrays.asList(address1.getId(), address2.getId(), address3.getId(), user.getId()));
|
||||
}
|
||||
|
||||
private void sleep(int millis) {
|
||||
try {
|
||||
Thread.sleep(millis); // so they are executed out-of-order
|
||||
|
||||
Reference in New Issue
Block a user