Skip to content

Commit 548823f

Browse files
feat: add support for not equals queries in datastore integration (#3635)
* chore: add support for NOT_EQUALS in datastore integration * update docs * remove debug config * restore product repository * correct whitespace
1 parent 06ca7a7 commit 548823f

File tree

9 files changed

+95
-5
lines changed

9 files changed

+95
-5
lines changed

docs/src/main/asciidoc/datastore.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,8 @@ public interface TradeRepository extends DatastoreRepository<Trade, String[]> {
965965
List<TestEntity> findBySymbol(String symbol, Sort sort);
966966
967967
Stream<TestEntity> findBySymbol(String symbol);
968+
969+
Stream<TestEntity> findBySymbolIsNot(String symbol);
968970
}
969971
----
970972

@@ -975,6 +977,7 @@ NOTE: You can refer to nested fields using https://docs.spring.io/spring-data/jp
975977
Cloud Datastore only supports filter components joined by AND, and the following operations:
976978

977979
* `equals`
980+
* `not equals`
978981
* `greater than or equals`
979982
* `greater than`
980983
* `less than or equals`

spring-cloud-gcp-data-datastore/src/main/java/com/google/cloud/spring/data/datastore/repository/query/PartTreeDatastoreQuery.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static org.springframework.data.repository.query.parser.Part.Type.GREATER_THAN_EQUAL;
2121
import static org.springframework.data.repository.query.parser.Part.Type.LESS_THAN;
2222
import static org.springframework.data.repository.query.parser.Part.Type.LESS_THAN_EQUAL;
23+
import static org.springframework.data.repository.query.parser.Part.Type.NEGATING_SIMPLE_PROPERTY;
2324
import static org.springframework.data.repository.query.parser.Part.Type.SIMPLE_PROPERTY;
2425

2526
import com.google.cloud.datastore.Cursor;
@@ -92,6 +93,7 @@ public class PartTreeDatastoreQuery<T> extends AbstractDatastoreQuery<T> {
9293
.put(GREATER_THAN, PropertyFilter::gt)
9394
.put(LESS_THAN_EQUAL, PropertyFilter::le)
9495
.put(LESS_THAN, PropertyFilter::lt)
96+
.put(NEGATING_SIMPLE_PROPERTY, PropertyFilter::neq)
9597
.build();
9698

9799
/**

spring-cloud-gcp-data-datastore/src/test/java/com/google/cloud/spring/data/datastore/it/DatastoreIntegrationTests.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,17 @@ void saveEntities() {
178178
.untilAsserted(() -> assertThat(this.testEntityRepository.countBySize(1L)).isEqualTo(3));
179179
}
180180

181+
@Test
182+
void testNotEquals() {
183+
Pet linux = new Dog("Linux");
184+
Pet lukas = new Dog("Lukas");
185+
petRepository.save(linux);
186+
petRepository.save(lukas);
187+
List<Pet> result = petRepository.findByNameNot("Linux");
188+
assertThat(result).hasSize(1);
189+
assertThat(result.get(0).getName()).isEqualTo("Lukas");
190+
}
191+
181192
@Test
182193
void testFindByExampleReference() {
183194
Store store1 = new Store("store1");
@@ -693,7 +704,8 @@ void ancestorsTest() {
693704
void referenceTest() {
694705
ReferenceEntity parent = saveReferenceEntitiesGraph();
695706

696-
ReferenceEntity loadedParent = this.datastoreTemplate.findById(parent.id, ReferenceEntity.class);
707+
ReferenceEntity loadedParent =
708+
this.datastoreTemplate.findById(parent.id, ReferenceEntity.class);
697709
assertThat(loadedParent).isEqualTo(parent);
698710

699711
parent.name = "parent updated";
@@ -718,7 +730,8 @@ void referenceTest() {
718730
void lazyReferenceCollectionTest() {
719731
ReferenceLazyEntity parent = saveEntitiesGraph();
720732

721-
ReferenceLazyEntity lazyParent = this.datastoreTemplate.findById(parent.id, ReferenceLazyEntity.class);
733+
ReferenceLazyEntity lazyParent =
734+
this.datastoreTemplate.findById(parent.id, ReferenceLazyEntity.class);
722735

723736
// Saving an entity with not loaded lazy field
724737
this.datastoreTemplate.save(lazyParent);
@@ -766,7 +779,8 @@ void lazyReferenceTransactionTest() {
766779

767780
// Exception should be produced if a lazy loaded property accessed outside of the initial
768781
// transaction
769-
ReferenceLazyEntity finalLoadedParent = this.transactionalTemplateService.findByIdLazy(parent.id);
782+
ReferenceLazyEntity finalLoadedParent =
783+
this.transactionalTemplateService.findByIdLazy(parent.id);
770784
assertThatThrownBy(() -> finalLoadedParent.children.size())
771785
.isInstanceOf(DatastoreDataException.class)
772786
.hasMessage("Lazy load should be invoked within the same transaction");
@@ -1016,8 +1030,6 @@ void sameClassDescendantsTest() {
10161030
assertThat(readCompany.leaders.get(0).id).isEqualTo(entity1.id);
10171031
}
10181032

1019-
1020-
10211033
@Test
10221034
void testPageableGqlEntityProjectionsPage() {
10231035
Page<TestEntityProjection> page =

spring-cloud-gcp-data-datastore/src/test/java/com/google/cloud/spring/data/datastore/it/testdomains/Pet.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,8 @@ public abstract class Pet {
3232
}
3333

3434
public abstract String speak();
35+
36+
public String getName() {
37+
return name;
38+
}
3539
}

spring-cloud-gcp-data-datastore/src/test/java/com/google/cloud/spring/data/datastore/it/testdomains/PetRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@
2121

2222
public interface PetRepository extends DatastoreRepository<Pet, Long> {
2323
List<Pet> findByName(String s);
24+
25+
List<Pet> findByNameNot(String s);
2426
}

spring-cloud-gcp-data-datastore/src/test/java/com/google/cloud/spring/data/datastore/repository/query/PartTreeDatastoreQueryTests.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,49 @@ private ProjectionInformation getProjectionInformationMock() {
152152
return mock;
153153
}
154154

155+
@Test
156+
void notEqualsSupportTest() throws NoSuchMethodException {
157+
queryWithMockResult(
158+
"findBySymbolNotAndSymbolIsNot",
159+
null,
160+
getClass()
161+
.getMethod(
162+
"tradeMethodNotEquals",
163+
String.class,
164+
String.class));
165+
166+
Object[] params =
167+
new Object[] {
168+
"abcd",
169+
"abcde",
170+
};
171+
172+
when(this.datastoreTemplate.queryKeysOrEntities(any(), any()))
173+
.thenAnswer(
174+
invocation -> {
175+
EntityQuery statement = invocation.getArgument(0);
176+
177+
EntityQuery expected =
178+
StructuredQuery.newEntityQueryBuilder()
179+
.setFilter(
180+
CompositeFilter.and(
181+
PropertyFilter.neq("ticker", "abcd"),
182+
PropertyFilter.neq("ticker", "abcde")
183+
))
184+
.setKind("trades")
185+
.build();
186+
187+
assertThat(statement).isEqualTo(expected);
188+
189+
return EMPTY_RESPONSE;
190+
});
191+
192+
when(this.queryMethod.getCollectionReturnType()).thenReturn(List.class);
193+
194+
this.partTreeDatastoreQuery.execute(params);
195+
verify(this.datastoreTemplate).queryKeysOrEntities(any(), any());
196+
}
197+
155198
@Test
156199
void compoundNameConventionTest() throws NoSuchMethodException {
157200
queryWithMockResult(
@@ -1126,6 +1169,11 @@ public List<Trade> tradeMethod(
11261169
return null;
11271170
}
11281171

1172+
public List<Trade> tradeMethodNotEquals(
1173+
String symbolNeq, String symbolNeq2) {
1174+
return null;
1175+
}
1176+
11291177
public int traderAndPrice() {
11301178
return 0;
11311179
}

spring-cloud-gcp-samples/spring-cloud-gcp-data-datastore-basic-sample/src/main/java/com/example/BookController.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ public String findByAuthor(@RequestParam("author") String author) {
5757
return books.toString();
5858
}
5959

60+
@GetMapping("/findByAuthorNotEquals")
61+
public String findByAuthorNotEquals(
62+
@RequestParam("author") String author) {
63+
List<Book> books = this.bookRepository.findByAuthorNot(author);
64+
return books.toString();
65+
}
66+
6067
@GetMapping("/findByYearGreaterThan")
6168
public String findByYearGreaterThan(@RequestParam("year") Optional<Integer> year) {
6269
List<Book> books = this.bookRepository.findByYearGreaterThan(year.orElse(0));

spring-cloud-gcp-samples/spring-cloud-gcp-data-datastore-basic-sample/src/main/java/com/example/BookRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,6 @@ public interface BookRepository extends DatastoreRepository<Book, Long> {
3030
List<Book> findByYearGreaterThan(int year);
3131

3232
List<Book> findByAuthorAndYear(String author, int year);
33+
34+
List<Book> findByAuthorNot(String author);
3335
}

spring-cloud-gcp-samples/spring-cloud-gcp-data-datastore-basic-sample/src/test/java/com/example/DatastoreBookshelfExampleIntegrationTests.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,16 @@ void removeAllBooksTest() {
143143
});
144144
}
145145

146+
@Test
147+
void findByAuthorNotEqualsTest() {
148+
String responseBody = sendRequest("/findByAuthorNotEquals?author=Philip K. Dick", null, HttpMethod.GET);
149+
assertThat(responseBody)
150+
.contains("title='The Moon Is a Harsh Mistress', author='Robert A. Heinlein', year=1966")
151+
.contains("title='Stranger in a Strange Land', author='Robert A. Heinlein', year=1961")
152+
.doesNotContain("title='The Crack in Space', author='Philip K. Dick', year=1966")
153+
.doesNotContain("title='Ubik', author='Philip K. Dick', year=1969");
154+
}
155+
146156
private String sendRequest(String url, String json, HttpMethod method) {
147157
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
148158
map.add("Content-Type", "application/json");

0 commit comments

Comments
 (0)