Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support AllIgnoreCase and IgnoreCase keywords in Cosmos #13128

Merged
merged 4 commits into from
Jul 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
Expand Down Expand Up @@ -462,7 +463,7 @@ public <T, ID> List<T> findByIds(Iterable<ID> ids, Class<T> domainType, String c
Assert.hasText(containerName, "container should not be null, empty or only whitespaces");

final DocumentQuery query = new DocumentQuery(Criteria.getInstance(CriteriaType.IN, "id",
Collections.singletonList(ids)));
Collections.singletonList(ids), Part.IgnoreCaseType.NEVER));
return find(query, domainType, containerName);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.azure.spring.data.cosmos.exception.IllegalQueryException;
import org.javatuples.Pair;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
Expand Down Expand Up @@ -53,13 +54,48 @@ private String generateBinaryQuery(@NonNull Criteria criteria, @NonNull List<Pai
final String subject = criteria.getSubject();
final Object subjectValue = toCosmosDbValue(criteria.getSubjectValues().get(0));
final String parameter = generateQueryParameter(subject);

final Part.IgnoreCaseType ignoreCase = criteria.getIgnoreCase();
final String sqlKeyword = criteria.getType().getSqlKeyword();
parameters.add(Pair.with(parameter, subjectValue));

if (CriteriaType.isFunction(criteria.getType())) {
return String.format("%s(r.%s, @%s)", criteria.getType().getSqlKeyword(), subject, parameter);
return getFunctionCondition(ignoreCase, sqlKeyword, subject, parameter);
} else {
return getCondition(ignoreCase, sqlKeyword, subject, parameter);
}
}

/**
* Get condition string with function
* @param ignoreCase ignore case flag
* @param sqlKeyword sql key word, operation name
* @param subject sql column name
* @param parameter sql filter value
* @return condition string
*/
private String getCondition(final Part.IgnoreCaseType ignoreCase, final String sqlKeyword,
final String subject, final String parameter) {
if (Part.IgnoreCaseType.NEVER == ignoreCase) {
return String.format("r.%s %s @%s", subject, sqlKeyword, parameter);
} else {
return String.format("UPPER(r.%s) %s UPPER(@%s)", subject, sqlKeyword, parameter);
}
}

