Skip to content

Commit

Permalink
repo-sqale: fix of normalized poly query, normalizes provided value too
Browse files Browse the repository at this point in the history
This also fixes intest TestMisc.
  • Loading branch information
virgo47 committed Aug 13, 2021
1 parent 0624896 commit dee9c16
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 53 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2010-2020 Evolveum and contributors
* Copyright (C) 2010-2021 Evolveum and contributors
*
* This work is dual-licensed under the Apache License 2.0
* and European Union Public License. See LICENSE file for details.
Expand Down Expand Up @@ -95,7 +95,7 @@ public void test100GetRepositoryDiag() {
displayValue("Diag", diag);
assertSuccess(result);

assertEquals("Wrong implementationShortName", "SQL", diag.getImplementationShortName());
assertThat(diag.getImplementationShortName()).isIn("SQL", "SQaLe");
assertNotNull("Missing implementationDescription", diag.getImplementationDescription());
}

Expand Down
2 changes: 2 additions & 0 deletions model/model-intest/testng-integration-sqale.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ mvn integration-test -P sqale -pl model/model-intest/
<class name="com.evolveum.midpoint.model.intest.archetypes.TestCollections"/>
<!--
<class name="com.evolveum.midpoint.model.intest.archetypes.TestArchetypeInheritance"/>
-->
<class name="com.evolveum.midpoint.model.intest.misc.TestMisc"/>
<!--
<class name="com.evolveum.midpoint.model.intest.misc.TestTracing"/>
<class name="com.evolveum.midpoint.model.intest.misc.TestUuidNonUniqueName"/>
<class name="com.evolveum.midpoint.model.intest.misc.TestMigration"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.BooleanExpression;
import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.prism.*;
import com.evolveum.midpoint.prism.polystring.PolyString;
Expand All @@ -45,6 +44,7 @@
import com.evolveum.midpoint.repo.sqlbase.filtering.ValueFilterValues;
import com.evolveum.midpoint.repo.sqlbase.filtering.item.FilterOperation;
import com.evolveum.midpoint.repo.sqlbase.filtering.item.ItemValueFilterProcessor;
import com.evolveum.midpoint.repo.sqlbase.filtering.item.PolyStringItemFilterProcessor;
import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.QNameUtil;
Expand Down Expand Up @@ -300,30 +300,18 @@ private Predicate processPolyString(MExtItem extItem, ValueFilterValues<?, ?> va
// The value here should be poly-string, otherwise it never matches both orig and norm.
return processPolyStringBoth(extItem, values, operation);
} else if (ORIG.equals(matchingRule) || ORIG_IGNORE_CASE.equals(matchingRule)) {
return processPolyStringComponent(
extItem, convertPolyValuesToString(values, filter, p -> p.getOrig()),
return processPolyStringComponent(extItem,
ValueFilterValues.from(filter, PolyStringItemFilterProcessor::extractOrig),
JSONB_POLY_ORIG_KEY, operation);
} else if (NORM.equals(matchingRule) || NORM_IGNORE_CASE.equals(matchingRule)) {
return processPolyStringComponent(
extItem, convertPolyValuesToString(values, filter, p -> p.getNorm()),
extItem, ValueFilterValues.from(filter, this::extractNorm),
JSONB_POLY_NORM_KEY, operation);
} else {
throw new QueryException("Unknown matching rule '" + matchingRule + "'.");
}
}

@NotNull
private ValueFilterValues<?, ?> convertPolyValuesToString(ValueFilterValues<?, ?> values,
PropertyValueFilter<?> filter, Function<PolyString, String> extractor) {
// In case it is string already we don't need to do anything.
if (values.singleValueRaw() instanceof String) {
return values;
}

//noinspection unchecked
return ValueFilterValues.from((PropertyValueFilter<PolyString>) filter, extractor);
}

