Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FMWK-153 Add support for "find by string like" #542

Merged
merged 6 commits into from
Apr 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}