Refactor RSQL searach fields related classes (3) (#1836)
Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user