private Predicate processPolyStringBoth(
MExtItem extItem, ValueFilterValues<?, ?> values, FilterOperation operation) {
PolyString poly = (PolyString) values.singleValueRaw(); // must be Poly here
Expand Down Expand Up @@ -397,4 +385,8 @@ private BooleanExpression extItemIsNull(MExtItem extItem) {
return booleanTemplate("({0} ?? {1} AND {0} is not null)",
path, extItem.id.toString()).not();
}

private String extractNorm(Object value) {
return PolyStringItemFilterProcessor.extractNorm(value, context.prismContext());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ public void initObjects() throws Exception {

// region simple filters
@Test
public void test100SearchUserByName() throws Exception {
public void test100SearchAllObjectsWithEmptyFilter() throws Exception {
when("searching all objects with query without any conditions and paging");
OperationResult operationResult = createOperationResult();
SearchResultList<ObjectType> result = searchObjects(ObjectType.class,
Expand All @@ -398,18 +398,23 @@ public void test100SearchUserByName() throws Exception {

@Test
public void test110SearchUserByName() throws Exception {
when("searching user with query without any conditions and paging");
OperationResult operationResult = createOperationResult();
SearchResultList<UserType> result = searchObjects(UserType.class,
prismContext.queryFor(UserType.class)
.item(UserType.F_NAME).eq(PolyString.fromOrig("user-1"))
.build(),
operationResult);
searchUsersTest("with name matching provided value",
f -> f.item(UserType.F_NAME).eq(PolyString.fromOrig("user-1")),
user1Oid);
}

then("user with the matching name is returned");
assertThatOperationResult(operationResult).isSuccess();
assertThat(result).hasSize(1);
assertThat(result.get(0).getOid()).isEqualTo(user1Oid);
/**
* Couple of notes about matchingNorm/Orig:
*
* * String value can be provided instead of {@link PolyString} with norm/orig matcher.
* * String value is taken as-is for orig matching.
* * String value is normalized as configured for norm matching (as demonstrated in this test).
*/
@Test
public void test110SearchUserByNameNormalized() throws Exception {
searchUsersTest("with normalized name matching provided value",
f -> f.item(UserType.F_NAME).eq("UseR--2").matchingNorm(),
user2Oid);
}

@Test
Expand Down Expand Up @@ -1344,7 +1349,8 @@ public void test564SearchObjectWithExtensionPolyStringByNormValueAsString()
throws SchemaException {
searchUsersTest("with extension poly-string item norm equal to String value",
f -> f.item(UserType.F_EXTENSION, new QName("poly"))
.eq("polyvalue").matchingNorm(),
// casing of provided string is ignored
.eq("PolyValue").matchingNorm(),
user1Oid);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,34 @@
*/
package com.evolveum.midpoint.repo.sqlbase.filtering.item;

import java.util.List;
import java.util.function.Function;

import com.google.common.base.Strings;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.StringPath;
import org.jetbrains.annotations.NotNull;

import com.evolveum.midpoint.prism.PrismConstants;
import com.evolveum.midpoint.prism.PrismPropertyValue;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.polystring.PolyString;
import com.evolveum.midpoint.prism.query.PropertyValueFilter;
import com.evolveum.midpoint.prism.query.ValueFilter;
import com.evolveum.midpoint.repo.sqlbase.QueryException;
import com.evolveum.midpoint.repo.sqlbase.SqlQueryContext;
import com.evolveum.midpoint.repo.sqlbase.filtering.ValueFilterValues;
import com.evolveum.midpoint.repo.sqlbase.querydsl.FlexibleRelationalPathBase;
import com.evolveum.prism.xml.ns._public.types_3.PolyStringType;

/**
* Filter processor for a polystring attribute path (Prism item).
* This creates conditions to either {@code *_orig} or {@code *_norm} column depending on
* matching conditions.
* Sorting is always executed by {@code *_orig} column.
*
* @param <T> type of values in filter - PolyString, PolyStringType and String is supported
*/
public class PolyStringItemFilterProcessor
extends ItemValueFilterProcessor<PropertyValueFilter<PolyString>> {
public class PolyStringItemFilterProcessor<T>
extends ItemValueFilterProcessor<PropertyValueFilter<T>> {

public static final String STRICT = PrismConstants.POLY_STRING_STRICT_MATCHING_RULE_NAME.getLocalPart();
public static final String ORIG = PrismConstants.POLY_STRING_ORIG_MATCHING_RULE_NAME.getLocalPart();
Expand All @@ -57,40 +58,28 @@ public <Q extends FlexibleRelationalPathBase<R>, R> PolyStringItemFilterProcesso
}

@Override
public Predicate process(PropertyValueFilter<PolyString> filter) throws QueryException {
public Predicate process(PropertyValueFilter<T> filter) throws QueryException {
String matchingRule = filter.getMatchingRule() != null
? filter.getMatchingRule().getLocalPart() : null;

if (Strings.isNullOrEmpty(matchingRule) || DEFAULT.equals(matchingRule)
|| STRICT.equals(matchingRule) || STRICT_IGNORE_CASE.equals(matchingRule)) {
return ExpressionUtils.and(
createBinaryCondition(filter, normPath,
convertPolyValuesToString(filter, p -> p.getNorm())),
ValueFilterValues.from(filter, this::extractNorm)),
createBinaryCondition(filter, origPath,
convertPolyValuesToString(filter, p -> p.getOrig())));
ValueFilterValues.from(filter, PolyStringItemFilterProcessor::extractOrig)));
} else if (ORIG.equals(matchingRule) || ORIG_IGNORE_CASE.equals(matchingRule)) {
return createBinaryCondition(filter, origPath,
convertPolyValuesToString(filter, p -> p.getOrig()));
ValueFilterValues.from(filter, PolyStringItemFilterProcessor::extractOrig));
} else if (NORM.equals(matchingRule) || NORM_IGNORE_CASE.equals(matchingRule)) {
return createBinaryCondition(filter, normPath,
convertPolyValuesToString(filter, p -> p.getNorm()));
ValueFilterValues.from(filter, this::extractNorm));
} else {
throw new QueryException("Unknown matching rule '" + matchingRule + "'.");
}
}

@SuppressWarnings("unchecked")
@NotNull
private ValueFilterValues<?, String> convertPolyValuesToString(
PropertyValueFilter<?> filter, Function<PolyString, String> extractor) {
List<? extends PrismPropertyValue<?>> values = filter.getValues();
if (values != null && !values.isEmpty() && values.get(0).getRealValue() instanceof String) {
return ValueFilterValues.from((PropertyValueFilter<String>) filter, s -> s);
}

return ValueFilterValues.from((PropertyValueFilter<PolyString>) filter, extractor);
}

@Override
protected boolean isIgnoreCaseFilter(ValueFilter<?, ?> filter) {
String matchingRule = filter.getMatchingRule() != null
Expand All @@ -100,4 +89,39 @@ protected boolean isIgnoreCaseFilter(ValueFilter<?, ?> filter) {
|| ORIG_IGNORE_CASE.equals(matchingRule)
|| NORM_IGNORE_CASE.equals(matchingRule);
}

private String extractNorm(Object value) {
return extractNorm(value, context.prismContext());
}

/**
* Method extracting normalized value from (potentially poly-)string.
* May require adapter method to provide {@link PrismContext} for normalization, see usages.
*/
public static String extractNorm(Object value, PrismContext prismContext) {
if (value instanceof String) {
// we normalize the provided String value to ignore casing, etc.
return prismContext.getDefaultPolyStringNormalizer().normalize((String) value);
} else if (value instanceof PolyString) {
return ((PolyString) value).getNorm();
} else if (value instanceof PolyStringType) {
return ((PolyStringType) value).getNorm();
} else {
throw new IllegalArgumentException(
"Value [" + value + "] is neither String nor PolyString(Type).");
}
}

public static String extractOrig(Object value) {
if (value instanceof String) {
return (String) value;
} else if (value instanceof PolyString) {
return ((PolyString) value).getOrig();
} else if (value instanceof PolyStringType) {
return ((PolyStringType) value).getOrig();
} else {
throw new IllegalArgumentException(
"Value [" + value + "] is neither String nor PolyString(Type).");
}
}
}

0 comments on commit dee9c16

Please sign in to comment.