Browse Source

EDQS - added SQL like style for filter contains/starts with/ends with

pull/13603/head
Volodymyr Babak 1 year ago
committed by Andrii Shvaika
parent
commit
dd9d954c2f
  1. 2
      common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractEntityProfileNameQueryProcessor.java
  2. 2
      common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractEntityProfileQueryProcessor.java
  3. 2
      common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/EntityNameQueryProcessor.java
  4. 60
      common/edqs/src/main/java/org/thingsboard/server/edqs/util/RepositoryUtils.java
  5. 40
      edqs/src/test/java/org/thingsboard/server/edqs/repo/RepositoryUtilsTest.java

2
common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractEntityProfileNameQueryProcessor.java

@ -36,7 +36,7 @@ public abstract class AbstractEntityProfileNameQueryProcessor<T extends EntityFi
public AbstractEntityProfileNameQueryProcessor(TenantRepo repo, QueryContext ctx, EdqsQuery query, T filter, EntityType entityType) { public AbstractEntityProfileNameQueryProcessor(TenantRepo repo, QueryContext ctx, EdqsQuery query, T filter, EntityType entityType) {
super(repo, ctx, query, filter, entityType); super(repo, ctx, query, filter, entityType);
entityProfileNames = new HashSet<>(getProfileNames(this.filter)); entityProfileNames = new HashSet<>(getProfileNames(this.filter));
pattern = RepositoryUtils.toSqlLikePattern(getEntityNameFilter(filter)); pattern = RepositoryUtils.toContainsSqlLikePattern(getEntityNameFilter(filter));
} }
protected abstract String getEntityNameFilter(T filter); protected abstract String getEntityNameFilter(T filter);

2
common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/AbstractEntityProfileQueryProcessor.java

@ -43,7 +43,7 @@ public abstract class AbstractEntityProfileQueryProcessor<T extends EntityFilter
entityProfileIds.add(dp.getId()); entityProfileIds.add(dp.getId());
} }
} }
pattern = RepositoryUtils.toSqlLikePattern(getEntityNameFilter(filter)); pattern = RepositoryUtils.toContainsSqlLikePattern(getEntityNameFilter(filter));
} }
protected abstract String getEntityNameFilter(T filter); protected abstract String getEntityNameFilter(T filter);

2
common/edqs/src/main/java/org/thingsboard/server/edqs/query/processor/EntityNameQueryProcessor.java

@ -30,7 +30,7 @@ public class EntityNameQueryProcessor extends AbstractSimpleQueryProcessor<Entit
public EntityNameQueryProcessor(TenantRepo repo, QueryContext ctx, EdqsQuery query) { public EntityNameQueryProcessor(TenantRepo repo, QueryContext ctx, EdqsQuery query) {
super(repo, ctx, query, (EntityNameFilter) query.getEntityFilter(), ((EntityNameFilter) query.getEntityFilter()).getEntityType()); super(repo, ctx, query, (EntityNameFilter) query.getEntityFilter(), ((EntityNameFilter) query.getEntityFilter()).getEntityType());
pattern = RepositoryUtils.toSqlLikePattern(filter.getEntityNameFilter()); pattern = RepositoryUtils.toContainsSqlLikePattern(filter.getEntityNameFilter());
} }
@Override @Override

60
common/edqs/src/main/java/org/thingsboard/server/edqs/util/RepositoryUtils.java

@ -249,11 +249,11 @@ public class RepositoryUtils {
} }
return switch (predicate.getOperation()) { return switch (predicate.getOperation()) {
case EQUAL -> value.equals(predicateValue); case EQUAL -> value.equals(predicateValue);
case STARTS_WITH -> value.startsWith(predicateValue); case STARTS_WITH -> toStartsWithSqlLikePattern(predicateValue).matcher(value).matches();
case ENDS_WITH -> value.endsWith(predicateValue); case ENDS_WITH -> toEndsWithSqlLikePattern(predicateValue).matcher(value).matches();
case NOT_EQUAL -> !value.equals(predicateValue); case NOT_EQUAL -> !value.equals(predicateValue);
case CONTAINS -> value.contains(predicateValue); case CONTAINS -> toContainsSqlLikePattern(predicateValue).matcher(value).matches();
case NOT_CONTAINS -> !value.contains(predicateValue); case NOT_CONTAINS -> !toContainsSqlLikePattern(predicateValue).matcher(value).matches();
case IN -> equalsAny(value, splitByCommaWithoutQuotes(predicateValue)); case IN -> equalsAny(value, splitByCommaWithoutQuotes(predicateValue));
case NOT_IN -> !equalsAny(value, splitByCommaWithoutQuotes(predicateValue)); case NOT_IN -> !equalsAny(value, splitByCommaWithoutQuotes(predicateValue));
}; };
@ -304,6 +304,15 @@ public class RepositoryUtils {
return true; return true;
} else if (filterPredicates.getOperation() == OR) { } else if (filterPredicates.getOperation() == OR) {
for (KeyFilterPredicate filterPredicate : filterPredicates.getPredicates()) { 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)) { if (simpleKeyFilter.check(value, filterPredicate)) {
return true; return true;
} }
@ -314,23 +323,38 @@ public class RepositoryUtils {
} }
} }
public static Pattern toSqlLikePattern(String nameFilter) { public static Pattern toContainsSqlLikePattern(String filter) {
if (StringUtils.isNotBlank(nameFilter)) { if (StringUtils.isNotBlank(filter)) {
boolean percentSymbolOnStart = nameFilter.startsWith("%"); return toSqlLikePattern(filter, ".*", ".*");
boolean percentSymbolOnEnd = nameFilter.endsWith("%"); }
if (percentSymbolOnStart) { return null;
nameFilter = nameFilter.substring(1); }
}
if (percentSymbolOnEnd) { private static Pattern toStartsWithSqlLikePattern(String filter) {
nameFilter = nameFilter.substring(0, nameFilter.length() - 1); return toSqlLikePattern(filter, "^", ".*");
} }
if (percentSymbolOnStart || percentSymbolOnEnd) {
return Pattern.compile((percentSymbolOnStart ? ".*" : "") + Pattern.quote(nameFilter) + (percentSymbolOnEnd ? ".*" : ""), Pattern.CASE_INSENSITIVE); 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 { } else {
return Pattern.compile(Pattern.quote(nameFilter) + ".*", Pattern.CASE_INSENSITIVE); 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);
} }
return null;
} }
@FunctionalInterface @FunctionalInterface

40
edqs/src/test/java/org/thingsboard/server/edqs/repo/RepositoryUtilsTest.java

@ -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 123, loranet 124"), true),
Arguments.of("loranet 123", getNameFilter(StringOperation.IN, "loranet 125, loranet 126"), false), 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 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…
Cancel
Save