/**
* Get condition string without function
* @param ignoreCase ignore case flag
* @param sqlKeyword sql key word, operation name
* @param subject sql column name
* @param parameter sql filter value
* @return condition string
*/
private String getFunctionCondition(final Part.IgnoreCaseType ignoreCase, final String sqlKeyword,
final String subject, final String parameter) {
if (Part.IgnoreCaseType.NEVER == ignoreCase) {
return String.format("%s(r.%s, @%s)", sqlKeyword, subject, parameter);
} else {
return String.format("r.%s %s @%s", subject, criteria.getType().getSqlKeyword(), parameter);
return String.format("%s(UPPER(r.%s), UPPER(@%s))", sqlKeyword, subject, parameter);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.
package com.azure.spring.data.cosmos.core.query;

import org.springframework.data.repository.query.parser.Part;
import org.springframework.lang.NonNull;

import java.util.ArrayList;
Expand All @@ -16,6 +17,15 @@ public final class Criteria {
private List<Object> subjectValues;
private final CriteriaType type;
private final List<Criteria> subCriteria;
private Part.IgnoreCaseType ignoreCase;

/**
* Ignore case flag
* @return ignore case flag
*/
public Part.IgnoreCaseType getIgnoreCase() {
return ignoreCase;
}

/**
* To get subject
Expand Down Expand Up @@ -55,18 +65,20 @@ private Criteria(CriteriaType type) {
}

/**
* To get a criteria instance with subject
* To get a criteria instance with subject and ignore case
* @param type CriteriaType
* @param subject subject
* @param values subject value
* @param ignoreCase ignore case flag
* @return Criteria instance
*/
public static Criteria getInstance(CriteriaType type, @NonNull String subject, @NonNull List<Object> values) {
public static Criteria getInstance(CriteriaType type, @NonNull String subject,
@NonNull List<Object> values, @NonNull Part.IgnoreCaseType ignoreCase) {
final Criteria criteria = new Criteria(type);

criteria.subject = subject;
criteria.subjectValues = values;

criteria.ignoreCase = ignoreCase;
return criteria;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ protected Criteria create(Part part, Iterator<Object> parameters) {
values.add(parameters.next());
}

return Criteria.getInstance(CriteriaType.toCriteriaType(type), subject, values);
return Criteria.getInstance(CriteriaType.toCriteriaType(type), subject, values, part.shouldIgnoreCase());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ protected Criteria create(Part part, Iterator<Object> parameters) {
values.add(parameters.next());
}

return Criteria.getInstance(CriteriaType.toCriteriaType(type), subject, values);
return Criteria.getInstance(CriteriaType.toCriteriaType(type), subject, values, part.shouldIgnoreCase());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

Expand Down Expand Up @@ -287,12 +288,20 @@ public void testCountByQuery() {
assertThat(responseDiagnosticsTestUtils.getCosmosResponseStatistics()).isNull();

final Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, "firstName",
Collections.singletonList(TEST_PERSON_2.getFirstName()));
Collections.singletonList(TEST_PERSON_2.getFirstName()), Part.IgnoreCaseType.NEVER);
final DocumentQuery query = new DocumentQuery(criteria);

final long count = cosmosTemplate.count(query, Person.class, containerName);
assertThat(count).isEqualTo(1);

// add ignoreCase testing
final Criteria criteriaIgnoreCase = Criteria.getInstance(CriteriaType.IS_EQUAL, "firstName",
Collections.singletonList(TEST_PERSON_2.getFirstName().toUpperCase()), Part.IgnoreCaseType.ALWAYS);
final DocumentQuery queryIgnoreCase = new DocumentQuery(criteriaIgnoreCase);

final long countIgnoreCase = cosmosTemplate.count(queryIgnoreCase, Person.class, containerName);
assertThat(countIgnoreCase).isEqualTo(1);

assertThat(responseDiagnosticsTestUtils.getFeedResponseDiagnostics()).isNotNull();
assertThat(responseDiagnosticsTestUtils.getCosmosResponseStatistics()).isNotNull();
assertThat(responseDiagnosticsTestUtils.getCosmosResponseStatistics().getRequestCharge()).isGreaterThan(0);
Expand Down Expand Up @@ -339,14 +348,23 @@ public void testPaginationQuery() {
assertThat(responseDiagnosticsTestUtils.getCosmosResponseStatistics()).isNull();

final Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, "firstName",
Collections.singletonList(FIRST_NAME));
Collections.singletonList(FIRST_NAME), Part.IgnoreCaseType.NEVER);
final PageRequest pageRequest = new CosmosPageRequest(0, PAGE_SIZE_2, null);
final DocumentQuery query = new DocumentQuery(criteria).with(pageRequest);

final Page<Person> page = cosmosTemplate.paginationQuery(query, Person.class, containerName);
assertThat(page.getContent().size()).isEqualTo(1);
PageTestUtils.validateLastPage(page, page.getContent().size());

// add ignore case testing
final Criteria criteriaIgnoreCase = Criteria.getInstance(CriteriaType.IS_EQUAL, "firstName",
Collections.singletonList(FIRST_NAME.toUpperCase()), Part.IgnoreCaseType.ALWAYS);
final DocumentQuery queryIgnoreCase = new DocumentQuery(criteriaIgnoreCase).with(pageRequest);

final Page<Person> pageIgnoreCase = cosmosTemplate.paginationQuery(queryIgnoreCase, Person.class, containerName);
assertThat(pageIgnoreCase.getContent().size()).isEqualTo(1);
PageTestUtils.validateLastPage(pageIgnoreCase, pageIgnoreCase.getContent().size());

assertThat(responseDiagnosticsTestUtils.getCosmosResponseDiagnostics()).isNull();
assertThat(responseDiagnosticsTestUtils.getFeedResponseDiagnostics()).isNotNull();
assertThat(responseDiagnosticsTestUtils.getCosmosResponseStatistics()).isNotNull();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.util.Assert;

import java.lang.reflect.InvocationTargetException;
Expand Down Expand Up @@ -42,8 +43,10 @@ public void setUp() {

@Test
public void deleteIllegalShouldFail() throws NoSuchMethodException {
final Method method = dbTemplateClass.getMethod("delete", DocumentQuery.class, Class.class, String.class);
final Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, "faker", Arrays.asList("faker-value"));
final Method method = dbTemplateClass.getMethod("delete",
DocumentQuery.class, Class.class, String.class);
final Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL,
"faker", Arrays.asList("faker-value"), Part.IgnoreCaseType.NEVER);
final DocumentQuery query = new DocumentQuery(criteria);

checkIllegalArgument(method, null, Person.class, DUMMY_COLL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.springframework.data.annotation.Persistent;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

Expand Down Expand Up @@ -91,17 +92,32 @@ public static void afterClassCleanup() {

@Test
public void testFindWithPartition() {
Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, PROPERTY_LAST_NAME, Arrays.asList(LAST_NAME));
Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, PROPERTY_LAST_NAME,
Arrays.asList(LAST_NAME), Part.IgnoreCaseType.NEVER);
DocumentQuery query = new DocumentQuery(criteria);
List<PartitionPerson> result = cosmosTemplate.find(query, PartitionPerson.class,
PartitionPerson.class.getSimpleName());

assertThat(result.size()).isEqualTo(1);
assertEquals(TEST_PERSON, result.get(0));

criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, PROPERTY_ID, Arrays.asList(ID_1));
criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, PROPERTY_ID,
Arrays.asList(ID_1), Part.IgnoreCaseType.NEVER);
query = new DocumentQuery(criteria);
result = cosmosTemplate.find(query, PartitionPerson.class, PartitionPerson.class.getSimpleName());
result = cosmosTemplate.find(query, PartitionPerson.class,
PartitionPerson.class.getSimpleName());

assertThat(result.size()).isEqualTo(1);
assertEquals(TEST_PERSON, result.get(0));
}

@Test
public void testFindIgnoreCaseWithPartition() {
Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, PROPERTY_LAST_NAME,
Arrays.asList(LAST_NAME.toUpperCase()), Part.IgnoreCaseType.ALWAYS);
DocumentQuery query = new DocumentQuery(criteria);
List<PartitionPerson> result = cosmosTemplate.find(query, PartitionPerson.class,
PartitionPerson.class.getSimpleName());

assertThat(result.size()).isEqualTo(1);
assertEquals(TEST_PERSON, result.get(0));
Expand All @@ -119,7 +135,8 @@ public void testFindByIdWithPartition() {

@Test
public void testFindByNonExistIdWithPartition() {
final Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, PROPERTY_ID, Arrays.asList(NOT_EXIST_ID));
final Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, PROPERTY_ID,
Arrays.asList(NOT_EXIST_ID), Part.IgnoreCaseType.NEVER);
final DocumentQuery query = new DocumentQuery(criteria);

final List<PartitionPerson> result = cosmosTemplate.find(query, PartitionPerson.class,
Expand Down Expand Up @@ -191,17 +208,29 @@ public void testCountForPartitionedCollectionByQuery() {
cosmosTemplate.insert(TEST_PERSON_2, new PartitionKey(TEST_PERSON_2.getLastName()));

final Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, "firstName",
Arrays.asList(TEST_PERSON_2.getFirstName()));
Arrays.asList(TEST_PERSON_2.getFirstName()), Part.IgnoreCaseType.NEVER);
final DocumentQuery query = new DocumentQuery(criteria);

final long count = cosmosTemplate.count(query, PartitionPerson.class, containerName);
assertThat(count).isEqualTo(1);
}

