Skip to content

Commit fe9b3a4

Browse files
GH-1862 - Support combining startingWith and endingWith with IgnoreCase in derived queries.
This closes #1862
1 parent cc75e02 commit fe9b3a4

File tree

3 files changed

+43
-48
lines changed

3 files changed

+43
-48
lines changed

spring-data-neo4j/src/main/java/org/springframework/data/neo4j/repository/query/filter/PropertyComparisonBuilder.java

+15-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717

1818
import static org.springframework.data.repository.query.parser.Part.Type.*;
1919

20+
import java.util.Arrays;
2021
import java.util.Collections;
22+
import java.util.HashSet;
2123
import java.util.List;
24+
import java.util.Set;
2225
import java.util.Stack;
2326

2427
import org.neo4j.ogm.cypher.BooleanOperator;
@@ -34,6 +37,17 @@
3437
*/
3538
class PropertyComparisonBuilder extends FilterBuilder {
3639

40+
private static final Set<Part.Type> TYPES_SUPPORTING_IGNORE_CASE;
41+
42+
static {
43+
Set<Part.Type> typesSupportingIgnoreCase = new HashSet<>();
44+
typesSupportingIgnoreCase.add(SIMPLE_PROPERTY);
45+
typesSupportingIgnoreCase.add(CONTAINING);
46+
typesSupportingIgnoreCase.add(STARTING_WITH);
47+
typesSupportingIgnoreCase.add(ENDING_WITH);
48+
TYPES_SUPPORTING_IGNORE_CASE = Collections.unmodifiableSet(typesSupportingIgnoreCase);
49+
}
50+
3751
PropertyComparisonBuilder(Part part, BooleanOperator booleanOperator, Class<?> entityType) {
3852
super(part, booleanOperator, entityType);
3953
}
@@ -128,7 +142,6 @@ private boolean canIgnoreCase(Part part) {
128142
}
129143

130144
private boolean isSupportedIgnoreKeyword(Part part) {
131-
Part.Type type = part.getType();
132-
return type == SIMPLE_PROPERTY || type == CONTAINING;
145+
return TYPES_SUPPORTING_IGNORE_CASE.contains(part.getType());
133146
}
134147
}

spring-data-neo4j/src/test/java/org/springframework/data/neo4j/examples/restaurants/RestaurantTests.java

+24-46
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import java.util.Collections;
2323
import java.util.Date;
2424
import java.util.List;
25+
import java.util.Locale;
26+
import java.util.function.Supplier;
2527

2628
import org.junit.After;
2729
import org.junit.Test;
@@ -370,74 +372,60 @@ public void shouldFindByLaunchDateAfter() {
370372
assertEquals(0, results.size());
371373
}
372374

