handle globlike patterns in in-expressions
This commit is contained in:
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user