Skip to content

Commit

Permalink
Added FuzzyStringMatch to ItemValueFilterProcessor
Browse files Browse the repository at this point in the history
Signed-off-by: Tony Tkacik <tonydamage@gmail.com>
  • Loading branch information
tonydamage committed Jul 27, 2022
1 parent 4df5237 commit 45e00d5
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -565,12 +565,15 @@ protected void displayQuery(@Nullable ObjectQuery query) throws SchemaException
if (query == null) {
return;
}

String serializedQuery = null;
try {
QueryType queryType = prismContext.getQueryConverter().createQueryType(query);
String serializedQuery = prismContext.xmlSerializer().serializeAnyData(
serializedQuery = prismContext.xmlSerializer().serializeAnyData(
queryType, SchemaConstants.MODEL_EXTENSION_OBJECT_QUERY);
display("Serialized QUERY: " + serializedQuery);

} catch (Exception e) {
display("Can not serialize query");
}
if (query.getFilter() != null) {
try {
PrismQuerySerialization serialization = prismContext.querySerializer().serialize(query.getFilter());
Expand All @@ -581,8 +584,10 @@ protected void displayQuery(@Nullable ObjectQuery query) throws SchemaException
}

// sanity check if it's re-parsable
assertThat(prismContext.parserFor(serializedQuery).parseRealValue(QueryType.class))
if (serializedQuery != null) {
assertThat(prismContext.parserFor(serializedQuery).parseRealValue(QueryType.class))
.isNotNull();
}
}

/** Parses object from byte array form and returns its real value (not Prism structure). */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,24 @@
import java.util.Collection;
import java.util.List;

import javax.xml.namespace.QName;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import com.evolveum.midpoint.prism.Containerable;
import com.evolveum.midpoint.prism.PrismConstants;
import com.evolveum.midpoint.prism.PrismContainerValue;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.path.ItemName;
import com.evolveum.midpoint.prism.query.ObjectQuery;
import com.evolveum.midpoint.repo.sqale.SqaleRepoBaseTest;
import com.evolveum.midpoint.schema.GetOperationOptions;
import com.evolveum.midpoint.schema.SchemaService;
import com.evolveum.midpoint.schema.SelectorOptions;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.exception.CommonException;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;

Expand Down Expand Up @@ -105,5 +112,25 @@ public void test115GetUserWithRetrieveOptions() throws CommonException {
.containsExactlyInAnyOrder("givenName", "familyName", "familyName.3", "dateOfBirth", "nationalId");
}


@Test
public void testSeachUsingFuzzyMatching() throws CommonException {
when("user is obtained with retrieve options for identities");
Collection<SelectorOptions<GetOperationOptions>> getOptions = SchemaService.get()
.getOperationOptionsBuilder().item(FocusType.F_IDENTITIES).retrieve().build();
OperationResult result = createOperationResult();
ItemName familyNameQName = new ItemName(SchemaConstants.NS_MIDPOINT_PUBLIC_COMMON, "familyName");
var def = PrismContext.get().definitionFactory().createPropertyDefinition(familyNameQName, DOMUtil.XSD_STRING, null, null);
def.toMutable().setRuntimeSchema(true);

ObjectQuery query = PrismContext.get().queryFor(UserType.class).itemWithDef(def, UserType.F_IDENTITIES,
FocusIdentitiesType.F_IDENTITY,
FocusIdentityType.F_ITEMS,
FocusIdentityItemsType.F_NORMALIZED,
new ItemName(SchemaConstants.NS_MIDPOINT_PUBLIC_COMMON, "familyName")
).eq("alice").build();
var ret = repositoryService.searchObjects(UserType.class, query, null, result);
assertThat(ret.size()).isEqualTo(1);
}
// TODO modification test + hopefully updateGetOptions in QFocusMapping does the trick
}
Original file line number Diff line number Diff line change
Expand Up @@ -2762,5 +2762,12 @@ public void test991SearchObjectWithStringIgnoreCaseWithoutNamespace()
.matching(new QName(STRING_IGNORE_CASE_MATCHING_RULE_NAME.getLocalPart())),
user1Oid);
}

@Test
public void fuzzyStringSearchTest() throws SchemaException {
searchUsersTest("With levelstein",
f -> f.item(UserType.F_EMPLOYEE_NUMBER).fuzzyString("User1").levenshtein(2, true),
user1Oid);
}
// endregion
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@
import static com.evolveum.midpoint.prism.PrismConstants.STRING_IGNORE_CASE_MATCHING_RULE_NAME;

import com.querydsl.core.types.*;
import com.querydsl.core.types.dsl.Expressions;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import com.evolveum.midpoint.prism.PrismValue;
import com.evolveum.midpoint.prism.query.*;
import com.evolveum.midpoint.prism.query.FuzzyStringMatchFilter.FuzzyMatchingMethod;
import com.evolveum.midpoint.prism.query.FuzzyStringMatchFilter.Levenshtein;
import com.evolveum.midpoint.repo.sqlbase.QueryException;
import com.evolveum.midpoint.repo.sqlbase.RepositoryException;
import com.evolveum.midpoint.repo.sqlbase.SqlQueryContext;
Expand Down Expand Up @@ -87,6 +91,10 @@ protected boolean isIgnoreCaseFilter(ValueFilter<?, ?> filter) {
protected <T> Predicate createBinaryCondition(
ValueFilter<?, ?> filter, Path<T> path, ValueFilterValues<?, T> values)
throws QueryException {
if (filter instanceof FuzzyStringMatchFilter<?>) {
return fuzzyStringPredicate((FuzzyStringMatchFilter<?>) filter, path, values);
}

FilterOperation operation = operation(filter);
if (values.isEmpty()) {
if (operation.isAnyEqualOperation()) {
Expand All @@ -108,6 +116,16 @@ protected <T> Predicate createBinaryCondition(
return singleValuePredicateWithNotTreated(path, operation, values.singleValue());
}

private <T> Predicate fuzzyStringPredicate(FuzzyStringMatchFilter<?> filter, Path<T> path,
ValueFilterValues<?, T> values) throws QueryException {
FuzzyMatchingMethod method = filter.getMatchingMethod();
if (method instanceof Levenshtein) {
var levenstein = (Levenshtein) method;
return Expressions.booleanTemplate("levenshtein_less_equal({0}, '{1s}', {2} ) <= {2}", path, values.singleValue().toString(), levenstein.getThreshold());
}
throw new QueryException("Unsupported filter " + filter.toString());
}

/**
* Creates predicate for specified path and value using the provided operator.
* If the value is not Querydsl {@link Expression} it is changed to constant expression,
Expand Down

0 comments on commit 45e00d5

Please sign in to comment.