handle globlike patterns in in-expressions

This commit is contained in:
2019-08-31 17:34:17 +02:00
parent f8e859fb6d
commit d8a114dbaf
5 changed files with 98 additions and 21 deletions

View File

@@ -0,0 +1,24 @@
package org.lucares.pdb.datastore.internal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.lucares.pdb.datastore.lang.GloblikePattern;
public class GlobMatcher {
private final Pattern pattern;
public GlobMatcher(final String globlike) {
pattern = GloblikePattern.globlikeToRegex(globlike);
}
public GlobMatcher(final Iterable<String> globlikes) {
pattern = GloblikePattern.globlikeToRegex(globlikes);
}
public boolean matches(final String s) {
final Matcher matcher = pattern.matcher(s);
return matcher.find();
}
}

View File

@@ -337,8 +337,8 @@ public class QueryCompletionIndex implements AutoCloseable {
} }
/** /**
* Find values that are yield results when executing the query "fieldA=valueA * Find values for fieldB that are yield results when executing the query
* and fieldB=???" * "fieldA=valueA and fieldB=???"
* *
* @param dateRange the date range * @param dateRange the date range
* @param fieldA the other field of the and expression * @param fieldA the other field of the and expression
@@ -354,8 +354,28 @@ public class QueryCompletionIndex implements AutoCloseable {
return find(dateRange, tag, fieldB); return find(dateRange, tag, fieldB);
} }
public SortedSet<String> find(final DateTimeRange dateRange, final String fieldA, final GlobMatcher valueA,
final String fieldB) {
final SortedSet<String> result = new TreeSet<>();
final TwoTags keyPrefix = new TwoTags(fieldB, fieldA, null, null);
final PartitionIdSource partitionIdSource = new DatePartitioner(dateRange);
tagToTagIndex.visitValues(partitionIdSource, keyPrefix, (k, v) -> {
final String vA = k.getTagA().getValueAsString();
if (valueA.matches(vA)) {
result.add(k.getTagB().getValueAsString());
}
});
return result;
}
/** /**
* Find values that are yield results when executing the query * Find values for fieldB that are yield results when executing the query
* "tag.field=tag.value and fieldB=???" * "tag.field=tag.value and fieldB=???"
* *
* @param dateRange the date range * @param dateRange the date range

View File

@@ -7,6 +7,7 @@ import java.util.TreeSet;
import org.lucares.pdb.api.DateTimeRange; import org.lucares.pdb.api.DateTimeRange;
import org.lucares.pdb.api.Tag; import org.lucares.pdb.api.Tag;
import org.lucares.pdb.datastore.internal.GlobMatcher;
import org.lucares.pdb.datastore.internal.QueryCompletionIndex; import org.lucares.pdb.datastore.internal.QueryCompletionIndex;
import org.lucares.pdb.datastore.lang.Expression.And; import org.lucares.pdb.datastore.lang.Expression.And;
import org.lucares.pdb.datastore.lang.Expression.AndCaretExpression; import org.lucares.pdb.datastore.lang.Expression.AndCaretExpression;
@@ -65,13 +66,12 @@ public class FindValuesForQueryCompletion extends ExpressionVisitor<SortedSet<St
@Override @Override
public SortedSet<String> visit(final InExpression expression) { public SortedSet<String> visit(final InExpression expression) {
final long start = System.nanoTime(); final long start = System.nanoTime();
final SortedSet<String> result = new TreeSet<>(); final SortedSet<String> result;
final String property = expression.getProperty(); final String fieldA = expression.getProperty();
final List<String> values = expression.getValues(); final List<String> values = expression.getValues();
for (final String value : values) {
final SortedSet<String> candidates = index.find(dateTimeRange, property, value, field); result = index.find(dateTimeRange, fieldA, new GlobMatcher(values), field);
result.addAll(candidates);
}
METRIC_AND_CARET_LOGGER.debug("{}: {}ms", expression, (System.nanoTime() - start) / 1_000_000.0); METRIC_AND_CARET_LOGGER.debug("{}: {}ms", expression, (System.nanoTime() - start) / 1_000_000.0);
return result; return result;
} }

View File

@@ -1,6 +1,8 @@
package org.lucares.pdb.datastore.lang; package org.lucares.pdb.datastore.lang;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -16,21 +18,42 @@ public class GloblikePattern {
KEEP_EQUAL KEEP_EQUAL
} }
static Pattern globlikeToRegex(final String globPattern) { public static Pattern globlikeToRegex(final String globlike) {
final String valueRegex = "^" + globlikeToPattern(globlike);
LOGGER.trace(">{}< -> >{}<", globlike, valueRegex);
return Pattern.compile(valueRegex);
}
public static Pattern globlikeToRegex(final Iterable<String> globlikes) {
final List<String> regex = new ArrayList<>();
for (final String globlike : globlikes) {
regex.add(globlikeToPattern(globlike));
}
final StringBuilder fullRegex = new StringBuilder("^(");
fullRegex.append(String.join("|", regex));
fullRegex.append(")");
LOGGER.trace(">{}< -> >{}<", globlikes, fullRegex);
return Pattern.compile(fullRegex.toString());
}
private static String globlikeToPattern(final String globlike) {
// a character that cannot be in the globPattern // a character that cannot be in the globPattern
final String dotPlaceholder = "\ue003"; // fourth character in the private use area final String dotPlaceholder = "\ue003"; // fourth character in the private use area
final String valueRegex = "^" + // final String valueRegex = globlike//
globPattern// .replace("-", Pattern.quote("-"))//
.replace("-", Pattern.quote("-"))// .replace(".", dotPlaceholder)//
.replace(".", dotPlaceholder)// .replace("*", ".*")//
.replace("*", ".*")// .replace(dotPlaceholder, ".*\\.")//
.replace(dotPlaceholder, ".*\\.")// .replaceAll("([A-Z])", "[a-z]*$1");
.replaceAll("([A-Z])", "[a-z]*$1"); return valueRegex;
LOGGER.trace(">{}< -> >{}<", globPattern, valueRegex);
return Pattern.compile(valueRegex);
} }
public static <T extends Collection<String>> T filterValues(final Collection<String> availableValues, public static <T extends Collection<String>> T filterValues(final Collection<String> availableValues,
@@ -54,4 +77,5 @@ public class GloblikePattern {
return result; return result;
} }
} }

View File

@@ -218,6 +218,15 @@ public class ProposerTest {
proposal("Tim", "name=Tim", "name=Tim|"), // proposal("Tim", "name=Tim", "name=Tim|"), //
proposal("Timothy", "name=Timothy", "name=Timothy|")// proposal("Timothy", "name=Timothy", "name=Timothy|")//
); );
assertProposals("(method=FooService.doIS,FooController.*) and method=|", ResultMode.FULL_VALUES, //
proposal("FooService.doImportantStuff",
"(method=FooService.doIS,FooController.*) and method=FooService.doImportantStuff",
"(method=FooService.doIS,FooController.*) and method=FooService.doImportantStuff|"), //
proposal("FooController.doImportantStuff",
"(method=FooService.doIS,FooController.*) and method=FooController.doImportantStuff",
"(method=FooService.doIS,FooController.*) and method=FooController.doImportantStuff|")//
);
} }
public void testProposalWithAndExpression() throws Exception { public void testProposalWithAndExpression() throws Exception {