@Test
public void testCountIgnoreCaseForPartitionedCollectionByQuery() {
cosmosTemplate.insert(TEST_PERSON_2, new PartitionKey(TEST_PERSON_2.getLastName()));
final Criteria criteriaIgnoreCase = Criteria.getInstance(CriteriaType.IS_EQUAL, "firstName",
Arrays.asList(TEST_PERSON_2.getFirstName().toUpperCase()), Part.IgnoreCaseType.ALWAYS);
final DocumentQuery queryIgnoreCase = new DocumentQuery(criteriaIgnoreCase);

final long countIgnoreCase = cosmosTemplate.count(queryIgnoreCase,
PartitionPerson.class, containerName);
assertThat(countIgnoreCase).isEqualTo(1);
}

@Test
public void testNonExistFieldValue() {
final Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, "firstName",
Arrays.asList("non-exist-first-name"));
Arrays.asList("non-exist-first-name"), Part.IgnoreCaseType.NEVER);
final DocumentQuery query = new DocumentQuery(criteria);

final long count = cosmosTemplate.count(query, PartitionPerson.class, containerName);
Expand Down Expand Up @@ -229,12 +258,26 @@ public void testPartitionedPaginationQuery() {
cosmosTemplate.insert(TEST_PERSON_2, new PartitionKey(TEST_PERSON_2.getLastName()));

final Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, "firstName",
Arrays.asList(FIRST_NAME));
Arrays.asList(FIRST_NAME), Part.IgnoreCaseType.NEVER);
final PageRequest pageRequest = new CosmosPageRequest(0, PAGE_SIZE_2, null);
final DocumentQuery query = new DocumentQuery(criteria).with(pageRequest);

