Skip to content

Commit

Permalink
Add support for NOT_IN, NOT_LIKE and NEGATING_SIMPLE_PROPERTY
Browse files Browse the repository at this point in the history
  • Loading branch information
TomVanWemmel committed Nov 12, 2024
1 parent 63f2065 commit 01e8201
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
* {@link AbstractQueryCreator} to create {@link Predicate}-based {@link KeyValueQuery}s.
*
* @author Christoph Strobl
* @author Tom Van Wemmel
* @since 3.3
*/
public class PredicateQueryCreator extends AbstractQueryCreator<KeyValueQuery<Predicate<?>>, Predicate<?>> {
Expand All @@ -59,12 +60,16 @@ protected Predicate<?> create(Part part, Iterator<Object> iterator) {
return PredicateBuilder.propertyValueOf(part).isFalse();
case SIMPLE_PROPERTY:
return PredicateBuilder.propertyValueOf(part).isEqualTo(iterator.next());
case NEGATING_SIMPLE_PROPERTY:
return PredicateBuilder.propertyValueOf(part).isEqualTo(iterator.next()).negate();
case IS_NULL:
return PredicateBuilder.propertyValueOf(part).isNull();
case IS_NOT_NULL:
return PredicateBuilder.propertyValueOf(part).isNotNull();
case LIKE:
return PredicateBuilder.propertyValueOf(part).contains(iterator.next());
case NOT_LIKE:
return PredicateBuilder.propertyValueOf(part).contains(iterator.next()).negate();
case STARTING_WITH:
return PredicateBuilder.propertyValueOf(part).startsWith(iterator.next());
case AFTER:
Expand All @@ -86,6 +91,8 @@ protected Predicate<?> create(Part part, Iterator<Object> iterator) {
return PredicateBuilder.propertyValueOf(part).matches(iterator.next());
case IN:
return PredicateBuilder.propertyValueOf(part).in(iterator.next());
case NOT_IN:
return PredicateBuilder.propertyValueOf(part).in(iterator.next()).negate();
default:
throw new InvalidDataAccessApiUsageException(String.format("Found invalid part '%s' in query", part.getType()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
* @author Christoph Strobl
* @author Oliver Gierke
* @author Mark Paluch
* @author Tom Van Wemmel
*/
public class SpelQueryCreator extends AbstractQueryCreator<KeyValueQuery<SpelExpression>, String> {

Expand Down Expand Up @@ -120,6 +121,9 @@ protected SpelExpression toPredicateExpression(PartTree tree) {
case SIMPLE_PROPERTY:
partBuilder.append("?.equals(").append("[").append(parameterIndex++).append("])");
break;
case NEGATING_SIMPLE_PROPERTY:
partBuilder.append("?.equals(").append("[").append(parameterIndex++).append("]) == false");
break;
case IS_NULL:
partBuilder.append(" == null");
break;
Expand All @@ -129,6 +133,9 @@ protected SpelExpression toPredicateExpression(PartTree tree) {
case LIKE:
partBuilder.append("?.contains(").append("[").append(parameterIndex++).append("])");
break;
case NOT_LIKE:
partBuilder.append("?.contains(").append("[").append(parameterIndex++).append("]) == false");
break;
case STARTING_WITH:
partBuilder.append("?.startsWith(").append("[").append(parameterIndex++).append("])");
break;
Expand Down Expand Up @@ -175,9 +182,16 @@ protected SpelExpression toPredicateExpression(PartTree tree) {
partBuilder.append(")");
break;

case NOT_IN:

partBuilder.append("[").append(parameterIndex++).append("].contains(");
partBuilder.append("#it?.");
partBuilder.append(part.getProperty().toDotPath().replace(".", "?."));
partBuilder.append(") == false");
break;

case CONTAINING:
case NOT_CONTAINING:
case NEGATING_SIMPLE_PROPERTY:
case EXISTS:
default:
throw new InvalidDataAccessApiUsageException(
Expand Down Expand Up @@ -206,6 +220,6 @@ protected SpelExpression toPredicateExpression(PartTree tree) {
}

private static boolean requiresInverseLookup(Part part) {
return part.getType() == Type.IN;
return part.getType() == Type.IN || part.getType() == Type.NOT_IN;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

/**
* @author Christoph Strobl
* @author Tom Van Wemmel
*/
@ExtendWith(MockitoExtension.class)
public abstract class AbstractQueryCreatorTestBase<QUERY_CREATOR extends AbstractQueryCreator<KeyValueQuery<CRITERIA>, ?>, CRITERIA> {
Expand Down Expand Up @@ -70,6 +71,17 @@ void equalsReturnsFalseWhenNotMatching() {
assertThat(evaluate("findByFirstname", BRAN.firstname).against(RICKON)).isFalse();
}

@Test
// GH-603
void notEqualsReturnsTrueWhenMatching() {
assertThat(evaluate("findByFirstnameNot", BRAN.firstname).against(RICKON)).isTrue();
}

@Test // GH-603
void notEqualsReturnsFalseWhenNotMatching() {
assertThat(evaluate("findByFirstnameNot", BRAN.firstname).against(BRAN)).isFalse();
}

@Test // DATACMNS-525
void isTrueAssertedProperlyWhenTrue() {
assertThat(evaluate("findBySkinChangerIsTrue").against(BRAN)).isTrue();
Expand Down Expand Up @@ -130,6 +142,16 @@ void likeReturnsFalseWhenNotMatching() {
assertThat(evaluate("findByFirstnameLike", "ra").against(ROBB)).isFalse();
}

@Test // GH-603
void notLikeReturnsTrueWhenMatching() {
assertThat(evaluate("findByFirstnameNotLike", "ra").against(ROBB)).isTrue();
}

@Test // GH-603
void notLikeReturnsFalseWhenNotMatching() {
assertThat(evaluate("findByFirstnameNotLike", "ob").against(ROBB)).isFalse();
}

@Test // DATACMNS-525
void endsWithReturnsTrueWhenMatching() {
assertThat(evaluate("findByFirstnameEndingWith", "bb").against(ROBB)).isTrue();
Expand Down Expand Up @@ -310,6 +332,53 @@ void inMatchesNullValuesCorrectly() {
.isTrue();
}

@Test // GH-603
void notInReturnsMatchCorrectly() {

ArrayList<String> list = new ArrayList<>();
list.add(ROBB.firstname);

assertThat(evaluate("findByFirstnameNotIn", list).against(JON)).isTrue();
}

@Test // GH-603
void notInNotMatchingReturnsCorrectly() {

ArrayList<String> list = new ArrayList<>();
list.add(ROBB.firstname);

assertThat(evaluate("findByFirstnameNotIn", list).against(ROBB)).isFalse();
}

@Test // GH-603
void notInWithNullCompareValuesCorrectly() {

ArrayList<String> list = new ArrayList<>();
list.add(null);

assertThat(evaluate("findByFirstnameNotIn", list).against(JON)).isTrue();
}

@Test // GH-603
void notInWithNullSourceValuesMatchesCorrectly() {

ArrayList<String> list = new ArrayList<>();
list.add(ROBB.firstname);

assertThat(evaluate("findByFirstnameNotIn", list).against(new PredicateQueryCreatorUnitTests.Person(null, 10)))
.isTrue();
}

@Test // GH-603
void notInMatchesNullValuesCorrectly() {

ArrayList<String> list = new ArrayList<>();
list.add(null);

assertThat(evaluate("findByFirstnameNotIn", list).against(new PredicateQueryCreatorUnitTests.Person(null, 10)))
.isFalse();
}

@Test // DATAKV-185
void noDerivedQueryArgumentsMatchesAlways() {

Expand Down Expand Up @@ -363,6 +432,9 @@ interface PersonRepository extends CrudRepository<Person, String> {
// Type.SIMPLE_PROPERTY
Person findByFirstname(String firstname);

// Type.NEGATING_SIMPLE_PROPERTY
Person findByFirstnameNot(String firstname);

// Type.TRUE
Person findBySkinChangerIsTrue();

Expand Down Expand Up @@ -404,6 +476,9 @@ interface PersonRepository extends CrudRepository<Person, String> {
// Type.LIKE
Person findByFirstnameLike(String firstname);

// Type.NOT_LIKE
Person findByFirstnameNotLike(String firstname);

// Type.ENDING_WITH
Person findByFirstnameEndingWith(String firstname);

Expand All @@ -417,6 +492,9 @@ interface PersonRepository extends CrudRepository<Person, String> {
// Type.IN
Person findByFirstnameIn(ArrayList<String> in);

// Type.NOT_IN
Person findByFirstnameNotIn(ArrayList<String> in);

}

public interface Evaluation {
Expand Down

0 comments on commit 01e8201

Please sign in to comment.