From 3787fcc1723331df686991f33be781b7b39e792a Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Tue, 23 Mar 2021 02:25:38 +0800 Subject: [PATCH 1/7] Fix indent (#1094) --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 864533fff..dd04c0ddf 100644 --- a/pom.xml +++ b/pom.xml @@ -216,11 +216,11 @@ sonatype-snapshot https://oss.sonatype.org/content/repositories/snapshots - true - + true + - false - + false + jitpack.io From 24116aee35938f61477c17d2e79e64404429fea7 Mon Sep 17 00:00:00 2001 From: Michael Reiche <48999328+mikereiche@users.noreply.github.com> Date: Wed, 24 Mar 2021 07:00:41 -0700 Subject: [PATCH 2/7] Update versions to correct values. (#1106) Closes #1105. Co-authored-by: mikereiche --- README.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.adoc b/README.adoc index 52e76a285..9774fbccc 100644 --- a/README.adoc +++ b/README.adoc @@ -109,7 +109,7 @@ Add the Maven dependency: org.springframework.data spring-data-couchbase - ${version}.RELEASE + ${version} ---- @@ -120,7 +120,7 @@ If you'd rather like the latest snapshots of the upcoming major version, use our org.springframework.data spring-data-couchbase - ${version}.BUILD-SNAPSHOT + ${version}-SNAPSHOT From 214b25075adb3a74e149a383e118285fd86a251b Mon Sep 17 00:00:00 2001 From: Michael Reiche <48999328+mikereiche@users.noreply.github.com> Date: Wed, 24 Mar 2021 07:57:11 -0700 Subject: [PATCH 3/7] Use queryScanConsistency on reactive deleteAll(). (#1108) It was present on non-Reactive, but missing from reactive. Closes #1096. Original pull request #1108. Co-authored-by: mikereiche --- .../SimpleReactiveCouchbaseRepository.java | 6 +++--- .../domain/ReactiveAirportRepository.java | 4 ++++ ...chbaseRepositoryQueryIntegrationTests.java | 19 +++++++++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleReactiveCouchbaseRepository.java b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleReactiveCouchbaseRepository.java index d6c69ef71..edb8ab0f4 100644 --- a/src/main/java/org/springframework/data/couchbase/repository/support/SimpleReactiveCouchbaseRepository.java +++ b/src/main/java/org/springframework/data/couchbase/repository/support/SimpleReactiveCouchbaseRepository.java @@ -16,7 +16,7 @@ package org.springframework.data.couchbase.repository.support; -import static org.springframework.data.couchbase.repository.support.Util.*; +import static org.springframework.data.couchbase.repository.support.Util.hasNonZeroVersionProperty; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -26,7 +26,6 @@ import java.util.stream.Collectors; import org.reactivestreams.Publisher; - import org.springframework.data.couchbase.core.CouchbaseOperations; import org.springframework.data.couchbase.core.ReactiveCouchbaseOperations; import org.springframework.data.couchbase.core.query.Query; @@ -189,7 +188,8 @@ public Mono count() { @Override public Mono deleteAll() { - return operations.removeByQuery(entityInformation.getJavaType()).all().then(); + return operations.removeByQuery(entityInformation.getJavaType()).withConsistency(buildQueryScanConsistency()).all() + .then(); } /** diff --git a/src/test/java/org/springframework/data/couchbase/domain/ReactiveAirportRepository.java b/src/test/java/org/springframework/data/couchbase/domain/ReactiveAirportRepository.java index 63cb3388d..51c4d48cc 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/ReactiveAirportRepository.java +++ b/src/test/java/org/springframework/data/couchbase/domain/ReactiveAirportRepository.java @@ -45,6 +45,10 @@ public interface ReactiveAirportRepository extends ReactiveSortingRepository findAll(); + @Override + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + Mono deleteAll(); + @Override Mono save(Airport a); diff --git a/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java index d6b582f7a..1c2d0d9bd 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/ReactiveCouchbaseRepositoryQueryIntegrationTests.java @@ -195,6 +195,25 @@ void deleteAllById() { } } + @Test + void deleteAll() { + + Airport vienna = new Airport("airports::vie", "vie", "LOWW"); + Airport frankfurt = new Airport("airports::fra", "fra", "EDDF"); + Airport losAngeles = new Airport("airports::lax", "lax", "KLAX"); + + try { + airportRepository.saveAll(asList(vienna, frankfurt, losAngeles)).as(StepVerifier::create) + .expectNext(vienna, frankfurt, losAngeles).verifyComplete(); + + airportRepository.deleteAll().as(StepVerifier::create).verifyComplete(); + + airportRepository.findAll().as(StepVerifier::create).verifyComplete(); + } finally { + airportRepository.deleteAll().block(); + } + } + @Configuration @EnableReactiveCouchbaseRepositories("org.springframework.data.couchbase") static class Config extends AbstractCouchbaseConfiguration { From 521006f0886cbc4e80b49657648c4e5f3cba36ef Mon Sep 17 00:00:00 2001 From: Michael Reiche <48999328+mikereiche@users.noreply.github.com> Date: Wed, 24 Mar 2021 12:10:28 -0700 Subject: [PATCH 4/7] Add QueryCriteria arrayContaining. (#1110) Add QueryCriteria arrayContaining which maps to n1ql array_containing. Closes #1073. Original pull request #1109. Co-authored-by: mikereiche --- .../data/couchbase/core/query/QueryCriteria.java | 7 +++++++ .../data/couchbase/core/query/QueryCriteriaTests.java | 11 +++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/springframework/data/couchbase/core/query/QueryCriteria.java b/src/main/java/org/springframework/data/couchbase/core/query/QueryCriteria.java index 7f1305812..4b7da2459 100644 --- a/src/main/java/org/springframework/data/couchbase/core/query/QueryCriteria.java +++ b/src/main/java/org/springframework/data/couchbase/core/query/QueryCriteria.java @@ -192,6 +192,13 @@ public QueryCriteria containing(@Nullable Object o) { return this; } + public QueryCriteria arrayContaining(@Nullable Object o) { + operator = "ARRAY_CONTAINING"; + value = new Object[] { o }; + format = "array_containing(%1$s, %3$s)"; + return this; + } + public QueryCriteria notContaining(@Nullable Object o) { value = new QueryCriteria[] { wrap(containing(o)) }; operator = "NOT"; diff --git a/src/test/java/org/springframework/data/couchbase/core/query/QueryCriteriaTests.java b/src/test/java/org/springframework/data/couchbase/core/query/QueryCriteriaTests.java index ca1e06afd..cc563e015 100644 --- a/src/test/java/org/springframework/data/couchbase/core/query/QueryCriteriaTests.java +++ b/src/test/java/org/springframework/data/couchbase/core/query/QueryCriteriaTests.java @@ -123,14 +123,11 @@ void testStartingWith() { assertEquals("`name` like (\"Cou\"||\"%\")", c.export()); } - /* cannot do this properly yet because in arg to when() in - * startingWith() cannot be a QueryCriteria @Test void testStartingWithExpr() { QueryCriteria c = where(i("name")).startingWith(where(i("name")).plus("xxx")); - assertEquals("`name` like (((`name` || "xxx") || ""%""))", c.export()); + assertEquals("`name` like (((`name` || \"xxx\"))||\"%\")", c.export()); } - */ @Test void testEndingWith() { @@ -162,6 +159,12 @@ void testNotContaining() { assertEquals("not( (contains(`name`, \"Elvis\")) )", c.export()); } + @Test + void testArrayContaining() { + QueryCriteria c = where(i("name")).arrayContaining("Elvis"); + assertEquals("array_containing(`name`, \"Elvis\")", c.export()); + } + @Test void testLike() { QueryCriteria c = where(i("name")).like("%ouch%"); From fcfe778c4a73674ab10950a2fcfcd68ac4dfb774 Mon Sep 17 00:00:00 2001 From: Michael Reiche <48999328+mikereiche@users.noreply.github.com> Date: Wed, 24 Mar 2021 14:25:28 -0700 Subject: [PATCH 5/7] Support enum arguments on repository queries. (#1112) Support enum in AbstractCouchbaseConverter.convertForWriteIfNeeded() and also call that from MappingCouchbaseConverter.getPotentiallyConvertedSimpleWrite() Closes #1069. Original pull request #1112. Co-authored-by: mikereiche --- .../core/convert/AbstractCouchbaseConverter.java | 4 +++- .../core/convert/MappingCouchbaseConverter.java | 11 ++--------- .../data/couchbase/domain/AirportRepository.java | 3 +++ .../springframework/data/couchbase/domain/Iata.java | 6 ++++++ .../CouchbaseRepositoryQueryIntegrationTests.java | 13 +++++++++++++ 5 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 src/test/java/org/springframework/data/couchbase/domain/Iata.java diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/AbstractCouchbaseConverter.java b/src/main/java/org/springframework/data/couchbase/core/convert/AbstractCouchbaseConverter.java index cc55d0503..d6cafffaa 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/AbstractCouchbaseConverter.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/AbstractCouchbaseConverter.java @@ -17,6 +17,7 @@ package org.springframework.data.couchbase.core.convert; import java.util.Collections; +import java.util.Optional; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.convert.ConversionService; @@ -100,7 +101,8 @@ public Object convertForWriteIfNeeded(Object value) { return this.conversions.getCustomWriteTarget(value.getClass()) // .map(it -> (Object) this.conversionService.convert(value, it)) // - .orElse(value); + .orElseGet(() -> Enum.class.isAssignableFrom(value.getClass()) ? ((Enum) value).name() : value); + } @Override diff --git a/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java b/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java index e84055707..7793be558 100644 --- a/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java +++ b/src/main/java/org/springframework/data/couchbase/core/convert/MappingCouchbaseConverter.java @@ -774,15 +774,8 @@ private void writeSimpleInternal(final Object source, final CouchbaseDocument ta target.put(key, getPotentiallyConvertedSimpleWrite(source)); } - private Object getPotentiallyConvertedSimpleWrite(final Object value) { - if (value == null) { - return null; - } - - Optional> customTarget = conversions.getCustomWriteTarget(value.getClass()); - - return customTarget.map(it -> (Object) conversionService.convert(value, it)) - .orElseGet(() -> Enum.class.isAssignableFrom(value.getClass()) ? ((Enum) value).name() : value); + public Object getPotentiallyConvertedSimpleWrite(final Object value) { + return convertForWriteIfNeeded(value); } /** diff --git a/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java b/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java index 911aebd70..1b04222f9 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java +++ b/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java @@ -50,6 +50,9 @@ public interface AirportRepository extends PagingAndSortingRepository getAllByIata(String iata); diff --git a/src/test/java/org/springframework/data/couchbase/domain/Iata.java b/src/test/java/org/springframework/data/couchbase/domain/Iata.java new file mode 100644 index 000000000..5e603005c --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/domain/Iata.java @@ -0,0 +1,6 @@ +package org.springframework.data.couchbase.domain; + +public enum Iata { + vie, + xxx +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java index db9f53fc9..f0d17add4 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java @@ -47,6 +47,7 @@ import org.springframework.data.couchbase.domain.Address; import org.springframework.data.couchbase.domain.Airport; import org.springframework.data.couchbase.domain.AirportRepository; +import org.springframework.data.couchbase.domain.Iata; import org.springframework.data.couchbase.domain.Person; import org.springframework.data.couchbase.domain.PersonRepository; import org.springframework.data.couchbase.domain.User; @@ -167,6 +168,18 @@ void findBySimpleProperty() { } } + @Test + void findByEnum() { + Airport vie = null; + try { + vie = new Airport("airports::vie", "vie", "loww"); + vie = airportRepository.save(vie); + Airport airport2 = airportRepository.findByIata(Iata.vie); + assertEquals(airport2.getId(), vie.getId()); + } finally { + airportRepository.delete(vie); + } + } @Test public void testCas() { User user = new User("1", "Dave", "Wilson"); From 4a900b8a66032b969cb7b8c4022c20b3b431b026 Mon Sep 17 00:00:00 2001 From: Michael Reiche <48999328+mikereiche@users.noreply.github.com> Date: Thu, 25 Mar 2021 08:37:35 -0700 Subject: [PATCH 6/7] Support enum parameters to repository queries. (#1111) * Add QueryCriteria arrayContaining. Add QueryCriteria arrayContaining which maps to n1ql array_containing. Closes #1073. Original pull request #1109. * Support enum parameters to repository queries. Support enums in AbstractCouchbaseConverter.convertForWriteIfNeeded(). Closes #1069. Original pull request #1110. Co-authored-by: mikereiche From 7854edd88a6e35b5e1daf17c3e44388306da8dc1 Mon Sep 17 00:00:00 2001 From: mikereiche Date: Mon, 29 Mar 2021 17:45:43 -0400 Subject: [PATCH 7/7] Specific message for string queries that do not project __id and __cas. com.couchbase.client.core.error.CouchbaseException: query did not project __id. Either use #{#n1ql.selectEntity} or project __id and __cas : SELECT __cas, * from `b406ab45-ef95-441f-a318-76b8e8af2d76` where iata = $1 Closes #1097. Original pull request: #1114. --- ...activeFindByAnalyticsOperationSupport.java | 22 +++++++++++++++---- .../ReactiveFindByQueryOperationSupport.java | 11 ++++++++++ .../couchbase/domain/AirportRepository.java | 8 +++++++ ...chbaseRepositoryQueryIntegrationTests.java | 22 ++++++++++++++++--- 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByAnalyticsOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByAnalyticsOperationSupport.java index d3205df94..a5ca3de00 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByAnalyticsOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByAnalyticsOperationSupport.java @@ -19,7 +19,9 @@ import reactor.core.publisher.Mono; import org.springframework.data.couchbase.core.query.AnalyticsQuery; +import org.springframework.data.couchbase.core.support.TemplateUtils; +import com.couchbase.client.core.error.CouchbaseException; import com.couchbase.client.java.analytics.AnalyticsOptions; import com.couchbase.client.java.analytics.AnalyticsScanConsistency; import com.couchbase.client.java.analytics.ReactiveAnalyticsResult; @@ -92,10 +94,22 @@ public Flux all() { return throwable; } }).flatMapMany(ReactiveAnalyticsResult::rowsAsObject).map(row -> { - String id = row.getString("__id"); - long cas = row.getLong("__cas"); - row.removeKey("__id"); - row.removeKey("__cas"); + String id = ""; + long cas = 0; + if (row.getString(TemplateUtils.SELECT_ID) == null) { + throw new CouchbaseException("query did not project " + TemplateUtils.SELECT_ID + + ". Either use #{#n1ql.selectEntity} or project " + TemplateUtils.SELECT_ID + " and " + + TemplateUtils.SELECT_CAS + " : " + statement); + } + id = row.getString(TemplateUtils.SELECT_ID); + if (row.getLong(TemplateUtils.SELECT_CAS) == null) { + throw new CouchbaseException("query did not project " + TemplateUtils.SELECT_CAS + + ". Either use #{#n1ql.selectEntity} or project " + TemplateUtils.SELECT_ID + " and " + + TemplateUtils.SELECT_CAS + " : " + statement); + } + cas = row.getLong(TemplateUtils.SELECT_CAS); + row.removeKey(TemplateUtils.SELECT_ID); + row.removeKey(TemplateUtils.SELECT_CAS); return template.support().decodeEntity(id, row.toString(), cas, domainType); }); }); diff --git a/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByQueryOperationSupport.java b/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByQueryOperationSupport.java index d9cd78af3..eea12845d 100644 --- a/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByQueryOperationSupport.java +++ b/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByQueryOperationSupport.java @@ -22,6 +22,7 @@ import org.springframework.data.couchbase.core.support.TemplateUtils; import org.springframework.util.Assert; +import com.couchbase.client.core.error.CouchbaseException; import com.couchbase.client.java.query.QueryScanConsistency; import com.couchbase.client.java.query.ReactiveQueryResult; @@ -147,7 +148,17 @@ public Flux all() { String id = ""; long cas = 0; if (distinctFields == null) { + if (row.getString(TemplateUtils.SELECT_ID) == null) { + throw new CouchbaseException( + "query did not project " + TemplateUtils.SELECT_ID + ". Either use #{#n1ql.selectEntity} or project " + + TemplateUtils.SELECT_ID + " and " + TemplateUtils.SELECT_CAS + " : " + statement); + } id = row.getString(TemplateUtils.SELECT_ID); + if (row.getLong(TemplateUtils.SELECT_CAS) == null) { + throw new CouchbaseException( + "query did not project " + TemplateUtils.SELECT_CAS + ". Either use #{#n1ql.selectEntity} or project " + + TemplateUtils.SELECT_ID + " and " + TemplateUtils.SELECT_CAS + " : " + statement); + } cas = row.getLong(TemplateUtils.SELECT_CAS); row.removeKey(TemplateUtils.SELECT_ID); row.removeKey(TemplateUtils.SELECT_CAS); diff --git a/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java b/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java index 1b04222f9..06464583d 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java +++ b/src/test/java/org/springframework/data/couchbase/domain/AirportRepository.java @@ -57,6 +57,14 @@ public interface AirportRepository extends PagingAndSortingRepository getAllByIata(String iata); + @Query("SELECT __cas, * from `#{#n1ql.bucket}` where iata = $1") + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + List getAllByIataNoID(String iata); + + @Query("SELECT __id, * from `#{#n1ql.bucket}` where iata = $1") + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) + List getAllByIataNoCAS(String iata); + @ScanConsistency(query = QueryScanConsistency.REQUEST_PLUS) long countByIataIn(String... iata); diff --git a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java index f0d17add4..6525f6df8 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java @@ -34,7 +34,6 @@ import java.util.concurrent.Future; import java.util.stream.Collectors; -import com.couchbase.client.java.query.QueryScanConsistency; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -66,7 +65,9 @@ import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import com.couchbase.client.core.error.CouchbaseException; import com.couchbase.client.core.error.IndexExistsException; +import com.couchbase.client.java.query.QueryScanConsistency; /** * Repository tests @@ -180,6 +181,7 @@ void findByEnum() { airportRepository.delete(vie); } } + @Test public void testCas() { User user = new User("1", "Dave", "Wilson"); @@ -271,6 +273,19 @@ void threadSafeParametersTest() throws Exception { } } + @Test + void stringQueryTest() throws Exception { + Airport airport = new Airport("airports::vie", "vie", "lowx"); + try { + airportRepository.save(airport); + airportRepository.getAllByIata("vie").get(0); // gets at least one with no exception + assertThrows(CouchbaseException.class, () -> airportRepository.getAllByIataNoID("vie")); + assertThrows(CouchbaseException.class, () -> airportRepository.getAllByIataNoCAS("vie")); + } finally { + airportRepository.deleteById(airport.getId()); + } + } + @Test void threadSafeStringParametersTest() throws Exception { String[] iatas = { "JFK", "IAD", "SFO", "SJC", "SEA", "LAX", "PHX" }; @@ -332,14 +347,15 @@ void deleteAllById() { void couchbaseRepositoryQuery() throws Exception { User user = new User("1", "Dave", "Wilson"); userRepository.save(user); - couchbaseTemplate.findByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS).matching(QueryCriteria.where("firstname").is("Dave").and("`1`").is("`1`")).all(); + couchbaseTemplate.findByQuery(User.class).withConsistency(QueryScanConsistency.REQUEST_PLUS) + .matching(QueryCriteria.where("firstname").is("Dave").and("`1`").is("`1`")).all(); String input = "findByFirstname"; Method method = UserRepository.class.getMethod(input, String.class); CouchbaseQueryMethod queryMethod = new CouchbaseQueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class), new SpelAwareProxyProjectionFactory(), couchbaseTemplate.getConverter().getMappingContext()); CouchbaseRepositoryQuery query = new CouchbaseRepositoryQuery(couchbaseTemplate, queryMethod, null); - List users = (List)query.execute(new String[] { "Dave" }); + List users = (List) query.execute(new String[] { "Dave" }); assertEquals(user, users.get(0)); }