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..83df95ad5 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("analytics 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("analytics 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/domain/Iata.java b/src/test/java/org/springframework/data/couchbase/domain/Iata.java index 5e603005c..c3b100bd2 100644 --- a/src/test/java/org/springframework/data/couchbase/domain/Iata.java +++ b/src/test/java/org/springframework/data/couchbase/domain/Iata.java @@ -1,6 +1,6 @@ package org.springframework.data.couchbase.domain; public enum Iata { - vie, + vie, // must be lower-case to match "vie" as airport.iata is always specified in lowercase 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 f0d17add4..fefec70eb 100644 --- a/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java +++ b/src/test/java/org/springframework/data/couchbase/repository/CouchbaseRepositoryQueryIntegrationTests.java @@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -34,7 +35,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 +66,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 @@ -175,11 +177,13 @@ void findByEnum() { vie = new Airport("airports::vie", "vie", "loww"); vie = airportRepository.save(vie); Airport airport2 = airportRepository.findByIata(Iata.vie); + assertNotNull(airport2, "should have found "+vie); assertEquals(airport2.getId(), vie.getId()); } finally { airportRepository.delete(vie); } } + @Test public void testCas() { User user = new User("1", "Dave", "Wilson"); @@ -271,6 +275,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 +349,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)); }