Fixed issue with querying documents annotated with @Collection with QuerydslPredicateExecutor. (#1867)

Closes #1866
This commit is contained in:
Tigran Babloyan
2023-11-18 00:51:25 +04:00
committed by mikereiche
parent 328439b2c1
commit 4ce7f1bf2a
5 changed files with 724 additions and 4 deletions

View File

@@ -26,6 +26,7 @@ import java.util.stream.Stream;
import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.couchbase.core.ExecutableFindByQueryOperation;
import org.springframework.data.couchbase.core.query.OptionsBuilder;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
@@ -42,9 +43,11 @@ import com.querydsl.core.QueryResults;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Predicate;
import org.springframework.util.StringUtils;
/**
* @author Michael Reiche
* @author Tigran Babloyan
*/
public class SpringDataCouchbaseQuery<T> extends SpringDataCouchbaseQuerySupport<SpringDataCouchbaseQuery<T>>
implements Fetchable<T> {
@@ -61,7 +64,7 @@ public class SpringDataCouchbaseQuery<T> extends SpringDataCouchbaseQuerySupport
* @param type must not be {@literal null}.
*/
public SpringDataCouchbaseQuery(CouchbaseOperations operations, Class<? extends T> type) {
this(operations, type, DEFAULT_COLLECTION);
this(operations, type, OptionsBuilder.getCollectionFrom(type));
}
/**
@@ -92,7 +95,7 @@ public class SpringDataCouchbaseQuery<T> extends SpringDataCouchbaseQuerySupport
this.couchbaseOperations = operations;
this.queryCustomizer = queryCustomizer;
this.find = (ExecutableFindByQueryOperation.ExecutableFindByQuery<T>) couchbaseOperations.findByQuery(domainType)
.as(resultType1).inCollection(collectionName);
.as(resultType1).inCollection(StringUtils.hasText(collectionName) ? collectionName : DEFAULT_COLLECTION);
}
/*

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2012-2023 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.annotation.Id;
import org.springframework.data.annotation.PersistenceConstructor;
import org.springframework.data.couchbase.core.index.CompositeQueryIndex;
import org.springframework.data.couchbase.core.index.QueryIndexed;
import org.springframework.data.couchbase.core.mapping.Document;
import org.springframework.data.couchbase.repository.Collection;
import org.springframework.data.couchbase.repository.Scope;
import org.springframework.data.couchbase.util.CollectionAwareDefaultScopeIntegrationTests;
@Document
@Collection(CollectionAwareDefaultScopeIntegrationTests.otherCollection)
@Scope(CollectionAwareDefaultScopeIntegrationTests.otherScope)
public class AirlineCollectioned extends ComparableEntity {
@Id String id;
@QueryIndexed String name;
String hqCountry;
@PersistenceConstructor
public AirlineCollectioned(String id, String name, String hqCountry) {
this.id = id;
this.name = name;
this.hqCountry = hqCountry;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getHqCountry() {
return hqCountry;
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2017-2023 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.CouchbaseRepository;
import org.springframework.data.couchbase.repository.DynamicProxyable;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.stereotype.Repository;
/**
* @author Michael Reiche
*/
@Repository
public interface AirlineCollectionedRepository extends CouchbaseRepository<AirlineCollectioned, String>,
QuerydslPredicateExecutor<AirlineCollectioned>, DynamicProxyable<AirlineCollectionedRepository> {
}

View File

@@ -0,0 +1,631 @@
/*
* Copyright 2017-2023 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.repository.query;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.BooleanExpression;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.couchbase.core.CouchbaseTemplate;
import org.springframework.data.couchbase.core.mapping.event.ValidatingCouchbaseEventListener;
import org.springframework.data.couchbase.core.query.QueryCriteriaDefinition;
import org.springframework.data.couchbase.domain.*;
import org.springframework.data.couchbase.repository.auditing.EnableCouchbaseAuditing;
import org.springframework.data.couchbase.repository.auditing.EnableReactiveCouchbaseAuditing;
import org.springframework.data.couchbase.repository.config.EnableCouchbaseRepositories;
import org.springframework.data.couchbase.repository.support.BasicQuery;
import org.springframework.data.couchbase.repository.support.SpringDataCouchbaseSerializer;
import org.springframework.data.couchbase.util.*;
import org.springframework.data.domain.Sort;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.StreamSupport;
import static com.couchbase.client.java.query.QueryScanConsistency.REQUEST_PLUS;
import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.data.couchbase.util.Util.comprises;
import static org.springframework.data.couchbase.util.Util.exactly;
/**
* Repository tests
*
* @author Tigran Babloyan
*/
@SpringJUnitConfig(CouchbaseRepositoryCollectionQuerydslIntegrationTests.Config.class)
@DirtiesContext
@IgnoreWhen(missesCapabilities = Capabilities.QUERY, clusterTypes = ClusterType.MOCKED)
public class CouchbaseRepositoryCollectionQuerydslIntegrationTests extends CollectionAwareDefaultScopeIntegrationTests {
@Autowired
AirlineCollectionedRepository airlineRepository;
static QAirlineCollectioned airline = QAirlineCollectioned.airlineCollectioned;
// saved
static AirlineCollectioned united = new AirlineCollectioned("1", "United Airlines", "US");
static AirlineCollectioned lufthansa = new AirlineCollectioned("2", "Lufthansa", "DE");
static AirlineCollectioned emptyStringAirline = new AirlineCollectioned("3", "Empty String", "");
static AirlineCollectioned nullStringAirline = new AirlineCollectioned("4", "Null String", null);
static AirlineCollectioned unitedLowercase = new AirlineCollectioned("5", "united airlines", "US");
static AirlineCollectioned[] saved = new AirlineCollectioned[] { united, lufthansa, emptyStringAirline, nullStringAirline, unitedLowercase };
// not saved
static AirlineCollectioned flyByNight = new AirlineCollectioned("1001", "Fly By Night", "UK");
static AirlineCollectioned sleepByDay = new AirlineCollectioned("1002", "Sleep By Day", "CA");
static AirlineCollectioned[] notSaved = new AirlineCollectioned[] { flyByNight, sleepByDay };
@Autowired CouchbaseTemplate couchbaseTemplate;
SpringDataCouchbaseSerializer serializer = null;
@BeforeEach
public void beforeEach() {
super.beforeEach();
serializer = new SpringDataCouchbaseSerializer(couchbaseTemplate.getConverter());
}
@BeforeAll
static public void beforeAll() {
callSuperBeforeAll(new Object() {});
ApplicationContext ac = new AnnotationConfigApplicationContext(
CouchbaseRepositoryCollectionQuerydslIntegrationTests.Config.class);
CouchbaseTemplate template = (CouchbaseTemplate) ac.getBean("couchbaseTemplate");
for (AirlineCollectioned airline : saved) {
template.insertById(AirlineCollectioned.class).one(airline);
}
template.findByQuery(Airline.class).withConsistency(REQUEST_PLUS).all();
logDisconnect(template.getCouchbaseClientFactory().getCluster(), "queryDsl-before");
}
@AfterAll
static public void afterAll() {
ApplicationContext ac = new AnnotationConfigApplicationContext(
CouchbaseRepositoryCollectionQuerydslIntegrationTests.Config.class);
CouchbaseTemplate template = (CouchbaseTemplate) ac.getBean("couchbaseTemplate");
for (AirlineCollectioned airline : saved) {
template.removeById(AirlineCollectioned.class).one(airline.getId());
}
template.findByQuery(Airline.class).withConsistency(REQUEST_PLUS).all();
logDisconnect(template.getCouchbaseClientFactory().getCluster(), "queryDsl-after");
callSuperAfterAll(new Object() {});
}
@Test
void testEq() {
{
BooleanExpression predicate = airline.name.eq(flyByNight.getName());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(
comprises(result,
Arrays.stream(saved).filter(a -> a.getName().equals(flyByNight.getName())).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE name = $1", bq(predicate));
}
{
BooleanExpression predicate = airline.name.eq(united.getName());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(
comprises(result,
Arrays.stream(saved).filter(a -> a.getName().equals(united.getName())).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE name = $1", bq(predicate));
}
}
// this gives hqCountry == "" and hqCountry is missing
// @Test
void testStringIsEmpty() {
{
BooleanExpression predicate = airline.hqCountry.isEmpty();
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result, emptyStringAirline, nullStringAirline), "[unexpected] -> [missing]");
assertEquals(" WHERE UPPER(name) like $1", bq(predicate));
}
}
@Test
void testNot() {
{
BooleanExpression predicate = airline.name.eq(united.getName()).and(airline.hqCountry.eq(united.getHqCountry()))
.not();
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved)
.filter(a -> !(a.getName().equals(united.getName()) && a.getHqCountry().equals(united.getHqCountry())))
.toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE not( ( (hqCountry = $1) and (name = $2)) )", bq(predicate));
}
{
BooleanExpression predicate = airline.name.in(Arrays.asList(united.getName())).not();
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(
comprises(result,
Arrays.stream(saved).filter(a -> !(a.getName().equals(united.getName()))).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE not( (name = $1) )", bq(predicate));
}
{
BooleanExpression predicate = airline.name.eq(united.getName()).not();
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(
comprises(result,
Arrays.stream(saved).filter(a -> !(a.getName().equals(united.getName()))).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE not( (name = $1) )", bq(predicate));
}
}
@Test
void testAnd() {
{
BooleanExpression predicate = airline.name.eq(united.getName()).and(airline.hqCountry.eq(united.getHqCountry()));
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved)
.filter(a -> a.getName().equals(united.getName()) && a.getHqCountry().equals(united.getHqCountry()))
.toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE (name = $1) and (hqCountry = $2)", bq(predicate));
}
{
BooleanExpression predicate = airline.name.eq(united.getName())
.and(airline.hqCountry.eq(lufthansa.getHqCountry()));
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved)
.filter(a -> a.getName().equals(united.getName()) && a.getHqCountry().equals(lufthansa.getHqCountry()))
.toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE (name = $1) and (hqCountry = $2)", bq(predicate));
}
}
@Test
void testOr() {
{
BooleanExpression predicate = airline.name.eq(united.getName())
.or(airline.hqCountry.eq(lufthansa.getHqCountry()));
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved)
.filter(a -> a.getName().equals(united.getName()) || a.getName().equals(lufthansa.getName()))
.toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE (name = $1) or (hqCountry = $2)", bq(predicate));
}
}
@Test
void testNe() {
{
BooleanExpression predicate = airline.name.ne(united.getName());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(
comprises(result,
Arrays.stream(saved).filter(a -> !a.getName().equals(united.getName())).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE name != $1", bq(predicate));
}
}
@Test
void testStartsWith() {
{
BooleanExpression predicate = airline.name.startsWith(united.getName().substring(0, 5));
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result, Arrays.stream(saved)
.filter(a -> a.getName().startsWith(united.getName().substring(0, 5))).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE name like ($1||\"%\")", bq(predicate));
}
}
@Test
void testStartsWithIgnoreCase() {
{
BooleanExpression predicate = airline.name.startsWithIgnoreCase(united.getName().substring(0, 5));
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved)
.filter(a -> a.getName().toUpperCase().startsWith(united.getName().toUpperCase().substring(0, 5)))
.toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE lower(name) like ($1||\"%\")", bq(predicate));
}
}
@Test
void testEndsWith() {
{
BooleanExpression predicate = airline.name.endsWith(united.getName().substring(1));
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result, Arrays.stream(saved).filter(a -> a.getName().endsWith(united.getName().substring(1)))
.toArray(AirlineCollectioned[]::new)), "[unexpected] -> [missing]");
assertEquals(" WHERE name like (\"%\"||$1)", bq(predicate));
}
}
@Test
void testEndsWithIgnoreCase() {
{
BooleanExpression predicate = airline.name.endsWithIgnoreCase(united.getName().substring(1));
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved)
.filter(a -> a.getName().toUpperCase().endsWith(united.getName().toUpperCase().substring(1)))
.toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE lower(name) like (\"%\"||$1)", bq(predicate));
}
}
@Test
void testEqIgnoreCase() {
{
BooleanExpression predicate = airline.name.equalsIgnoreCase(flyByNight.getName());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved).filter(a -> a.getName().equalsIgnoreCase(flyByNight.getName())).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE lower(name) = $1", bq(predicate));
}
{
BooleanExpression predicate = airline.name.equalsIgnoreCase(united.getName());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(
comprises(result,
Arrays.stream(saved).filter(a -> a.getName().equalsIgnoreCase(united.getName())).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE lower(name) = $1", bq(predicate));
}
}
@Test
void testContains() {
{
BooleanExpression predicate = airline.name.contains("United");
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(
comprises(result, Arrays.stream(saved).filter(a -> a.getName().contains("United")).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE contains(name, $1)", bq(predicate));
}
}
@Test
void testContainsIgnoreCase() {
{
BooleanExpression predicate = airline.name.containsIgnoreCase("united");
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved)
.filter(a -> a.getName().toUpperCase(Locale.ROOT).contains("united".toUpperCase(Locale.ROOT)))
.toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE contains(lower(name), $1)", bq(predicate));
}
}
@Test
void testLike() {
{
BooleanExpression predicate = airline.name.like("%nited%");
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(
comprises(result, Arrays.stream(saved).filter(a -> a.getName().contains("nited")).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE name like $1", bq(predicate));
}
}
@Test
void testLikeIgnoreCase() {
{
BooleanExpression predicate = airline.name.likeIgnoreCase("%Airlines");
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved)
.filter(a -> a.getName().toUpperCase(Locale.ROOT).endsWith("Airlines".toUpperCase(Locale.ROOT)))
.toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE lower(name) like $1", bq(predicate));
}
}
// This is 'between' is inclusive
@Test
void testBetween() {
{
BooleanExpression predicate = airline.name.between(flyByNight.getName(), united.getName());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved)
.filter(
a -> a.getName().compareTo(flyByNight.getName()) >= 0 && a.getName().compareTo(united.getName()) <= 0)
.toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE name between $1 and $2", bq(predicate));
}
}
@Test
void testIn() {
{
BooleanExpression predicate = airline.name.in(Arrays.asList(united.getName()));
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(
comprises(result,
Arrays.stream(saved).filter(a -> a.getName().equals(united.getName())).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE name = $1", bq(predicate));
}
{
BooleanExpression predicate = airline.name.in(Arrays.asList(united.getName(), lufthansa.getName()));
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved)
.filter(a -> a.getName().equals(united.getName()) || a.getName().equals(lufthansa.getName()))
.toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE name in $1", bq(predicate));
}
{
BooleanExpression predicate = airline.name.in("Fly By Night", "Sleep By Day");
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved)
.filter(a -> a.getName().equals(flyByNight.getName()) || a.getName().equals(sleepByDay.getName()))
.toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE name in $1", bq(predicate));
}
}
@Test
void testSort(){
{
BooleanExpression predicate = airline.name.in(Arrays.stream(saved).map(AirlineCollectioned::getName).toList());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate, Sort.by("name").ascending());
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(AirlineCollectioned[]::new),
Arrays.stream(saved)
.sorted(Comparator.comparing(AirlineCollectioned::getName))
.toArray(AirlineCollectioned[]::new),
"Order of airlines does not match");
}
{
BooleanExpression predicate = airline.name.in(Arrays.stream(saved).map(AirlineCollectioned::getName).toList());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate, Sort.by("name").descending());
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(AirlineCollectioned[]::new),
Arrays.stream(saved)
.sorted(Comparator.comparing(AirlineCollectioned::getName).reversed())
.toArray(AirlineCollectioned[]::new),
"Order of airlines does not match");
}
{
BooleanExpression predicate = airline.name.in(Arrays.stream(saved).map(AirlineCollectioned::getName).toList());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate, airline.name.asc());
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(AirlineCollectioned[]::new),
Arrays.stream(saved)
.sorted(Comparator.comparing(AirlineCollectioned::getName))
.toArray(AirlineCollectioned[]::new),
"Order of airlines does not match");
}
{
BooleanExpression predicate = airline.name.in(Arrays.stream(saved).map(AirlineCollectioned::getName).toList());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate, airline.name.desc());
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(AirlineCollectioned[]::new),
Arrays.stream(saved)
.sorted(Comparator.comparing(AirlineCollectioned::getName).reversed())
.toArray(AirlineCollectioned[]::new),
"Order of airlines does not match");
}
{
Comparator<String> nullSafeStringComparator = Comparator
.nullsFirst(String::compareTo);
Iterable<AirlineCollectioned> result = airlineRepository.findAll(airline.hqCountry.asc().nullsFirst());
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(AirlineCollectioned[]::new),
Arrays.stream(saved)
.sorted(Comparator.comparing(AirlineCollectioned::getHqCountry, nullSafeStringComparator))
.toArray(AirlineCollectioned[]::new),
"Order of airlines does not match");
}
{
Comparator<String> nullSafeStringComparator = Comparator
.nullsFirst(String::compareTo);
Iterable<AirlineCollectioned> result = airlineRepository.findAll(airline.hqCountry.desc().nullsLast());
assertArrayEquals(StreamSupport.stream(result.spliterator(), false).toArray(AirlineCollectioned[]::new),
Arrays.stream(saved)
.sorted(Comparator.comparing(AirlineCollectioned::getHqCountry, nullSafeStringComparator).reversed())
.toArray(AirlineCollectioned[]::new),
"Order of airlines does not match");
}
}
@Test
void testNotIn() {
{
BooleanExpression predicate = airline.name.notIn("Fly By Night", "Sleep By Day");
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved)
.filter(a -> !(a.getName().equals(flyByNight.getName()) || a.getName().equals(sleepByDay.getName())))
.toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE not( (name in $1) )", bq(predicate));
}
{
BooleanExpression predicate = airline.name.notIn(united.getName());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(
comprises(result,
Arrays.stream(saved).filter(a -> !a.getName().equals(united.getName())).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE name != $1", bq(predicate));
}
}
@Test
@Disabled
void testColIsEmpty() {}
@Test
void testLt() {
{
BooleanExpression predicate = airline.name.lt(lufthansa.getName());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(
comprises(result,
Arrays.stream(saved).filter(a -> a.getName().compareTo(lufthansa.getName()) < 0).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE name < $1", bq(predicate));
}
}
@Test
void testGt() {
{
BooleanExpression predicate = airline.name.gt(lufthansa.getName());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(
comprises(result,
Arrays.stream(saved).filter(a -> a.getName().compareTo(lufthansa.getName()) > 0).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE name > $1", bq(predicate));
}
}
@Test
void testLoe() {
{
BooleanExpression predicate = airline.name.loe(lufthansa.getName());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved).filter(a -> a.getName().compareTo(lufthansa.getName()) <= 0).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE name <= $1", bq(predicate));
}
}
@Test
void testGoe() {
{
BooleanExpression predicate = airline.name.goe(lufthansa.getName());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved).filter(a -> a.getName().compareTo(lufthansa.getName()) >= 0).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE name >= $1", bq(predicate));
}
}
// when hqCountry == null, no value is stored therefore isNull is false. Only hqCountry:null gives isNull
// and we don't have that. Conversely, only hqCountry has a value (which is not 'null') gives isNotNull
// so isNull and isNotNull are *not* compliments
@Test
@Disabled
void testIsNull() {
{
BooleanExpression predicate = airline.hqCountry.isNull();
Optional<AirlineCollectioned> result = airlineRepository.findOne(predicate);
assertNull(exactly(result, nullStringAirline), "[unexpected] -> [missing]");
assertEquals(" WHERE name = $1", bq(predicate));
}
}
// when hqCountry == null, no value is stored therefore isNull is false. Only hqCountry:null gives isNull
// and we don't have that. Conversely, only hqCountry has a value (which is not 'null') gives isNotNull
// so isNull and isNotNull are *not* compliments
@Test
void testIsNotNull() {
{
BooleanExpression predicate = airline.hqCountry.isNotNull();
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result, Arrays.stream(saved).filter(a -> a.getHqCountry() != null).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE hqCountry is not null", bq(predicate));
}
}
@Test
@Disabled
void testContainsKey() {}
@Test
void testStringLength() {
{
BooleanExpression predicate = airline.name.length().eq(united.getName().length());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result,
Arrays.stream(saved).filter(a -> a.getName().length() == united.getName().length()).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE LENGTH(name) = $1", bq(predicate));
}
{
BooleanExpression predicate = airline.name.length().eq(flyByNight.getName().length());
Iterable<AirlineCollectioned> result = airlineRepository.findAll(predicate);
assertNull(comprises(result, Arrays.stream(saved)
.filter(a -> a.getName().length() == flyByNight.getName().length()).toArray(AirlineCollectioned[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE LENGTH(name) = $1", bq(predicate));
}
}
@Configuration
@EnableCouchbaseRepositories("org.springframework.data.couchbase")
@EnableCouchbaseAuditing(auditorAwareRef = "auditorAwareRef", dateTimeProviderRef = "dateTimeProviderRef")
@EnableReactiveCouchbaseAuditing(auditorAwareRef = "reactiveAuditorAwareRef", dateTimeProviderRef = "dateTimeProviderRef")
static class Config extends org.springframework.data.couchbase.domain.Config {
@Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
@Bean
public ValidatingCouchbaseEventListener validationEventListener() {
return new ValidatingCouchbaseEventListener(validator());
}
}
String bq(Predicate predicate) {
BasicQuery basicQuery = new BasicQuery((QueryCriteriaDefinition) serializer.handle(predicate), null);
return basicQuery.export(new int[1]);
}
}

View File

@@ -40,9 +40,9 @@ public class CollectionAwareDefaultScopeIntegrationTests extends JavaIntegration
public static String scopeName = "_default";// + randomString();
public static String collectionName = "my_collection";// + randomString();
public static String otherScope = "other_scope";
public static final String otherScope = "other_scope";
public static String collectionName2 = "my_collection2";// + randomString();
public static String otherCollection = "other_collection";// + randomString();
public static final String otherCollection = "other_collection";// + randomString();
@BeforeAll
public static void beforeAll() {