Skip to content

Commit

Permalink
FMWK-153 Add support for "find by string like" (#542)
Browse files Browse the repository at this point in the history
  • Loading branch information
agrgr authored Apr 3, 2023
1 parent 4806384 commit e0c6335
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,21 @@ public Filter sIndexFilter(Map<String, Object> map) {
return null; // String secondary index does not support "contains" queries
}
},
LIKE {
@Override
public Exp filterExp(Map<String, Object> map) {
int flags = RegexFlag.EXTENDED;
if (ignoreCase(map)) {
flags = RegexFlag.EXTENDED | RegexFlag.ICASE;
}
return Exp.regexCompare(getValue1(map).toString(), flags, Exp.stringBin(getField(map)));
}

@Override
public Filter sIndexFilter(Map<String, Object> map) {
return null; // not supported
}
},
MAP_VALUE_EQ_BY_KEY {
@Override
public Exp filterExp(Map<String, Object> map) {
Expand Down Expand Up @@ -602,6 +617,24 @@ public Filter sIndexFilter(Map<String, Object> map) {
return null; // String secondary index does not support "contains" queries
}
},
MAP_VALUE_LIKE_BY_KEY {
@Override
public Exp filterExp(Map<String, Object> map) {
int flags = RegexFlag.EXTENDED;
if (ignoreCase(map)) {
flags = RegexFlag.EXTENDED | RegexFlag.ICASE;
}
return Exp.regexCompare(getValue1(map).toString(), flags,
MapExp.getByKey(MapReturnType.VALUE, Exp.Type.STRING, Exp.val(getValue2(map).toString()),
Exp.mapBin(getField(map)))
);
}

@Override
public Filter sIndexFilter(Map<String, Object> map) {
return null; // String secondary index does not support "contains" queries
}
},
MAP_VALUE_ENDS_WITH_BY_KEY {
@Override
public Exp filterExp(Map<String, Object> map) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,16 +239,12 @@ public static String escapeBRERegexp(String base) {
*/
private static String getRegexp(String base, FilterOperation op) {
String escapedBase = escapeBRERegexp(base);
if (op == FilterOperation.STARTS_WITH) {
return "^" + escapedBase;
}
if (op == FilterOperation.ENDS_WITH) {
return escapedBase + "$";
}
if (op == FilterOperation.EQ) {
return "^" + escapedBase + "$";
}
return escapedBase;
return switch (op) {
case STARTS_WITH -> "^" + escapedBase;
case ENDS_WITH -> escapedBase + "$";
case EQ -> "^" + escapedBase + "$";
default -> escapedBase;
};
}

public static String getStartsWith(String base) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ private AerospikeCriteria create(Part part, AerospikePersistentProperty property
v2 = parameters.next();
break;
case LIKE:
op = FilterOperation.LIKE;
break;
case STARTING_WITH:
op = FilterOperation.STARTS_WITH;
break;
Expand Down Expand Up @@ -186,6 +188,10 @@ private AerospikeCriteria create(Part part, AerospikePersistentProperty property
op = FilterOperation.MAP_VALUE_STARTS_WITH_BY_KEY;
setQbValuesForMapByKey(qb, v1, next);
break;
case LIKE:
op = FilterOperation.MAP_VALUE_LIKE_BY_KEY;
setQbValuesForMapByKey(qb, v1, next);
break;
case ENDS_WITH:
op = FilterOperation.MAP_VALUE_ENDS_WITH_BY_KEY;
setQbValuesForMapByKey(qb, v1, next);
Expand Down Expand Up @@ -227,7 +233,6 @@ private AerospikeCriteria create(Part part, AerospikePersistentProperty property
break;
case NOTEQ:
op = FilterOperation.MAP_VALUE_NOTEQ_BY_KEY;
qb.setDotPath(part.getProperty().toDotPath());
break;
case GT:
op = FilterOperation.MAP_VALUE_GT_BY_KEY;
Expand All @@ -248,6 +253,9 @@ private AerospikeCriteria create(Part part, AerospikePersistentProperty property
case STARTS_WITH:
op = FilterOperation.MAP_VALUE_STARTS_WITH_BY_KEY;
break;
case LIKE:
op = FilterOperation.MAP_VALUE_LIKE_BY_KEY;
break;
case ENDS_WITH:
op = FilterOperation.MAP_VALUE_ENDS_WITH_BY_KEY;
break;
Expand All @@ -260,7 +268,7 @@ private AerospikeCriteria create(Part part, AerospikePersistentProperty property
fieldName = part.getProperty().getSegment(); // POJO name, later passed to Exp.mapBin()
qb.setValue2(Value.get(property.getFieldName())); // VALUE2 contains key (field name)
qb.setDotPath(part.getProperty().toDotPath());
} else if (isPojo(part)) { // if it is a first level POJO or a Map
} else if (isPojo(part)) { // if it is a first level POJO
// if it is a POJO compared for equality it already has op == FilterOperation.EQ
fieldName = part.getProperty().getSegment(); // POJO name, later passed to Exp.mapBin()
qb.setValue2(Value.get(property.getFieldName())); // VALUE2 contains key (field name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,18 @@ void findByMapKeyValueStartsWith() {
assertThat(persons).contains(stefan, boyd);
}

@Test
void findByMapKeyValueLike() {
assertThat(stefan.getStringMap()).containsKey("key1");
assertThat(stefan.getStringMap()).containsValue("val1");
assertThat(boyd.getStringMap()).containsKey("key1");
assertThat(boyd.getStringMap()).containsValue("val1");

List<Person> persons = repository.findByStringMapLike("key1", "^.*al1$");

assertThat(persons).contains(stefan, boyd);
}

@Test
void findByMapKeyValueGreaterThan() {
assertThat(leroi.getIntMap()).containsKey("key2");
Expand Down Expand Up @@ -236,6 +248,21 @@ void findByFirstNameContaining() {
assertThat(persons).containsExactlyInAnyOrder(carter, oliver, leroi, leroi2);
}

@Test
void findByFirstNameLike() { // with a wildcard
List<Person> persons = repository.findByFirstNameLike("Ca.*er");
assertThat(persons).contains(carter);

List<Person> persons0 = repository.findByFirstNameLikeIgnoreCase("CART.*er");
assertThat(persons0).contains(carter);

List<Person> persons1 = repository.findByFirstNameLike(".*ve.*");
assertThat(persons1).contains(dave, oliver);

List<Person> persons2 = repository.findByFirstNameLike("Carr.*er");
assertThat(persons2).isEmpty();
}

@Test
void findByAddressZipCodeContaining() {
carter.setAddress(new Address("Foo Street 2", 2, "C10124", "C0123"));
Expand Down Expand Up @@ -633,18 +660,29 @@ public void findPersonsByFirstNameStartsWith() {

@Test
public void findPersonsByFriendFirstNameStartsWith() {
dave.setFriend(oliver);
repository.save(dave);
stefan.setFriend(oliver);
repository.save(stefan);
carter.setFriend(dave);
repository.save(carter);

List<Person> result = repository.findByFriendFirstNameStartsWith("D");

assertThat(result)
.hasSize(1)
.containsExactly(carter);

setFriendsToNull(dave, carter);
setFriendsToNull(stefan, carter);
}

@Test
public void findPersonsByFriendLastNameLike() {
oliver.setFriend(dave);
repository.save(oliver);
carter.setFriend(stefan);
repository.save(carter);

List<Person> result = repository.findByFriendLastNameLike(".*tthe.*");
assertThat(result).contains(oliver);
setFriendsToNull(oliver, carter);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class PersonTestData {
public static final Person donny = Person.builder().id(nextId()).firstName("Donny").lastName("Macintire").age(39)
.strings(Arrays.asList("str1", "str2", "str3")).build();
public static final Person oliver = Person.builder().id(nextId()).firstName("Oliver August").lastName("Matthews")
.age(4).build();
.age(14).build();
public static final Person carter = Person.builder().id(nextId()).firstName("Carter").lastName("Beauford").age(49)
.intMap(of("key1", 0, "key2", 1))
.address(new Address("Foo Street 2", 2, "C0124", "C0123")).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,15 @@ public interface PersonRepository<P extends Person> extends AerospikeRepository<

List<P> findByLastNameOrderByFirstNameDesc(String lastName);

List<P> findByFirstNameLike(String firstName);
/**
* Find all entities with firstName matching the given regex. POSIX Extended Regular Expression syntax is used to
* interpret the regex.
*
* @param firstNameRegex Regex to find matching firstName
*/
List<P> findByFirstNameLike(String firstNameRegex);

List<P> findByFirstNameLikeIgnoreCase(String firstNameRegex);

List<P> findByFirstNameLikeOrderByLastNameAsc(String firstName, Sort sort);

Expand Down Expand Up @@ -103,8 +111,8 @@ public interface PersonRepository<P extends Person> extends AerospikeRepository<
List<P> findByAddressIsNot(Address address);

/**
* Find all entities that satisfy the condition "have Address with fewer elements or with a corresponding
* key-value lower in ordering than in the given argument" (find by POJO).
* Find all entities that satisfy the condition "have Address with fewer elements or with a corresponding key-value
* lower in ordering than in the given argument" (find by POJO).
* <p>
* <a href="https://docs.aerospike.com/server/guide/data-types/cdt-ordering">Information about ordering</a>
*
Expand Down Expand Up @@ -232,8 +240,8 @@ public interface PersonRepository<P extends Person> extends AerospikeRepository<
List<P> findByStringMap(Map<String, String> map);

/**
* Find all entities that satisfy the condition "have stringMap with more elements or with a corresponding
* key-value higher in ordering than in the given argument" (find by POJO).
* Find all entities that satisfy the condition "have stringMap with more elements or with a corresponding key-value
* higher in ordering than in the given argument" (find by POJO).
* <p>
* <a href="https://docs.aerospike.com/server/guide/data-types/cdt-ordering">Information about ordering</a>
*
Expand Down Expand Up @@ -265,42 +273,53 @@ public interface PersonRepository<P extends Person> extends AerospikeRepository<
List<P> findByIntMapIsNot(String key, int value);

/**
* Find all entities that satisfy the condition "have the given map key and a value that starts with the given
* Find all entities that satisfy the condition "have the given map key and the value that starts with the given
* string"
*
* @param key Map key
* @param valueStartsWith String to check if value starts with it
* @param valueStartsWith String to check if map value starts with it
*/
List<P> findByStringMapStartsWith(String key, String valueStartsWith);

/**
* Find all entities that satisfy the condition "have the given map key and a value that contains the given string"
* Find all entities that satisfy the condition "have the given map key and the value matching the given regex"
* POSIX Extended Regular Expression syntax is used to interpret the regex.
*
* @param key Map key
* @param valueRegex Regex to find matching map value
*/
List<P> findByStringMapLike(String key, String valueRegex);

/**
* Find all entities that satisfy the condition "have the given map key and the value that contains the given
* string"
*
* @param key Map key
* @param valuePart String to check if value contains it
* @param valuePart String to check if map value contains it
*/
List<P> findByStringMapContaining(String key, String valuePart);

/**
* Find all entities that satisfy the condition "have the given map key and a value that is greater than the given
* Find all entities that satisfy the condition "have the given map key and the value that is greater than the given
* integer"
*
* @param key Map key
* @param greaterThan integer to check if value is greater than it
* @param greaterThan integer to check if map value is greater than it
*/
List<P> findByIntMapGreaterThan(String key, int greaterThan);

/**
* Find all entities that satisfy the condition "have the given map key and a value that is less than or equal to
* Find all entities that satisfy the condition "have the given map key and the value that is less than or equal to
* the given integer"
*
* @param key Map key
* @param lessThanOrEqualTo integer to check if value satisfies the condition
* @param lessThanOrEqualTo integer to check if map value satisfies the condition
*/
List<P> findByIntMapLessThanEqual(String key, int lessThanOrEqualTo);

/**
* Find all entities that satisfy the condition "have the given map key and a value in between the given integers"
* Find all entities that satisfy the condition "have the given map key and the value in between the given
* integers"
*
* @param key Map key
* @param from the lower limit for the map value, inclusive
Expand Down Expand Up @@ -490,5 +509,13 @@ public interface PersonRepository<P extends Person> extends AerospikeRepository<

List<P> findByFriendFirstNameStartsWith(String string);

/**
* Find all entities that satisfy the condition "have a friend with lastName matching the giving regex". POSIX
* Extended Regular Expression syntax is used to interpret the regex.
*
* @param lastNameRegex Regex to find matching lastName
*/
List<P> findByFriendLastNameLike(String lastNameRegex);

Iterable<P> findByAgeBetweenOrderByLastName(int i, int j);
}

0 comments on commit e0c6335

Please sign in to comment.