final Page<PartitionPerson> page = cosmosTemplate.paginationQuery(query, PartitionPerson.class, containerName);
assertThat(page.getContent().size()).isEqualTo(1);
PageTestUtils.validateLastPage(page, page.getContent().size());
}

@Test
public void testPartitionedPaginationQueryIgnoreCase() {
cosmosTemplate.insert(TEST_PERSON_2, new PartitionKey(TEST_PERSON_2.getLastName()));
final Criteria criteriaIgnoreCase = Criteria.getInstance(CriteriaType.IS_EQUAL, "firstName",
Arrays.asList(FIRST_NAME.toUpperCase()), Part.IgnoreCaseType.ALWAYS);
final PageRequest pageRequest = new CosmosPageRequest(0, PAGE_SIZE_2, null);
final DocumentQuery queryIgnoreCase = new DocumentQuery(criteriaIgnoreCase).with(pageRequest);

final Page<PartitionPerson> pageIgnoreCase = cosmosTemplate
.paginationQuery(queryIgnoreCase, PartitionPerson.class, containerName);
assertThat(pageIgnoreCase.getContent().size()).isEqualTo(1);
PageTestUtils.validateLastPage(pageIgnoreCase, pageIgnoreCase.getContent().size());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.springframework.boot.autoconfigure.domain.EntityScanner;
import org.springframework.context.ApplicationContext;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import reactor.core.publisher.Flux;
Expand Down Expand Up @@ -343,12 +344,20 @@ public void testDeleteByIdBySecondaryKey() {
@Test
public void testFind() {
final Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, "firstName",
Arrays.asList(TEST_PERSON.getFirstName()));
Arrays.asList(TEST_PERSON.getFirstName()), Part.IgnoreCaseType.NEVER);
final DocumentQuery query = new DocumentQuery(criteria);
final Flux<Person> personFlux = cosmosTemplate.find(query, Person.class,
Person.class.getSimpleName());
StepVerifier.create(personFlux).expectNextCount(1).verifyComplete();

// add ignore testing
final Criteria criteriaIgnoreCase = Criteria.getInstance(CriteriaType.IS_EQUAL, "firstName",
Arrays.asList(TEST_PERSON.getFirstName().toUpperCase()), Part.IgnoreCaseType.ALWAYS);
final DocumentQuery queryIgnoreCase = new DocumentQuery(criteriaIgnoreCase);
final Flux<Person> personFluxIgnoreCase = cosmosTemplate.find(queryIgnoreCase, Person.class,
Person.class.getSimpleName());
StepVerifier.create(personFluxIgnoreCase).expectNextCount(1).verifyComplete();

assertThat(responseDiagnosticsTestUtils.getFeedResponseDiagnostics()).isNotNull();
Assertions.assertThat(responseDiagnosticsTestUtils.getCosmosResponseStatistics()).isNotNull();
Assertions.assertThat(responseDiagnosticsTestUtils.getCosmosResponseStatistics().getRequestCharge()).isGreaterThan(0);
Expand All @@ -358,11 +367,18 @@ public void testFind() {
@Test
public void testExists() {
final Criteria criteria = Criteria.getInstance(CriteriaType.IS_EQUAL, "firstName",
Arrays.asList(TEST_PERSON.getFirstName()));
Arrays.asList(TEST_PERSON.getFirstName()), Part.IgnoreCaseType.NEVER);
final DocumentQuery query = new DocumentQuery(criteria);
final Mono<Boolean> exists = cosmosTemplate.exists(query, Person.class, containerName);
StepVerifier.create(exists).expectNext(true).verifyComplete();

// add ignore testing
final Criteria criteriaIgnoreCase = Criteria.getInstance(CriteriaType.IS_EQUAL, "firstName",
Arrays.asList(TEST_PERSON.getFirstName().toUpperCase()), Part.IgnoreCaseType.ALWAYS);
final DocumentQuery queryIgnoreCase = new DocumentQuery(criteriaIgnoreCase);
final Mono<Boolean> existsIgnoreCase = cosmosTemplate.exists(queryIgnoreCase, Person.class, containerName);
StepVerifier.create(existsIgnoreCase).expectNext(true).verifyComplete();

assertThat(responseDiagnosticsTestUtils.getFeedResponseDiagnostics()).isNotNull();
Assertions.assertThat(responseDiagnosticsTestUtils.getCosmosResponseStatistics()).isNotNull();
Assertions.assertThat(responseDiagnosticsTestUtils.getCosmosResponseStatistics().getRequestCharge()).isGreaterThan(0);
Expand Down
Loading