EDQS - added SQL like style for filter contains/starts with/ends with
This commit is contained in:
parent
2bd7b4d01d
commit
dd9d954c2f
@ -36,7 +36,7 @@ public abstract class AbstractEntityProfileNameQueryProcessor<T extends EntityFi
|
||||
public AbstractEntityProfileNameQueryProcessor(TenantRepo repo, QueryContext ctx, EdqsQuery query, T filter, EntityType entityType) {
|
||||
super(repo, ctx, query, filter, entityType);
|
||||
entityProfileNames = new HashSet<>(getProfileNames(this.filter));
|
||||
pattern = RepositoryUtils.toSqlLikePattern(getEntityNameFilter(filter));
|
||||
pattern = RepositoryUtils.toContainsSqlLikePattern(getEntityNameFilter(filter));
|
||||
}
|
||||
|
||||
protected abstract String getEntityNameFilter(T filter);
|
||||
|
||||
@ -43,7 +43,7 @@ public abstract class AbstractEntityProfileQueryProcessor<T extends EntityFilter
|
||||
entityProfileIds.add(dp.getId());
|
||||
}
|
||||
}
|
||||
pattern = RepositoryUtils.toSqlLikePattern(getEntityNameFilter(filter));
|
||||
pattern = RepositoryUtils.toContainsSqlLikePattern(getEntityNameFilter(filter));
|
||||
}
|
||||
|
||||
protected abstract String getEntityNameFilter(T filter);
|
||||
|
||||
@ -30,7 +30,7 @@ public class EntityNameQueryProcessor extends AbstractSimpleQueryProcessor<Entit
|
||||
|
||||
public EntityNameQueryProcessor(TenantRepo repo, QueryContext ctx, EdqsQuery query) {
|
||||
super(repo, ctx, query, (EntityNameFilter) query.getEntityFilter(), ((EntityNameFilter) query.getEntityFilter()).getEntityType());
|
||||
pattern = RepositoryUtils.toSqlLikePattern(filter.getEntityNameFilter());
|
||||
pattern = RepositoryUtils.toContainsSqlLikePattern(filter.getEntityNameFilter());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -249,11 +249,11 @@ public class RepositoryUtils {
|
||||
}
|
||||
return switch (predicate.getOperation()) {
|
||||
case EQUAL -> value.equals(predicateValue);
|
||||
case STARTS_WITH -> value.startsWith(predicateValue);
|
||||
case ENDS_WITH -> value.endsWith(predicateValue);
|
||||
case STARTS_WITH -> toStartsWithSqlLikePattern(predicateValue).matcher(value).matches();
|
||||
case ENDS_WITH -> toEndsWithSqlLikePattern(predicateValue).matcher(value).matches();
|
||||
case NOT_EQUAL -> !value.equals(predicateValue);
|
||||
case CONTAINS -> value.contains(predicateValue);
|
||||
case NOT_CONTAINS -> !value.contains(predicateValue);
|
||||
case CONTAINS -> toContainsSqlLikePattern(predicateValue).matcher(value).matches();
|
||||
case NOT_CONTAINS -> !toContainsSqlLikePattern(predicateValue).matcher(value).matches();
|
||||
case IN -> equalsAny(value, splitByCommaWithoutQuotes(predicateValue));
|
||||
case NOT_IN -> !equalsAny(value, splitByCommaWithoutQuotes(predicateValue));
|
||||
};
|
||||
@ -304,6 +304,15 @@ public class RepositoryUtils {
|
||||
return true;
|
||||
} else if (filterPredicates.getOperation() == OR) {
|
||||
for (KeyFilterPredicate filterPredicate : filterPredicates.getPredicates()) {
|
||||
|
||||
// Emulate the SQL-like behavior of ThingsBoard's Entity Data Query service:
|
||||
// for COMPLEX filters, return no results if filter value is empty
|
||||
if (filterPredicate instanceof StringFilterPredicate stringFilterPredicate) {
|
||||
if (StringUtils.isEmpty(stringFilterPredicate.getValue().getValue())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (simpleKeyFilter.check(value, filterPredicate)) {
|
||||
return true;
|
||||
}
|
||||
@ -314,25 +323,40 @@ public class RepositoryUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static Pattern toSqlLikePattern(String nameFilter) {
|
||||
if (StringUtils.isNotBlank(nameFilter)) {
|
||||
boolean percentSymbolOnStart = nameFilter.startsWith("%");
|
||||
boolean percentSymbolOnEnd = nameFilter.endsWith("%");
|
||||
if (percentSymbolOnStart) {
|
||||
nameFilter = nameFilter.substring(1);
|
||||
}
|
||||
if (percentSymbolOnEnd) {
|
||||
nameFilter = nameFilter.substring(0, nameFilter.length() - 1);
|
||||
}
|
||||
if (percentSymbolOnStart || percentSymbolOnEnd) {
|
||||
return Pattern.compile((percentSymbolOnStart ? ".*" : "") + Pattern.quote(nameFilter) + (percentSymbolOnEnd ? ".*" : ""), Pattern.CASE_INSENSITIVE);
|
||||
} else {
|
||||
return Pattern.compile(Pattern.quote(nameFilter) + ".*", Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
public static Pattern toContainsSqlLikePattern(String filter) {
|
||||
if (StringUtils.isNotBlank(filter)) {
|
||||
return toSqlLikePattern(filter, ".*", ".*");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Pattern toStartsWithSqlLikePattern(String filter) {
|
||||
return toSqlLikePattern(filter, "^", ".*");
|
||||
}
|
||||
|
||||
private static Pattern toEndsWithSqlLikePattern(String filter) {
|
||||
return toSqlLikePattern(filter, ".*", "$");
|
||||
}
|
||||
|
||||
private static Pattern toSqlLikePattern(String value, String prefix, String suffix ) {
|
||||
if (value.contains("%") || value.contains("_")) {
|
||||
String regexValue = value
|
||||
.replace("_", ".")
|
||||
.replace("%", ".*");
|
||||
String regex;
|
||||
if ("^".equals(prefix)) {
|
||||
regex = "^" + regexValue + (regexValue.endsWith(".*") ? "" : ".*");
|
||||
} else if ("$".equals(suffix)) {
|
||||
regex = (regexValue.startsWith(".*") ? "" : ".*") + regexValue + "$";
|
||||
} else {
|
||||
regex = (regexValue.startsWith(".*") ? "" : ".*") + regexValue + (regexValue.endsWith(".*") ? "" : ".*");
|
||||
}
|
||||
return Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
|
||||
} else {
|
||||
return Pattern.compile(prefix + Pattern.quote(value) + suffix, Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SimpleKeyFilter<T> {
|
||||
|
||||
|
||||
@ -70,7 +70,45 @@ public class RepositoryUtilsTest {
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.IN, "loranet 123, loranet 124"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.IN, "loranet 125, loranet 126"), false),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.NOT_IN, "loranet 125, loranet 126"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.NOT_IN, "loranet 123, loranet 126"), false)
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.NOT_IN, "loranet 123, loranet 126"), false),
|
||||
|
||||
// Basic CONTAINS
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "%loranet"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "loranet%"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "%ranet%"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "%123"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "%loranx%"), false),
|
||||
|
||||
// Basic STARTS_WITH
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.STARTS_WITH, "loranet%"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.STARTS_WITH, "lora%"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.STARTS_WITH, "lorax%"), false),
|
||||
|
||||
// Basic ENDS_WITH
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.ENDS_WITH, "%123"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.ENDS_WITH, "%23"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.ENDS_WITH, "%124"), false),
|
||||
|
||||
// CONTAINS with _
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "loranet_123"), true), // '_' = ' '
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "loranet_12_"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "loran_t%"), true),
|
||||
|
||||
// STARTS_WITH with _
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.STARTS_WITH, "loranet_"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.STARTS_WITH, "lora__t%"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.STARTS_WITH, "lor_net%"), true),
|
||||
|
||||
// ENDS_WITH with _
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.ENDS_WITH, "_23"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.ENDS_WITH, "_2_"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.ENDS_WITH, "_3"), true),
|
||||
|
||||
// Mixed patterns
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "lora__t 1%"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "lora%net%3"), true),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "%o_anet%2_3"), false),
|
||||
Arguments.of("loranet 123", getNameFilter(StringOperation.CONTAINS, "lora___ ___"), true)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user