Support N1QL Join Annotations. (#1211)

* Support N1QL Join Annotations.

Closes #1198.
This commit is contained in:
Michael Reiche
2021-09-12 18:38:01 -07:00
committed by GitHub
parent 4bd82ea23c
commit da3e8e0e36
11 changed files with 200 additions and 42 deletions

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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