Skip to content

Commit c6a729e

Browse files
hannes-angstboostchickenfrommeyercworkromancer
authored
Introduce string set handling (#34)
* v5.2.3 (#18) * Read Consistency Setting * #8 Custom Mapper Spec * #8 Custom Mapper CDI fix * Fix symlink to CONTRIBUTING.md (#13) * #8 Don't use autowired or constructor injection with FactoryBeans (#12) * [maven-release-plugin] prepare release v5.2.2 * [maven-release-plugin] prepare for next development iteration * 5.2.2 docs fix * Hibernate Validator CVE fix GHSA-m8p2-495h-ccmh * Documentation updates * Create FUNDING.yml * Fixed a Type (#17) Co-authored-by: thedevluffy <[email protected]> * Documentation updates * 5.2.3 prep * [maven-release-plugin] prepare release v5.2.3 * [maven-release-plugin] prepare for next development iteration Co-authored-by: Christian Frommeyer <[email protected]> Co-authored-by: thedevluffy <[email protected]> * ✨ Allow CONTAINS and NOT_CONTAINS is repositories * 🎨 cleanup * 🔧 check for collection * 🔧 check for collection * ✏️ cleanup * 🎨 cleanup imports * Remove funding changes Co-authored-by: John D <[email protected]> Co-authored-by: Christian Frommeyer <[email protected]> Co-authored-by: thedevluffy <[email protected]> Co-authored-by: John Dorman <[email protected]> Co-authored-by: Hannes Angst <[email protected]>
1 parent de80ee4 commit c6a729e

File tree

5 files changed

+280
-28
lines changed

5 files changed

+280
-28
lines changed

src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/AbstractDynamoDBQueryCreator.java

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.util.Arrays;
4040
import java.util.HashMap;
4141
import java.util.Iterator;
42+
import java.util.List;
4243
import java.util.Map;
4344
import java.util.Optional;
4445

@@ -139,18 +140,12 @@ protected DynamoDBQueryCriteria<T, ID> addCriteria(DynamoDBQueryCriteria<T, ID>
139140

140141
switch (part.getType()) {
141142
case IN :
142-
Object in = iterator.next();
143-
Assert.notNull(in, "Creating conditions on null parameters not supported: please specify a value for '"
144-
+ leafNodePropertyName + "'");
145-
boolean isIterable = ClassUtils.isAssignable(Iterable.class, in.getClass());
146-
boolean isArray = ObjectUtils.isArray(in);
147-
Assert.isTrue(isIterable || isArray, "In criteria can only operate with Iterable or Array parameters");
148-
Iterable<?> iterable = isIterable ? ((Iterable<?>) in) : Arrays.asList(ObjectUtils.toObjectArray(in));
149-
return criteria.withPropertyIn(leafNodePropertyName, iterable, leafNodePropertyType);
150-
case CONTAINING :
151-
return criteria.withSingleValueCriteria(leafNodePropertyName, ComparisonOperator.CONTAINS,
152-
iterator.next(), leafNodePropertyType);
153-
case STARTING_WITH :
143+
return getInProperty(criteria, iterator, leafNodePropertyType, leafNodePropertyName);
144+
case CONTAINING :
145+
return getItemsProperty(criteria, ComparisonOperator.CONTAINS, iterator, leafNodePropertyType, leafNodePropertyName);
146+
case NOT_CONTAINING:
147+
return getItemsProperty(criteria, ComparisonOperator.NOT_CONTAINS, iterator, leafNodePropertyType, leafNodePropertyName);
148+
case STARTING_WITH :
154149
return criteria.withSingleValueCriteria(leafNodePropertyName, ComparisonOperator.BEGINS_WITH,
155150
iterator.next(), leafNodePropertyType);
156151
case BETWEEN :
@@ -192,7 +187,38 @@ protected DynamoDBQueryCriteria<T, ID> addCriteria(DynamoDBQueryCriteria<T, ID>
192187

193188
}
194189

195-
@Override
190+
private DynamoDBQueryCriteria<T, ID> getItemsProperty(DynamoDBQueryCriteria<T, ID> criteria, ComparisonOperator comparisonOperator, Iterator<Object> iterator, Class<?> leafNodePropertyType, String leafNodePropertyName) {
191+
Object in = iterator.next();
192+
Assert.notNull(in, "Creating conditions on null parameters not supported: please specify a value for '" + leafNodePropertyName + "'");
193+
194+
if(ObjectUtils.isArray(in)) {
195+
List<?> list = Arrays.asList(ObjectUtils.toObjectArray(in));
196+
Assert.isTrue(list.size()==1, "Only one value is supported: please specify a value for '\" + leafNodePropertyName + \"'\"");
197+
Object value = list.get(0);
198+
return criteria.withSingleValueCriteria(leafNodePropertyName, comparisonOperator, value, leafNodePropertyType);
199+
} else if(ClassUtils.isAssignable(Iterable.class, in.getClass())) {
200+
Iterator<?> iter = ((Iterable<?>) in).iterator();
201+
Assert.isTrue(iter.hasNext(), "Creating conditions on empty parameters not supported: please specify a value for '\" + leafNodePropertyName + \"'\"");
202+
Object value = iter.next();
203+
Assert.isTrue(!iter.hasNext(), "Only one value is supported: please specify a value for '\" + leafNodePropertyName + \"'\"");
204+
return criteria.withSingleValueCriteria(leafNodePropertyName, comparisonOperator, value, leafNodePropertyType);
205+
} else {
206+
return criteria.withSingleValueCriteria(leafNodePropertyName, comparisonOperator, in, leafNodePropertyType);
207+
}
208+
}
209+
210+
private DynamoDBQueryCriteria<T, ID> getInProperty(DynamoDBQueryCriteria<T, ID> criteria, Iterator<Object> iterator, Class<?> leafNodePropertyType, String leafNodePropertyName) {
211+
Object in = iterator.next();
212+
Assert.notNull(in, "Creating conditions on null parameters not supported: please specify a value for '"
213+
+ leafNodePropertyName + "'");
214+
boolean isIterable = ClassUtils.isAssignable(Iterable.class, in.getClass());
215+
boolean isArray = ObjectUtils.isArray(in);
216+
Assert.isTrue(isIterable || isArray, "In criteria can only operate with Iterable or Array parameters");
217+
Iterable<?> iterable = isIterable ? ((Iterable<?>) in) : Arrays.asList(ObjectUtils.toObjectArray(in));
218+
return criteria.withPropertyIn(leafNodePropertyName, iterable, leafNodePropertyType);
219+
}
220+
221+
@Override
196222
protected DynamoDBQueryCriteria<T, ID> and(Part part, DynamoDBQueryCriteria<T, ID> base,
197223
Iterator<Object> iterator) {
198224
return addCriteria(base, part, iterator);

src/main/java/org/socialsignin/spring/data/dynamodb/repository/query/AbstractDynamoDBQueryCriteria.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ public DynamoDBQueryCriteria<T, ID> withPropertyIn(String propertyName, Iterable
476476
return withCondition(propertyName, condition);
477477
}
478478

479-
@Override
479+
@Override
480480
public DynamoDBQueryCriteria<T, ID> withSingleValueCriteria(String propertyName,
481481
ComparisonOperator comparisonOperator, Object value, Class<?> propertyType) {
482482
if (comparisonOperator.equals(ComparisonOperator.EQ)) {
@@ -554,8 +554,15 @@ protected <V extends Object> Object getPropertyAttributeValue(final String prope
554554

555555
DynamoDBMapperFieldModel<T, Object> fieldModel = tableModel.field(attributeName);
556556
if (fieldModel != null) {
557-
return fieldModel.convert(value);
558-
}
557+
if (fieldModel.attributeType() == DynamoDBMapperFieldModel.DynamoDBAttributeType.SS) {
558+
if(value instanceof Collection) {
559+
return fieldModel.convert(value);
560+
} else {
561+
return new AttributeValue(value.toString());
562+
}
563+
}
564+
return fieldModel.convert(value);
565+
}
559566
}
560567

561568
return value;

src/test/java/org/socialsignin/spring/data/dynamodb/domain/sample/CRUDOperationsIT.java

Lines changed: 159 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
import org.junit.Before;
1919
import org.junit.Rule;
20-
2120
import org.junit.Test;
2221
import org.junit.rules.ExpectedException;
2322
import org.junit.runner.RunWith;
@@ -36,8 +35,10 @@
3635
import java.time.Instant;
3736
import java.util.ArrayList;
3837
import java.util.Arrays;
38+
import java.util.HashSet;
3939
import java.util.List;
4040
import java.util.Optional;
41+
import java.util.Set;
4142
import java.util.concurrent.ExecutionException;
4243
import java.util.concurrent.Future;
4344
import java.util.concurrent.ThreadLocalRandom;
@@ -263,14 +264,163 @@ public void testFilterAndPagination() {
263264

264265
@Test
265266
public void testDeleteNonExistentIdWithCondition() {
266-
// Delete conditional
267-
userRepository.deleteByIdAndName("non-existent", "non-existent");
268-
}
267+
// Delete conditional
268+
userRepository.deleteByIdAndName("non-existent", "non-existent");
269+
}
270+
271+
@Test
272+
public void testDeleteNonExistingGsiWithCondition() {
273+
// Delete via GSI
274+
userRepository.deleteByPostCodeAndNumberOfPlaylists("non-existing", 23);
275+
276+
}
277+
278+
279+
@Test
280+
public void testFilterWithCollections() {
281+
// Prepare
282+
User u1 = new User();
283+
String name1 = "name1" + ThreadLocalRandom.current().nextLong();
284+
u1.setId("u1");
285+
u1.setName(name1);
286+
u1.setPostCode("1234");
287+
Set<String> u1Tags = new HashSet<>();
288+
u1Tags.add("tag-a");
289+
u1Tags.add("tag-b");
290+
u1Tags.add("tag-c");
291+
u1.setTags(u1Tags);
292+
293+
User u2 = new User();
294+
String name2 = "name1" + ThreadLocalRandom.current().nextLong();
295+
u2.setId("u2");
296+
u2.setName(name2);
297+
u2.setPostCode("1234");
298+
Set<String> u2Tags = new HashSet<>();
299+
u2Tags.add("tag-a");
300+
u2Tags.add("tag-b");
301+
u2.setTags(u2Tags);
302+
303+
User u3 = new User();
304+
String name3 = "name1" + ThreadLocalRandom.current().nextLong();
305+
u3.setId("u3");
306+
u3.setName(name3);
307+
u3.setPostCode("1234");
308+
Set<String> u3Tags = new HashSet<>();
309+
u3Tags.add("tag-a");
310+
u3Tags.add("tag-c");
311+
u3.setTags(u3Tags);
312+
313+
314+
u1 = userRepository.save(u1);
315+
u2 = userRepository.save(u2);
316+
u3 = userRepository.save(u3);
317+
318+
Set<User> tagA = setOf(u1, u2, u3);
319+
Set<User> tagB = setOf(u1,u2);
320+
Set<User> tagC = setOf(u1, u3);
321+
322+
Set<User> notTagA = setOf();
323+
Set<User> notTagB = setOf(u3);
324+
Set<User> notTagC = setOf(u2);
325+
326+
327+
// Single value
328+
assertEquals(tagA, new HashSet<>(userRepository.findAllByTagsContaining("tag-a")));
329+
assertEquals(tagB, new HashSet<>(userRepository.findAllByTagsContaining("tag-b")));
330+
assertEquals(tagC, new HashSet<>(userRepository.findAllByTagsContaining("tag-c")));
331+
332+
assertEquals(tagA, new HashSet<>(userRepository.findAllByTagsContains("tag-a")));
333+
assertEquals(tagB, new HashSet<>(userRepository.findAllByTagsContains("tag-b")));
334+
assertEquals(tagC, new HashSet<>(userRepository.findAllByTagsContains("tag-c")));
335+
336+
assertEquals(tagA, new HashSet<>(userRepository.findAllByTagsIsContaining("tag-a")));
337+
assertEquals(tagB, new HashSet<>(userRepository.findAllByTagsIsContaining("tag-b")));
338+
assertEquals(tagC, new HashSet<>(userRepository.findAllByTagsIsContaining("tag-c")));
339+
340+
assertEquals(notTagA, new HashSet<>(userRepository.findAllByTagsNotContaining("tag-a")));
341+
assertEquals(notTagB, new HashSet<>(userRepository.findAllByTagsNotContaining("tag-b")));
342+
assertEquals(notTagC, new HashSet<>(userRepository.findAllByTagsNotContaining("tag-c")));
343+
344+
assertEquals(notTagA, new HashSet<>(userRepository.findAllByTagsNotContains("tag-a")));
345+
assertEquals(notTagB, new HashSet<>(userRepository.findAllByTagsNotContains("tag-b")));
346+
assertEquals(notTagC, new HashSet<>(userRepository.findAllByTagsNotContains("tag-c")));
347+
348+
assertEquals(notTagA, new HashSet<>(userRepository.findAllByTagsIsNotContaining("tag-a")));
349+
assertEquals(notTagB, new HashSet<>(userRepository.findAllByTagsIsNotContaining("tag-b")));
350+
assertEquals(notTagC, new HashSet<>(userRepository.findAllByTagsIsNotContaining("tag-c")));
351+
352+
353+
//Sets
354+
assertEquals(tagA, new HashSet<>(userRepository.findAllByTagsContaining(setOf("tag-a"))));
355+
assertEquals(tagB, new HashSet<>(userRepository.findAllByTagsContaining(setOf("tag-b"))));
356+
assertEquals(tagC, new HashSet<>(userRepository.findAllByTagsContaining(setOf("tag-c"))));
357+
358+
assertEquals(tagA, new HashSet<>(userRepository.findAllByTagsContains(setOf("tag-a"))));
359+
assertEquals(tagB, new HashSet<>(userRepository.findAllByTagsContains(setOf("tag-b"))));
360+
assertEquals(tagC, new HashSet<>(userRepository.findAllByTagsContains(setOf("tag-c"))));
361+
362+
assertEquals(tagA, new HashSet<>(userRepository.findAllByTagsIsContaining(setOf("tag-a"))));
363+
assertEquals(tagB, new HashSet<>(userRepository.findAllByTagsIsContaining(setOf("tag-b"))));
364+
assertEquals(tagC, new HashSet<>(userRepository.findAllByTagsIsContaining(setOf("tag-c"))));
365+
366+
assertEquals(notTagA, new HashSet<>(userRepository.findAllByTagsNotContaining(setOf("tag-a"))));
367+
assertEquals(notTagB, new HashSet<>(userRepository.findAllByTagsNotContaining(setOf("tag-b"))));
368+
assertEquals(notTagC, new HashSet<>(userRepository.findAllByTagsNotContaining(setOf("tag-c"))));
369+
370+
assertEquals(notTagA, new HashSet<>(userRepository.findAllByTagsNotContains(setOf("tag-a"))));
371+
assertEquals(notTagB, new HashSet<>(userRepository.findAllByTagsNotContains(setOf("tag-b"))));
372+
assertEquals(notTagC, new HashSet<>(userRepository.findAllByTagsNotContains(setOf("tag-c"))));
373+
374+
assertEquals(notTagA, new HashSet<>(userRepository.findAllByTagsIsNotContaining(setOf("tag-a"))));
375+
assertEquals(notTagB, new HashSet<>(userRepository.findAllByTagsIsNotContaining(setOf("tag-b"))));
376+
assertEquals(notTagC, new HashSet<>(userRepository.findAllByTagsIsNotContaining(setOf("tag-c"))));
377+
378+
//Lists
379+
assertEquals(tagA, new HashSet<>(userRepository.findAllByTagsContaining(listOf("tag-a"))));
380+
assertEquals(tagB, new HashSet<>(userRepository.findAllByTagsContaining(listOf("tag-b"))));
381+
assertEquals(tagC, new HashSet<>(userRepository.findAllByTagsContaining(listOf("tag-c"))));
382+
383+
assertEquals(tagA, new HashSet<>(userRepository.findAllByTagsContains(listOf("tag-a"))));
384+
assertEquals(tagB, new HashSet<>(userRepository.findAllByTagsContains(listOf("tag-b"))));
385+
assertEquals(tagC, new HashSet<>(userRepository.findAllByTagsContains(listOf("tag-c"))));
386+
387+
assertEquals(tagA, new HashSet<>(userRepository.findAllByTagsIsContaining(listOf("tag-a"))));
388+
assertEquals(tagB, new HashSet<>(userRepository.findAllByTagsIsContaining(listOf("tag-b"))));
389+
assertEquals(tagC, new HashSet<>(userRepository.findAllByTagsIsContaining(listOf("tag-c"))));
390+
391+
assertEquals(notTagA, new HashSet<>(userRepository.findAllByTagsNotContaining(listOf("tag-a"))));
392+
assertEquals(notTagB, new HashSet<>(userRepository.findAllByTagsNotContaining(listOf("tag-b"))));
393+
assertEquals(notTagC, new HashSet<>(userRepository.findAllByTagsNotContaining(listOf("tag-c"))));
394+
395+
assertEquals(notTagA, new HashSet<>(userRepository.findAllByTagsNotContains(listOf("tag-a"))));
396+
assertEquals(notTagB, new HashSet<>(userRepository.findAllByTagsNotContains(listOf("tag-b"))));
397+
assertEquals(notTagC, new HashSet<>(userRepository.findAllByTagsNotContains(listOf("tag-c"))));
269398

270-
@Test
271-
public void testDeleteNonExistingGsiWithConditoin() {
272-
// Delete via GSI
273-
userRepository.deleteByPostCodeAndNumberOfPlaylists("non-existing", 23);
399+
assertEquals(notTagA, new HashSet<>(userRepository.findAllByTagsIsNotContaining(listOf("tag-a"))));
400+
assertEquals(notTagB, new HashSet<>(userRepository.findAllByTagsIsNotContaining(listOf("tag-b"))));
401+
assertEquals(notTagC, new HashSet<>(userRepository.findAllByTagsIsNotContaining(listOf("tag-c"))));
274402

275-
}
403+
404+
}
405+
406+
407+
@SafeVarargs
408+
private final <E> Set<E> setOf(E... values) {
409+
Set<E> result = new HashSet<>();
410+
411+
if (values != null) {
412+
result.addAll(Arrays.asList(values));
413+
}
414+
return result;
415+
}
416+
417+
@SafeVarargs
418+
private final <E> List<E> listOf(E... values) {
419+
List<E> result = new ArrayList<>();
420+
421+
if (values != null) {
422+
result.addAll(Arrays.asList(values));
423+
}
424+
return result;
425+
}
276426
}

src/test/java/org/socialsignin/spring/data/dynamodb/domain/sample/User.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public class User {
3838

3939
private Date joinDate;
4040

41+
private Set<String> tags;
42+
4143
@SuppressWarnings("deprecation")
4244
@DynamoDBMarshalling(marshallerClass = DynamoDBYearMarshaller.class)
4345
private Date joinYear;
@@ -72,7 +74,15 @@ public void setJoinYear(Date joinYear) {
7274
this.joinYear = joinYear;
7375
}
7476

75-
@SuppressWarnings("deprecation")
77+
public Set<String> getTags() {
78+
return tags;
79+
}
80+
81+
public void setTags(Set<String> tags) {
82+
this.tags = tags;
83+
}
84+
85+
@SuppressWarnings("deprecation")
7686
@DynamoDBMarshalling(marshallerClass = Instant2IsoDynamoDBMarshaller.class)
7787
public Instant getLeaveDate() {
7888
return leaveDate;
@@ -184,4 +194,8 @@ public boolean equals(Object obj) {
184194
return true;
185195
}
186196

197+
@Override
198+
public String toString() {
199+
return "<User "+ id +">";
200+
}
187201
}

0 commit comments

Comments
 (0)