373-
/**
374-
* All findByPropertyLike does currently is to require an exact match, ignoring case.
375-
*/
376-
@Test // DATAGRAPH-904
377-
public void shouldFindByNameNotLike() {
378-
375+
void runTestThatShouldReturnOnlySFO(Supplier<List<Restaurant>> restaurantSupplier) {
379376
Restaurant restaurant = new Restaurant("San Francisco International Airport (SFO)", 68.0);
380377
restaurantRepository.save(restaurant);
381378

382379
Restaurant kuroda = new Restaurant("Kuroda", 72.4);
383380
restaurantRepository.save(kuroda);
384381

385-
List<Restaurant> results = restaurantRepository.findByNameNotLike("kuroda");
382+
List<Restaurant> results = restaurantSupplier.get();
386383
assertNotNull(results);
387384
assertEquals(1, results.size());
388385
assertEquals("San Francisco International Airport (SFO)", results.get(0).getName());
389-
390386
}
391387

392388
/**
393389
* All findByPropertyLike does currently is to require an exact match, ignoring case.
394390
*/
395391
@Test // DATAGRAPH-904
396-
public void shouldFindByNameLike() {
397-
398-
Restaurant restaurant = new Restaurant("San Francisco International Airport (SFO)", 68.0);
399-
restaurantRepository.save(restaurant);
392+
public void shouldFindByNameNotLike() {
400393

401-
Restaurant kuroda = new Restaurant("Kuroda", 72.4);
402-
restaurantRepository.save(kuroda);
394+
runTestThatShouldReturnOnlySFO(() -> restaurantRepository.findByNameNotLike("kuroda"));
395+
}
403396

404-
List<Restaurant> results = restaurantRepository.findByNameLike("*san francisco international*");
405-
assertNotNull(results);
406-
assertEquals(1, results.size());
407-
assertEquals("San Francisco International Airport (SFO)", results.get(0).getName());
397+
/**
398+
* All findByPropertyLike does currently is to require an exact match, ignoring case.
399+
*/
400+
@Test // DATAGRAPH-904
401+
public void shouldFindByNameLike() {
408402

403+
runTestThatShouldReturnOnlySFO(() -> restaurantRepository.findByNameLike("*san francisco international*"));
409404
}
410405

411406
@Test // DATAGRAPH-904
412407
public void shouldFindByNameStartingWith() {
413408

414-
Restaurant restaurant = new Restaurant("San Francisco International Airport (SFO)", 68.0);
415-
restaurantRepository.save(restaurant);
409+
runTestThatShouldReturnOnlySFO(() -> restaurantRepository.findByNameStartingWith("San Francisco"));
416410

417-
Restaurant kuroda = new Restaurant("Kuroda", 72.4);
418-
restaurantRepository.save(kuroda);
411+
}
419412

420-
List<Restaurant> results = restaurantRepository.findByNameStartingWith("San Francisco");
421-
assertNotNull(results);
422-
assertEquals(1, results.size());
423-
assertEquals("San Francisco International Airport (SFO)", results.get(0).getName());
413+
@Test // DATAGRAPH-1862
414+
public void shouldFindByNameStartingWithIgnoringCase() {
424415

416+
runTestThatShouldReturnOnlySFO(() -> restaurantRepository.findByNameStartingWithIgnoreCase("San Francisco".toLowerCase(Locale.ROOT)));
425417
}
426418

427419
@Test // DATAGRAPH-904
428420
public void shouldFindByNameEndingWith() {
429421

430-
Restaurant restaurant = new Restaurant("San Francisco International Airport (SFO)", 68.0);
431-
restaurantRepository.save(restaurant);
422+
runTestThatShouldReturnOnlySFO(() -> restaurantRepository.findByNameEndingWith("Airport (SFO)"));
423+
}
432424

433-
Restaurant kuroda = new Restaurant("Kuroda", 72.4);
434-
restaurantRepository.save(kuroda);
435-
436-
List<Restaurant> results = restaurantRepository.findByNameEndingWith("Airport (SFO)");
437-
assertNotNull(results);
438-
assertEquals(1, results.size());
439-
assertEquals("San Francisco International Airport (SFO)", results.get(0).getName());
425+
@Test // DATAGRAPH-1862
426+
public void shouldFindByNameEndingWithIgnoringCase() {
440427

428+
runTestThatShouldReturnOnlySFO(() -> restaurantRepository.findByNameEndingWithIgnoreCase("Airport (SFO)".toLowerCase(Locale.ROOT)));
441429
}
442430

443431
@Test // DATAGRAPH-904
@@ -510,17 +498,7 @@ public void shouldFindByNameIn() {
510498
@Test // DATAGRAPH-904
511499
public void shouldFindByNameMatchesRegEx() {
512500

513-
Restaurant restaurant = new Restaurant("San Francisco International Airport (SFO)", 68.0);
514-
restaurantRepository.save(restaurant);
515-
516-
Restaurant kuroda = new Restaurant("Kuroda", 72.4);
517-
restaurantRepository.save(kuroda);
518-
519-
List<Restaurant> results = restaurantRepository.findByNameMatchesRegex("(?i)san francisco.*");
520-
assertNotNull(results);
521-
assertEquals(1, results.size());
522-
assertEquals("San Francisco International Airport (SFO)", results.get(0).getName());
523-
501+
runTestThatShouldReturnOnlySFO(() -> restaurantRepository.findByNameMatchesRegex("(?i)san francisco.*"));
524502
}
525503

526504
@Test // DATAGRAPH-904

spring-data-neo4j/src/test/java/org/springframework/data/neo4j/examples/restaurants/repo/RestaurantRepository.java

+4
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,12 @@ public interface RestaurantRepository extends Neo4jRepository<Restaurant, Long>
6161

6262
List<Restaurant> findByNameStartingWith(String string);
6363

64+
List<Restaurant> findByNameStartingWithIgnoreCase(String string);
65+
6466
List<Restaurant> findByNameEndingWith(String string);
6567

68+
List<Restaurant> findByNameEndingWithIgnoreCase(String string);
69+
6670
List<Restaurant> findByNameContaining(String string);
6771

6872
List<Restaurant> findByNameNotContaining(String string);

0 commit comments

Comments
 (0)