Refactor RSQL searach fields related classes (3) (#1836)

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2024-09-10 11:38:31 +03:00
committed by GitHub
parent 368c18ed5e
commit dd500b4d53
8 changed files with 127 additions and 113 deletions

View File

@@ -25,7 +25,7 @@ import java.util.Optional;
public enum DistributionSetFields implements RsqlQueryField {
ID("id"),
TYPE("type.key"),
TYPE("type", "key"),
NAME("name"),
DESCRIPTION("description"),
CREATEDAT("createdAt"),
@@ -33,27 +33,27 @@ public enum DistributionSetFields implements RsqlQueryField {
VERSION("version"),
COMPLETE("complete"),
MODULE("modules", SoftwareModuleFields.ID.getJpaEntityFieldName(), SoftwareModuleFields.NAME.getJpaEntityFieldName()),
TAG("tags.name"),
TAG("tags", "name"),
METADATA("metadata", new SimpleImmutableEntry<>("key", "value")),
VALID("valid");
private final String jpaEntityFieldName;
private final Entry<String, String> subEntityMapTuple;
private final List<String> subEntityAttributes;
private final Entry<String, String> subEntityMapTuple;
DistributionSetFields(final String jpaEntityFieldName) {
this(jpaEntityFieldName, null, Collections.emptyList());
this(jpaEntityFieldName, Collections.emptyList(), null);
}
DistributionSetFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this(jpaEntityFieldName, null, List.of(subEntityAttributes));
this(jpaEntityFieldName, List.of(subEntityAttributes), null);
}
DistributionSetFields(final String jpaEntityFieldName, final Entry<String, String> subEntityMapTuple) {
this(jpaEntityFieldName, subEntityMapTuple, Collections.emptyList());
this(jpaEntityFieldName, Collections.emptyList(), subEntityMapTuple);
}
DistributionSetFields(final String jpaEntityFieldName, final Entry<String, String> subEntityMapTuple, List<String> subEntityAttributes) {
DistributionSetFields(final String jpaEntityFieldName, List<String> subEntityAttributes, final Entry<String, String> subEntityMapTuple) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityMapTuple = subEntityMapTuple;
this.subEntityAttributes = subEntityAttributes;

View File

@@ -35,23 +35,6 @@ public interface RsqlQueryField {
@NotNull
String getJpaEntityFieldName();
/**
* Returns the sub attributes
*
* @param propertyFieldName the given field
* @return array consisting of sub attributes
*/
default String[] getSubAttributes(final String propertyFieldName) {
if (isMap()) {
final String[] subAttributes = propertyFieldName.split(SUB_ATTRIBUTE_SPLIT_REGEX, 2);
// [0] field name | [1] key name (could miss, e.g. for target attributes)
final String mapKeyName = subAttributes.length == 2 ? subAttributes[1] : null;
return ObjectUtils.isEmpty(mapKeyName) ? new String[] { getJpaEntityFieldName() } : new String[] { getJpaEntityFieldName(), mapKeyName };
} else {
return propertyFieldName.split(SUB_ATTRIBUTE_SPLIT_REGEX);
}
}
/**
* Contains the sub entity the given field.
*

View File

@@ -12,6 +12,8 @@ package org.eclipse.hawkbit.repository;
import lombok.Getter;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
@@ -22,22 +24,31 @@ import java.util.Optional;
public enum SoftwareModuleFields implements RsqlQueryField {
ID("id"),
TYPE("type.key"),
TYPE("type", "key"),
NAME("name"),
DESCRIPTION("description"),
VERSION("version"),
METADATA("metadata", new SimpleImmutableEntry<>("key", "value"));
private final String jpaEntityFieldName;
private Entry<String, String> subEntityMapTuple;
private final List<String> subEntityAttributes;
private final Entry<String, String> subEntityMapTuple;
SoftwareModuleFields(final String jpaEntityFieldName) {
this(jpaEntityFieldName, null);
this(jpaEntityFieldName, Collections.emptyList(), null);
}
SoftwareModuleFields(final String jpaEntityFieldName, final String... subEntityAttributes) {
this(jpaEntityFieldName, List.of(subEntityAttributes), null);
}
SoftwareModuleFields(final String jpaEntityFieldName, final Entry<String, String> subEntityMapTuple) {
this(jpaEntityFieldName, Collections.emptyList(), subEntityMapTuple);
}
SoftwareModuleFields(final String jpaEntityFieldName, List<String> subEntityAttributes, final Entry<String, String> subEntityMapTuple) {
this.jpaEntityFieldName = jpaEntityFieldName;
this.subEntityMapTuple = subEntityMapTuple;
this.subEntityAttributes = subEntityAttributes;
}
@Override

View File

@@ -35,7 +35,7 @@ public enum TargetFields implements RsqlQueryField {
ATTRIBUTE("controllerAttributes"),
ASSIGNEDDS("assignedDistributionSet", "name", "version"),
INSTALLEDDS("installedDistributionSet", "name", "version"),
TAG("tags.name"),
TAG("tags", "name"),
LASTCONTROLLERREQUESTAT("lastTargetQuery"),
METADATA("metadata", new SimpleImmutableEntry<>("key", "value")),
TARGETTYPE("targetType", TargetTypeFields.KEY.getJpaEntityFieldName(), TargetTypeFields.NAME.getJpaEntityFieldName());

View File

@@ -21,6 +21,7 @@ import org.eclipse.hawkbit.repository.RsqlQueryField;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import cz.jirutka.rsql.parser.ast.ComparisonNode;
import org.springframework.util.ObjectUtils;
@Slf4j
public abstract class AbstractRSQLVisitor<A extends Enum<A> & RsqlQueryField> {
@@ -28,14 +29,14 @@ public abstract class AbstractRSQLVisitor<A extends Enum<A> & RsqlQueryField> {
private final Class<A> rsqlQueryFieldType;
@Value
protected class RsqlField {
protected class QuertPath {
A enumValue;
String[] subAttributes;
String[] jpaPath;
private RsqlField(final A enumValue, final String[] subAttributes) {
private QuertPath(final A enumValue, final String[] jpaPath) {
this.enumValue = enumValue;
this.subAttributes = subAttributes;
this.jpaPath = jpaPath;
}
}
@@ -43,49 +44,74 @@ public abstract class AbstractRSQLVisitor<A extends Enum<A> & RsqlQueryField> {
this.rsqlQueryFieldType = rsqlQueryFieldType;
}
protected RsqlField getRsqlField(final ComparisonNode node) {
final String[] graph = node.getSelector().split(RsqlQueryField.SUB_ATTRIBUTE_SPLIT_REGEX);
final String enumName = graph.length == 0 ? node.getSelector() : graph[0];
protected QuertPath getQuertPath(final ComparisonNode node) {
final String[] path = node.getSelector().split(RsqlQueryField.SUB_ATTRIBUTE_SPLIT_REGEX);
if (path.length == 0) {
throw createRSQLParameterUnsupportedException(node, null);
}
final String enumName = path[0].toUpperCase();
log.debug("get field identifier by name {} of enum type {}", enumName, rsqlQueryFieldType);
try {
final A enumValue = Enum.valueOf(rsqlQueryFieldType, enumName.toUpperCase());
final String[] subAttributes = enumValue.getSubAttributes(node.getSelector());
final A enumValue = Enum.valueOf(rsqlQueryFieldType, enumName);
String[] split = getSplit(enumValue, node.getSelector());
// validate
if (enumValue.isMap()) {
// <enum>.<key>
if (subAttributes.length != 2) {
if (split.length != 2) {
throw new RSQLParameterUnsupportedFieldException(
"The syntax of the given map search parameter field {" + node.getSelector() + "} is wrong. Syntax is: <enum name>.<key name>");
}
} else {
// sub entity need minimum 1 dot
if (!enumValue.getSubEntityAttributes().isEmpty() && subAttributes.length < 2) {
throw createRSQLParameterUnsupportedException(node, null);
if (!enumValue.getSubEntityAttributes().isEmpty() && split.length < 2) {
if (enumValue.getSubEntityAttributes().size() == 1) { // single sub attribute - so default
split = new String[] { split[0], enumValue.getSubEntityAttributes().get(0) };
} else {
throw createRSQLParameterUnsupportedException(node, null);
}
}
}
// build property and validate sub attributes
final StringBuilder fieldNameBuilder = new StringBuilder(enumValue.getJpaEntityFieldName());
for (int i = 1; i < subAttributes.length; i++) {
final String propertyField = getFormattedSubEntityAttribute(enumValue, subAttributes[i]);
// validate and normalize (replace enum name with JPA entity field name and format the rest) prepare jpa path
split[0] = enumValue.getJpaEntityFieldName();
for (int i = 1; i < split.length; i++) {
split[i] = getFormattedSubEntityAttribute(enumValue, split[i]);
if (!enumValue.containsSubEntityAttribute(propertyField)) {
if (i != subAttributes.length - 1 || !enumValue.isMap()) {
if (!enumValue.containsSubEntityAttribute(split[i])) {
if (i != split.length - 1 || !enumValue.isMap()) {
throw createRSQLParameterUnsupportedException(node, null);
} // otherwise - the key of map is not in the sub entity attributes
}
fieldNameBuilder.append(RsqlQueryField.SUB_ATTRIBUTE_SEPARATOR).append(propertyField);
}
return new RsqlField(enumValue, enumValue.getSubAttributes(fieldNameBuilder.toString()));
return new QuertPath(enumValue, split);
} catch (final IllegalArgumentException e) {
throw createRSQLParameterUnsupportedException(node, e);
}
}
/**
* Returns the sub attributes
*
* @param rsqlFieldName the given field
* @return array consisting of sub attributes
*/
private String[] getSplit(final A enumValue, final String rsqlFieldName) {
if (enumValue.isMap()) {
final String[] subAttributes = rsqlFieldName.split(RsqlQueryField.SUB_ATTRIBUTE_SPLIT_REGEX, 2);
// [0] field name | [1] key name (could miss, e.g. for target attributes)
final String mapKeyName = subAttributes.length == 2 ? subAttributes[1] : null;
return ObjectUtils.isEmpty(mapKeyName) ?
new String[] { enumValue.getJpaEntityFieldName() } :
new String[] { enumValue.getJpaEntityFieldName(), mapKeyName };
} else {
return rsqlFieldName.split(RsqlQueryField.SUB_ATTRIBUTE_SPLIT_REGEX);
}
}
/**
* @param node current processing node
* @param rootException in case there is a cause otherwise {@code null}

View File

@@ -48,7 +48,7 @@ public class FieldValidationRsqlVisitor<A extends Enum<A> & RsqlQueryField> exte
@Override
public Void visit(final ComparisonNode node, final String param) {
// get AND validates
getRsqlField(node);
getQuertPath(node);
return null;
}

View File

@@ -171,12 +171,12 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
* @param enumField
* field from a FieldNameProvider to resolve on the persistence
* layer
* @param rsqlField RSQL field
* @param queryPath RSQL field
* @return the Path for a field
*/
@SuppressWarnings("unchecked")
private Path<Object> getFieldPath(final A enumField, final RsqlField rsqlField) {
return (Path<Object>) getFieldPath(root, rsqlField.getSubAttributes(), enumField.isMap(),
private Path<Object> getFieldPath(final A enumField, final QuertPath queryPath) {
return (Path<Object>) getFieldPath(root, queryPath.getJpaPath(), enumField.isMap(),
this::getJoinFieldPath).orElseThrow(
() -> new RSQLParameterUnsupportedFieldException("RSQL field path cannot be empty", null));
}
@@ -217,19 +217,19 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
// https://jira.sonarsource.com/browse/SONARJAVA-1478
@SuppressWarnings({ "squid:S2095" })
public List<Predicate> visit(final ComparisonNode node, final String param) {
final RsqlField rsqlField = getRsqlField(node);
final QuertPath queryPath = getQuertPath(node);
final List<String> values = node.getArguments();
final List<Object> transformedValues = new ArrayList<>();
final Path<Object> fieldPath = getFieldPath(rsqlField.getEnumValue(), rsqlField);
final Path<Object> fieldPath = getFieldPath(queryPath.getEnumValue(), queryPath);
for (final String value : values) {
transformedValues.add(convertValueIfNecessary(node, rsqlField.getEnumValue(), value, fieldPath));
transformedValues.add(convertValueIfNecessary(node, queryPath.getEnumValue(), value, fieldPath));
}
this.joinsNeeded = this.joinsNeeded || areJoinsNeeded(node);
return mapToPredicate(node, fieldPath, node.getArguments(), transformedValues, rsqlField);
return mapToPredicate(node, fieldPath, node.getArguments(), transformedValues, queryPath);
}
private static boolean areJoinsNeeded(final ComparisonNode node) {
@@ -305,7 +305,7 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
}
private List<Predicate> mapToPredicate(final ComparisonNode node, final Path<Object> fieldPath,
final List<String> values, final List<Object> transformedValues, final RsqlField rsqlField) {
final List<String> values, final List<Object> transformedValues, final QuertPath queryPath) {
String value = values.get(0);
// if lookup is available, replace macros ...
@@ -313,16 +313,16 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
value = virtualPropertyReplacer.replace(value);
}
final Predicate mapPredicate = mapToMapPredicate(node, fieldPath, rsqlField.getEnumValue());
final Predicate mapPredicate = mapToMapPredicate(fieldPath, queryPath);
final Predicate valuePredicate = addOperatorPredicate(node, getMapValueFieldPath(rsqlField.getEnumValue(), fieldPath),
transformedValues, value, rsqlField);
final Predicate valuePredicate = addOperatorPredicate(node, getMapValueFieldPath(queryPath.getEnumValue(), fieldPath),
transformedValues, value, queryPath);
return toSingleList(mapPredicate != null ? cb.and(mapPredicate, valuePredicate) : valuePredicate);
}
private Predicate addOperatorPredicate(final ComparisonNode node, final Path<Object> fieldPath,
final List<Object> transformedValues, final String value, final RsqlField rsqlField) {
final List<Object> transformedValues, final String value, final QuertPath queryPath) {
// only 'equal' and 'notEqual' can handle transformed value like
// enums. The JPA API cannot handle object types for greaterThan etc
@@ -334,7 +334,7 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
case "==":
return getEqualToPredicate(transformedValue, fieldPath);
case "!=":
return getNotEqualToPredicate(transformedValue, fieldPath, rsqlField);
return getNotEqualToPredicate(transformedValue, fieldPath, queryPath);
case "=gt=":
return cb.greaterThan(pathOfString(fieldPath), value);
case "=ge=":
@@ -346,7 +346,7 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
case "=in=":
return in(pathOfString(fieldPath), transformedValues);
case "=out=":
return getOutPredicate(transformedValues, rsqlField, fieldPath);
return getOutPredicate(transformedValues, queryPath, fieldPath);
default:
throw new RSQLParameterSyntaxException(
"operator symbol {" + operator + "} is either not supported or not implemented");
@@ -355,17 +355,17 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
private Predicate getOutPredicate(
final List<Object> transformedValues,
final RsqlField rsqlField, final Path<Object> fieldPath) {
final String[] fieldNames = rsqlField.getSubAttributes();
final QuertPath queryPath, final Path<Object> fieldPath) {
final String[] fieldNames = queryPath.getJpaPath();
if (isSimpleField(fieldNames, rsqlField.getEnumValue().isMap())) {
if (isSimpleField(fieldNames, queryPath.getEnumValue().isMap())) {
final Path<String> pathOfString = pathOfString(fieldPath);
return cb.or(cb.isNull(pathOfString), cb.not(in(pathOfString, transformedValues)));
}
clearOuterJoinsIfNotNeeded();
return toNotExistsSubQueryPredicate(fieldNames, rsqlField.getEnumValue(), expressionToCompare -> in(expressionToCompare, transformedValues));
return toNotExistsSubQueryPredicate(fieldNames, queryPath.getEnumValue(), expressionToCompare -> in(expressionToCompare, transformedValues));
}
private Path<Object> getMapValueFieldPath(final A enumField, final Path<Object> fieldPath) {
@@ -378,20 +378,19 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
}
@SuppressWarnings("unchecked")
private Predicate mapToMapPredicate(final ComparisonNode node, final Path<Object> fieldPath, final A enumField) {
if (!enumField.isMap()) {
private Predicate mapToMapPredicate(final Path<Object> fieldPath, final QuertPath queryPath) {
if (!queryPath.getEnumValue().isMap()) {
return null;
}
final String[] graph = enumField.getSubAttributes(node.getSelector());
final String[] graph = queryPath.getJpaPath();
final String keyValue = graph[graph.length - 1];
if (fieldPath instanceof MapJoin) {
// Currently we support only string key. So below cast is safe.
return equal((Expression<String>) (((MapJoin<?, ?, ?>) fieldPath).key()), keyValue);
}
final String keyFieldName = enumField.getSubEntityMapTuple().map(Entry::getKey)
final String keyFieldName = queryPath.getEnumValue().getSubEntityMapTuple().map(Entry::getKey)
.orElseThrow(() -> new UnsupportedOperationException(
"For the fields, defined as Map, only Map java type or tuple in the form of " +
"SimpleImmutableEntry are allowed. Neither of those could be found!"));
@@ -419,7 +418,7 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
}
private Predicate getNotEqualToPredicate(final Object transformedValue, final Path<Object> fieldPath,
final RsqlField rsqlField) {
final QuertPath queryPath) {
if (transformedValue == null) {
return cb.isNotNull(pathOfString(fieldPath));
@@ -430,9 +429,9 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
return cb.and(cb.isNotNull(pathOfString(fieldPath)), cb.notEqual(pathOfString(fieldPath), ""));
}
final String[] fieldNames = rsqlField.getSubAttributes();
final String[] fieldNames = queryPath.getJpaPath();
if (isSimpleField(fieldNames, rsqlField.getEnumValue().isMap())) {
if (isSimpleField(fieldNames, queryPath.getEnumValue().isMap())) {
if (isPattern(transformedValueStr)) { // a pattern, use like
return cb.or(cb.isNull(pathOfString(fieldPath)), notLike(pathOfString(fieldPath), toSQL(transformedValueStr)));
} else {
@@ -443,9 +442,9 @@ public class JpaQueryRsqlVisitor<A extends Enum<A> & RsqlQueryField, T> extends
clearOuterJoinsIfNotNeeded();
if (isPattern(transformedValueStr)) { // a pattern, use like
return toNotExistsSubQueryPredicate(fieldNames, rsqlField.getEnumValue(), expressionToCompare -> like(expressionToCompare, toSQL(transformedValueStr)));
return toNotExistsSubQueryPredicate(fieldNames, queryPath.getEnumValue(), expressionToCompare -> like(expressionToCompare, toSQL(transformedValueStr)));
} else {
return toNotExistsSubQueryPredicate(fieldNames, rsqlField.getEnumValue(), expressionToCompare -> equal(expressionToCompare, transformedValueStr));
return toNotExistsSubQueryPredicate(fieldNames, queryPath.getEnumValue(), expressionToCompare -> equal(expressionToCompare, transformedValueStr));
}
}

View File

@@ -110,30 +110,30 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
@Override
public List<Predicate> visit(final ComparisonNode node, final String param) {
final RsqlField rsqlField = getRsqlField(node);
final QuertPath queryField = getQuertPath(node);
final List<String> values = node.getArguments();
final List<Object> transformedValues = new ArrayList<>();
final Path<Object> fieldPath = getFieldPath(root, rsqlField);
final Path<Object> fieldPath = getFieldPath(root, queryField);
for (final String value : values) {
transformedValues.add(convertValueIfNecessary(node, rsqlField.getEnumValue(), fieldPath, value));
transformedValues.add(convertValueIfNecessary(node, queryField.getEnumValue(), fieldPath, value));
}
this.joinsNeeded = this.joinsNeeded || areJoinsNeeded(node);
return mapToPredicate(node, rsqlField, fieldPath, node.getArguments(), transformedValues);
return mapToPredicate(node, queryField, fieldPath, node.getArguments(), transformedValues);
}
private List<Predicate> mapToPredicate(final ComparisonNode node, final RsqlField rsqlField,
private List<Predicate> mapToPredicate(final ComparisonNode node, final QuertPath queryField,
final Path<Object> fieldPath,
final List<String> values, final List<Object> transformedValues) {
// if lookup is available, replace macros ...
final String value = virtualPropertyReplacer == null ? values.get(0) : virtualPropertyReplacer.replace(values.get(0));
final Predicate mapPredicate = mapToMapPredicate(node, rsqlField.getEnumValue(), fieldPath);
final Predicate valuePredicate = addOperatorPredicate(node, rsqlField,
getValueFieldPath(rsqlField.getEnumValue(), fieldPath), transformedValues, value);
final Predicate mapPredicate = queryField.getEnumValue().isMap() ? mapToMapPredicate(queryField, fieldPath) : null;
final Predicate valuePredicate = addOperatorPredicate(node, queryField,
getValueFieldPath(queryField.getEnumValue(), fieldPath), transformedValues, value);
return Collections.singletonList(mapPredicate != null ? cb.and(mapPredicate, valuePredicate) : valuePredicate);
}
@@ -145,26 +145,21 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
}
}
@SuppressWarnings("unchecked")
private Predicate mapToMapPredicate(final ComparisonNode node, final A enumField, final Path<Object> fieldPath) {
if (!enumField.isMap()) {
return null;
}
final String[] graph = enumField.getSubAttributes(node.getSelector());
private Predicate mapToMapPredicate(final QuertPath queryField, final Path<Object> fieldPath) {
final String[] graph = queryField.getJpaPath();
final String keyValue = graph[graph.length - 1];
if (fieldPath instanceof MapJoin) {
// Currently we support only string key. So below cast is safe.
return equal((Expression<String>) (((MapJoin<?, ?, ?>) fieldPath).key()), keyValue);
}
final String keyFieldName = enumField.getSubEntityMapTuple().map(Entry::getKey)
final String keyFieldName = queryField.getEnumValue().getSubEntityMapTuple().map(Entry::getKey)
.orElseThrow(() -> new UnsupportedOperationException(
"For the fields, defined as Map, only Map java type or tuple in the form of " +
"SimpleImmutableEntry are allowed. Neither of those could be found!"));
return equal(fieldPath.get(keyFieldName), keyValue);
}
private Predicate addOperatorPredicate(final ComparisonNode node, final RsqlField rsqlField,
private Predicate addOperatorPredicate(final ComparisonNode node, final QuertPath queryField,
final Path<Object> fieldPath, final List<Object> transformedValues, final String value) {
// only 'equal' and 'notEqual' can handle transformed value like enums.
// The JPA API cannot handle object types for greaterThan etc. methods.
@@ -172,13 +167,13 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
final String operator = node.getOperator().getSymbol();
return switch (operator) {
case "==" -> getEqualToPredicate(fieldPath, transformedValue);
case "!=" -> getNotEqualToPredicate(rsqlField, fieldPath, transformedValue);
case "!=" -> getNotEqualToPredicate(queryField, fieldPath, transformedValue);
case "=gt=" -> cb.greaterThan(pathOfString(fieldPath), value);
case "=ge=" -> cb.greaterThanOrEqualTo(pathOfString(fieldPath), value);
case "=lt=" -> cb.lessThan(pathOfString(fieldPath), value);
case "=le=" -> cb.lessThanOrEqualTo(pathOfString(fieldPath), value);
case "=in=" -> in(pathOfString(fieldPath), transformedValues);
case "=out=" -> getOutPredicate(rsqlField, fieldPath, transformedValues);
case "=out=" -> getOutPredicate(queryField, fieldPath, transformedValues);
default -> throw new RSQLParameterSyntaxException(
"Operator symbol {" + operator + "} is either not supported or not implemented");
};
@@ -203,7 +198,7 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
return cb.equal(fieldPath, transformedValue);
}
private Predicate getNotEqualToPredicate(final RsqlField rsqlField,
private Predicate getNotEqualToPredicate(final QuertPath queryField,
final Path<Object> fieldPath, final Object transformedValue) {
if (transformedValue == null) {
return cb.isNotNull(fieldPath);
@@ -214,9 +209,9 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
return cb.and(cb.isNotNull(fieldPath), cb.notEqual(pathOfString(fieldPath), ""));
}
final String[] fieldNames = rsqlField.getSubAttributes();
final String[] fieldNames = queryField.getJpaPath();
if (isSimpleField(fieldNames, rsqlField.getEnumValue().isMap())) {
if (isSimpleField(fieldNames, queryField.getEnumValue().isMap())) {
if (isPattern(transformedValueStr)) { // a pattern, use like
return cb.or(cb.isNull(fieldPath), notLike(pathOfString(fieldPath), toSQL(transformedValueStr)));
} else {
@@ -225,7 +220,7 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
}
clearJoinsIfNotNeeded();
return toNotExistsSubQueryPredicate(rsqlField, expressionToCompare ->
return toNotExistsSubQueryPredicate(queryField, expressionToCompare ->
isPattern(transformedValueStr) ? // a pattern, use like
like(expressionToCompare, toSQL(transformedValueStr)) :
equal(expressionToCompare, transformedValueStr));
@@ -234,22 +229,22 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
return toNullOrNotEqualPredicate(fieldPath, transformedValue);
}
private Predicate getOutPredicate(final RsqlField rsqlField, final Path<Object> fieldPath,
private Predicate getOutPredicate(final QuertPath queryField, final Path<Object> fieldPath,
final List<Object> transformedValues) {
final String[] subAttributes = rsqlField.getSubAttributes();
final String[] subAttributes = queryField.getJpaPath();
if (isSimpleField(subAttributes, rsqlField.getEnumValue().isMap())) {
if (isSimpleField(subAttributes, queryField.getEnumValue().isMap())) {
return cb.or(cb.isNull(fieldPath), cb.not(in(pathOfString(fieldPath), transformedValues)));
}
clearJoinsIfNotNeeded();
return toNotExistsSubQueryPredicate(rsqlField, expressionToCompare -> in(expressionToCompare, transformedValues));
return toNotExistsSubQueryPredicate(queryField, expressionToCompare -> in(expressionToCompare, transformedValues));
}
private Path<Object> getFieldPath(final Root<?> root, final RsqlField rsqlField) {
final String[] split = rsqlField.getSubAttributes();
private Path<Object> getFieldPath(final Root<?> root, final QuertPath queryField) {
final String[] split = queryField.getJpaPath();
Path<Object> fieldPath = null;
for (int i = 0, end = rsqlField.getEnumValue().isMap() ? split.length - 1 : split.length; i < end; i++) {
for (int i = 0, end = queryField.getEnumValue().isMap() ? split.length - 1 : split.length; i < end; i++) {
final String fieldNameSplit = split[i];
fieldPath = fieldPath == null ? getPath(root, fieldNameSplit) : fieldPath.get(fieldNameSplit);
}
@@ -358,14 +353,14 @@ public class JpaQueryRsqlVisitorG2<A extends Enum<A> & RsqlQueryField, T>
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Predicate toNotExistsSubQueryPredicate(final RsqlField rsqlField, final Function<Expression<String>, Predicate> subQueryPredicateProvider) {
private Predicate toNotExistsSubQueryPredicate(final QuertPath queryField, final Function<Expression<String>, Predicate> subQueryPredicateProvider) {
final Class<?> javaType = root.getJavaType();
final Subquery<?> subquery = query.subquery(javaType);
final Root subqueryRoot = subquery.from(javaType);
final Predicate equalPredicate = cb.equal(root.get(rsqlField.getEnumValue().identifierFieldName()),
subqueryRoot.get(rsqlField.getEnumValue().identifierFieldName()));
final Expression<String> expressionToCompare = getExpressionToCompare(rsqlField.getEnumValue(),
getFieldPath(subqueryRoot, rsqlField));
final Predicate equalPredicate = cb.equal(root.get(queryField.getEnumValue().identifierFieldName()),
subqueryRoot.get(queryField.getEnumValue().identifierFieldName()));
final Expression<String> expressionToCompare = getExpressionToCompare(queryField.getEnumValue(),
getFieldPath(subqueryRoot, queryField));
final Predicate subQueryPredicate = subQueryPredicateProvider.apply(expressionToCompare);
subquery.select(subqueryRoot).where(cb.and(equalPredicate, subQueryPredicate));
return cb.not(cb.exists(subquery));