find(PredicateDto predicateDto, String statement) {
+
+ if (predicateDto instanceof PredicateLogicalOrOperation) {
+
+ return ((PredicateLogicalOrOperation) predicateDto)
+ .getValues().stream()
+ .map(p -> find(p, statement))
+ .flatMap(Optional::stream)
+ .findAny();
+ }
+
+ if (predicateDto instanceof PredicateLogicalAndOperation) {
+
+ return ((PredicateLogicalAndOperation) predicateDto)
+ .getValues().stream()
+ .map(p -> find(p, statement))
+ .flatMap(Optional::stream)
+ .findAny();
+ }
+
+ if (predicateDto instanceof PredicateComparisonOperatorDto) {
+ return Optional.of((PredicateComparisonOperatorDto) predicateDto)
+ .filter(p -> p.getStatement().equals(statement));
+ }
+
+ return Optional.empty();
+ }
+
+ public static P clone(P predicateDto) {
+
+ if (predicateDto instanceof PredicateLogicalAndOperation) {
+
+ PredicateLogicalAndOperation clone = new PredicateLogicalAndOperation();
+ clone.setValues(((PredicateLogicalAndOperation) predicateDto)
+ .getValues().stream().map(PredicateHelper::clone).collect(Collectors.toList()));
+ return (P) clone;
+ }
+
+ if (predicateDto instanceof PredicateComparisonOperatorDto) {
+
+ return (P) new PredicateComparisonOperatorDto((PredicateComparisonOperatorDto) predicateDto);
+ }
+
+ return predicateDto;
+ }
+
+ public static SimplePredicateDto add(SimplePredicateDto simplePredicateDto, SimplePredicateDto add) {
+ if (simplePredicateDto instanceof PredicateLogicalAndOperation) {
+ ((PredicateLogicalAndOperation) simplePredicateDto).getValues().add(add);
+ return simplePredicateDto;
+ } else {
+ PredicateLogicalAndOperation and = new PredicateLogicalAndOperation();
+ and.getValues().add(simplePredicateDto);
+ and.getValues().add(add);
+
+ return and;
+ }
+ }
+}
diff --git a/aql/src/main/java/org/ehrbase/aql/dto/path/predicate/PredicateLogicalAndOperation.java b/aql/src/main/java/org/ehrbase/aql/dto/path/predicate/PredicateLogicalAndOperation.java
new file mode 100644
index 000000000..5fa212a8c
--- /dev/null
+++ b/aql/src/main/java/org/ehrbase/aql/dto/path/predicate/PredicateLogicalAndOperation.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School.
+ *
+ * This file is part of project openEHR_SDK
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ehrbase.aql.dto.path.predicate;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.ehrbase.aql.dto.condition.LogicalOperatorDto;
+
+/**
+ * @author Stefan Spiska
+ */
+public class PredicateLogicalAndOperation
+ implements LogicalOperatorDto,
+ SimplePredicateDto,
+ Serializable {
+
+ private final PredicateLogicalOperatorSymbol symbol = PredicateLogicalOperatorSymbol.AND;
+ private List values = new ArrayList<>();
+
+ @Override
+ public PredicateLogicalOperatorSymbol getSymbol() {
+ return symbol;
+ }
+
+ @Override
+ public List getValues() {
+ return values;
+ }
+
+ @Override
+ public void setSymbol(PredicateLogicalOperatorSymbol symbol) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setValues(List values) {
+ this.values = values;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ PredicateLogicalAndOperation that = (PredicateLogicalAndOperation) o;
+ return symbol == that.symbol && Objects.equals(values, that.values);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(symbol, values);
+ }
+
+ @Override
+ public String toString() {
+ return "PredicateLogicalAndOperation{" + "symbol=" + symbol + ", values=" + values + '}';
+ }
+}
diff --git a/aql/src/main/java/org/ehrbase/aql/dto/path/predicate/PredicateLogicalOperatorSymbol.java b/aql/src/main/java/org/ehrbase/aql/dto/path/predicate/PredicateLogicalOperatorSymbol.java
new file mode 100644
index 000000000..138926194
--- /dev/null
+++ b/aql/src/main/java/org/ehrbase/aql/dto/path/predicate/PredicateLogicalOperatorSymbol.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2020 vitasystems GmbH and Hannover Medical School.
+ *
+ * This file is part of project openEHR_SDK
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ehrbase.aql.dto.path.predicate;
+
+import org.ehrbase.aql.dto.LogicalOperatorSymbol;
+
+public enum PredicateLogicalOperatorSymbol implements LogicalOperatorSymbol {
+ OR(4),
+ AND(2);
+
+ private final int precedence;
+
+ PredicateLogicalOperatorSymbol(int precedence) {
+ this.precedence = precedence;
+ }
+
+ public int getPrecedence() {
+ return precedence;
+ }
+}
diff --git a/aql/src/main/java/org/ehrbase/aql/dto/path/predicate/PredicateLogicalOrOperation.java b/aql/src/main/java/org/ehrbase/aql/dto/path/predicate/PredicateLogicalOrOperation.java
new file mode 100644
index 000000000..5f66ce4e3
--- /dev/null
+++ b/aql/src/main/java/org/ehrbase/aql/dto/path/predicate/PredicateLogicalOrOperation.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School.
+ *
+ * This file is part of project openEHR_SDK
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ehrbase.aql.dto.path.predicate;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import org.ehrbase.aql.dto.condition.LogicalOperatorDto;
+
+/**
+ * @author Stefan Spiska
+ */
+public class PredicateLogicalOrOperation
+ implements PredicateDto, LogicalOperatorDto, Serializable {
+
+ private final PredicateLogicalOperatorSymbol symbol = PredicateLogicalOperatorSymbol.OR;
+ private List values = new ArrayList<>();
+
+ @Override
+ public PredicateLogicalOperatorSymbol getSymbol() {
+ return symbol;
+ }
+
+ @Override
+ public List getValues() {
+ return values;
+ }
+
+ @Override
+ public void setSymbol(PredicateLogicalOperatorSymbol symbol) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void setValues(List values) {
+ this.values = values;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ PredicateLogicalOrOperation that = (PredicateLogicalOrOperation) o;
+ return symbol == that.symbol && Objects.equals(values, that.values);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(symbol, values);
+ }
+
+ @Override
+ public String toString() {
+ return "PredicateLogicalOrOperation{" + "symbol=" + symbol + ", values=" + values + '}';
+ }
+}
diff --git a/aql/src/main/java/org/ehrbase/aql/dto/path/predicate/SimplePredicateDto.java b/aql/src/main/java/org/ehrbase/aql/dto/path/predicate/SimplePredicateDto.java
new file mode 100644
index 000000000..4f6cd2cf9
--- /dev/null
+++ b/aql/src/main/java/org/ehrbase/aql/dto/path/predicate/SimplePredicateDto.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School.
+ *
+ * This file is part of project openEHR_SDK
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ehrbase.aql.dto.path.predicate;
+
+/**
+ * @author Stefan Spiska
+ */
+public interface SimplePredicateDto extends PredicateDto {}
diff --git a/aql/src/main/java/org/ehrbase/aql/dto/select/AQLFunction.java b/aql/src/main/java/org/ehrbase/aql/dto/select/AQLFunction.java
new file mode 100644
index 000000000..ad15b4a86
--- /dev/null
+++ b/aql/src/main/java/org/ehrbase/aql/dto/select/AQLFunction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School.
+ *
+ * This file is part of project openEHR_SDK
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ehrbase.aql.dto.select;
+
+/**
+ * @author Stefan Spiska
+ */
+public enum AQLFunction {
+ COUNT,
+ MIN,
+ MAX,
+ AVG;
+}
diff --git a/aql/src/main/java/org/ehrbase/aql/dto/select/FunctionDto.java b/aql/src/main/java/org/ehrbase/aql/dto/select/FunctionDto.java
new file mode 100644
index 000000000..250e0ef08
--- /dev/null
+++ b/aql/src/main/java/org/ehrbase/aql/dto/select/FunctionDto.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School.
+ *
+ * This file is part of project openEHR_SDK
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ehrbase.aql.dto.select;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @author Stefan Spiska
+ */
+public class FunctionDto implements SelectStatementDto {
+
+ private AQLFunction aqlFunction;
+
+ private List parameters;
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public AQLFunction getAqlFunction() {
+ return aqlFunction;
+ }
+
+ public void setAqlFunction(AQLFunction aqlFunction) {
+ this.aqlFunction = aqlFunction;
+ }
+
+ public List getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(List parameters) {
+ this.parameters = parameters;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ FunctionDto that = (FunctionDto) o;
+ return aqlFunction == that.aqlFunction
+ && Objects.equals(parameters, that.parameters)
+ && Objects.equals(name, that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(aqlFunction, parameters, name);
+ }
+
+ @Override
+ public String toString() {
+ return "FunctionDto{" + "aqlFunction="
+ + aqlFunction + ", parameters="
+ + parameters + ", name='"
+ + name + '\'' + '}';
+ }
+}
diff --git a/aql/src/main/java/org/ehrbase/aql/dto/select/SelectDto.java b/aql/src/main/java/org/ehrbase/aql/dto/select/SelectDto.java
index 9d4561477..9eb924e7a 100644
--- a/aql/src/main/java/org/ehrbase/aql/dto/select/SelectDto.java
+++ b/aql/src/main/java/org/ehrbase/aql/dto/select/SelectDto.java
@@ -24,6 +24,9 @@ public class SelectDto {
private Integer topCount;
private Direction topDirection;
+
+ private boolean isDistinct = false;
+
private List statement;
public Integer getTopCount() {
@@ -50,6 +53,14 @@ public void setStatement(List statement) {
this.statement = statement;
}
+ public boolean isDistinct() {
+ return isDistinct;
+ }
+
+ public void setDistinct(boolean distinct) {
+ isDistinct = distinct;
+ }
+
public boolean equals(final Object o) {
if (o == this) return true;
if (!(o instanceof SelectDto)) return false;
diff --git a/aql/src/main/java/org/ehrbase/aql/dto/select/SelectFieldDto.java b/aql/src/main/java/org/ehrbase/aql/dto/select/SelectFieldDto.java
index ccb4aff75..e5eca69a7 100644
--- a/aql/src/main/java/org/ehrbase/aql/dto/select/SelectFieldDto.java
+++ b/aql/src/main/java/org/ehrbase/aql/dto/select/SelectFieldDto.java
@@ -17,10 +17,12 @@
*/
package org.ehrbase.aql.dto.select;
+import org.ehrbase.aql.dto.path.AqlPath;
+
public class SelectFieldDto implements SelectStatementDto {
private String name;
- private String aqlPath;
+ private AqlPath aqlPath;
private int containmentId;
public String getName() {
@@ -28,7 +30,7 @@ public String getName() {
}
public String getAqlPath() {
- return this.aqlPath;
+ return this.aqlPath.format(AqlPath.OtherPredicatesFormat.SHORTED, false);
}
public int getContainmentId() {
@@ -40,7 +42,7 @@ public void setName(String name) {
}
public void setAqlPath(String aqlPath) {
- this.aqlPath = aqlPath;
+ this.aqlPath = AqlPath.parse(aqlPath);
}
public void setContainmentId(int containmentId) {
diff --git a/aql/src/main/java/org/ehrbase/aql/parser/AqlToDtoVisitor.java b/aql/src/main/java/org/ehrbase/aql/parser/AqlToDtoVisitor.java
index 70095807f..283636943 100644
--- a/aql/src/main/java/org/ehrbase/aql/parser/AqlToDtoVisitor.java
+++ b/aql/src/main/java/org/ehrbase/aql/parser/AqlToDtoVisitor.java
@@ -21,14 +21,20 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.apache.commons.collections4.MultiValuedMap;
import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
import org.ehrbase.aql.dto.AqlDto;
import org.ehrbase.aql.dto.EhrDto;
import org.ehrbase.aql.dto.LogicalOperatorSymbol;
@@ -37,7 +43,10 @@
import org.ehrbase.aql.dto.condition.ConditionDto;
import org.ehrbase.aql.dto.condition.ConditionLogicalOperatorDto;
import org.ehrbase.aql.dto.condition.ConditionLogicalOperatorSymbol;
+import org.ehrbase.aql.dto.condition.ExistsConditionOperatorDto;
+import org.ehrbase.aql.dto.condition.LogicalOperatorDto;
import org.ehrbase.aql.dto.condition.MatchesOperatorDto;
+import org.ehrbase.aql.dto.condition.NotConditionOperatorDto;
import org.ehrbase.aql.dto.condition.ParameterValue;
import org.ehrbase.aql.dto.condition.SimpleValue;
import org.ehrbase.aql.dto.condition.Value;
@@ -47,6 +56,13 @@
import org.ehrbase.aql.dto.containment.ContainmentLogicalOperatorSymbol;
import org.ehrbase.aql.dto.orderby.OrderByExpressionDto;
import org.ehrbase.aql.dto.orderby.OrderByExpressionSymbol;
+import org.ehrbase.aql.dto.path.predicate.PredicateComparisonOperatorDto;
+import org.ehrbase.aql.dto.path.predicate.PredicateDto;
+import org.ehrbase.aql.dto.path.predicate.PredicateHelper;
+import org.ehrbase.aql.dto.path.predicate.PredicateLogicalAndOperation;
+import org.ehrbase.aql.dto.path.predicate.PredicateLogicalOrOperation;
+import org.ehrbase.aql.dto.select.AQLFunction;
+import org.ehrbase.aql.dto.select.FunctionDto;
import org.ehrbase.aql.dto.select.SelectDto;
import org.ehrbase.aql.dto.select.SelectFieldDto;
import org.ehrbase.aql.dto.select.SelectStatementDto;
@@ -63,7 +79,10 @@ public class AqlToDtoVisitor extends AqlBaseVisitor {
public AqlDto visitQuery(AqlParser.QueryContext ctx) {
AqlDto aqlDto = new AqlDto();
- aqlDto.setEhr(visitFromEHR(ctx.queryExpr().from().fromEHR()));
+ Pair visitFromEHR =
+ visitFromEHR(ctx.queryExpr().from().fromEHR());
+ aqlDto.setEhr(visitFromEHR.getLeft());
+
if (ctx.queryExpr().from().containsExpression() != null) {
aqlDto.setContains(visitContainsExpression(ctx.queryExpr().from().containsExpression()));
}
@@ -71,36 +90,118 @@ public AqlDto visitQuery(AqlParser.QueryContext ctx) {
if (ctx.queryExpr().where() != null) {
aqlDto.setWhere(visitIdentifiedExpr(ctx.queryExpr().where().identifiedExpr()));
}
+ if (visitFromEHR.getRight() != null) {
+ if (aqlDto.getWhere() == null) {
+ aqlDto.setWhere(visitFromEHR.getRight());
+ } else {
+ ConditionLogicalOperatorDto and = new ConditionLogicalOperatorDto();
+ and.setSymbol(ConditionLogicalOperatorSymbol.AND);
+ and.setValues(new ArrayList<>());
+ and.getValues().add(aqlDto.getWhere());
+ and.getValues().add(visitFromEHR.getRight());
+ aqlDto.setWhere(and);
+ }
+ }
if (ctx.queryExpr().orderBy() != null) {
aqlDto.setOrderBy(visitOrderBySeq(ctx.queryExpr().orderBy().orderBySeq()));
}
- if (ctx.queryExpr().limitExpr() != null) {
- AqlParser.LimitExprContext limitExpr = ctx.queryExpr().limitExpr();
+ if (ctx.queryExpr().limit() != null) {
+ AqlParser.LimitContext limitExpr = ctx.queryExpr().limit();
aqlDto.setLimit(Integer.parseInt(limitExpr.INTEGER().getText()));
- if (limitExpr.offset() != null) {
- aqlDto.setOffset(Integer.parseInt(limitExpr.offset().INTEGER().getText()));
+ if (ctx.queryExpr().offset() != null) {
+ aqlDto.setOffset(
+ Integer.parseInt(ctx.queryExpr().offset().INTEGER().getText()));
}
}
- selectFieldDtoMultiMap.entries().forEach(e -> e.getValue()
- .setContainmentId(
- Optional.ofNullable(identifierMap.get(e.getKey())).orElseThrow()));
+ selectFieldDtoMultiMap.entries().forEach(e -> {
+ if (identifierMap.containsKey(e.getKey())) {
+ e.getValue().setContainmentId(identifierMap.get(e.getKey()));
+ }
+ });
+
+ // replace reference by name
+ selectFieldDtoMultiMap.entries().stream()
+ .filter(e -> !identifierMap.containsKey(e.getKey()))
+ .forEach(e -> {
+ SelectFieldDto selectFieldDto = selectFieldDtoMultiMap.values().stream()
+ .filter(d -> e.getKey().equals(d.getName()))
+ .findAny()
+ .orElseThrow();
+
+ SelectFieldDto value = e.getValue();
+ value.setName(selectFieldDto.getName());
+ value.setAqlPath(selectFieldDto.getAqlPath());
+ value.setContainmentId(selectFieldDto.getContainmentId());
+ });
return aqlDto;
}
@Override
- public EhrDto visitFromEHR(AqlParser.FromEHRContext ctx) {
+ public Pair visitFromEHR(AqlParser.FromEHRContext ctx) {
EhrDto ehrDto = new EhrDto();
ehrDto.setContainmentId(buildContainmentId());
if (ctx.IDENTIFIER() != null) {
identifierMap.put(ctx.IDENTIFIER().getText(), ehrDto.getContainmentId());
ehrDto.setIdentifier(ctx.IDENTIFIER().getText());
}
+ return Pair.of(
+ ehrDto,
+ Optional.ofNullable(ctx.standardPredicate())
+ .map(AqlParser.StandardPredicateContext::predicateExpr)
+ .map(p -> buildConditionDtoFromPredicate(p, ehrDto.getContainmentId()))
+ .orElse(null));
+ }
+
+ private ConditionDto buildConditionDtoFromPredicate(AqlParser.PredicateExprContext p, int containmentId) {
+ PredicateDto predicateDto = PredicateHelper.buildPredicate(getFullText(p));
+ return to(predicateDto, containmentId);
+ }
+
+ public static String getFullText(ParserRuleContext context) {
+ if (context.start == null
+ || context.stop == null
+ || context.start.getStartIndex() < 0
+ || context.stop.getStopIndex() < 0) return context.getText(); // Fallback
+
+ return context.start
+ .getInputStream()
+ .getText(Interval.of(context.start.getStartIndex(), context.stop.getStopIndex()));
+ }
+
+ private ConditionDto to(PredicateDto predicateDto, int containmentId) {
+ if (predicateDto instanceof PredicateComparisonOperatorDto) {
+ ConditionComparisonOperatorDto conditionComparisonOperatorDto = new ConditionComparisonOperatorDto();
+ SelectFieldDto statement = new SelectFieldDto();
+ statement.setContainmentId(containmentId);
+ statement.setAqlPath(
+ StringUtils.prependIfMissing(((PredicateComparisonOperatorDto) predicateDto).getStatement(), "/"));
+ conditionComparisonOperatorDto.setStatement(statement);
+ conditionComparisonOperatorDto.setSymbol(((PredicateComparisonOperatorDto) predicateDto).getSymbol());
+ conditionComparisonOperatorDto.setValue(((PredicateComparisonOperatorDto) predicateDto).getValue());
+ return conditionComparisonOperatorDto;
+ }
- return ehrDto;
+ if (predicateDto instanceof PredicateLogicalAndOperation) {
+ ConditionLogicalOperatorDto and = new ConditionLogicalOperatorDto();
+ and.setSymbol(ConditionLogicalOperatorSymbol.AND);
+ and.setValues(((PredicateLogicalAndOperation) predicateDto)
+ .getValues().stream().map(p -> to(p, containmentId)).collect(Collectors.toList()));
+ return and;
+ }
+
+ if (predicateDto instanceof PredicateLogicalOrOperation) {
+ ConditionLogicalOperatorDto or = new ConditionLogicalOperatorDto();
+ or.setSymbol(ConditionLogicalOperatorSymbol.OR);
+ or.setValues(((PredicateLogicalOrOperation) predicateDto)
+ .getValues().stream().map(p -> to(p, containmentId)).collect(Collectors.toList()));
+ return or;
+ }
+
+ return null;
}
@Override
@@ -111,6 +212,9 @@ public SelectDto visitSelect(AqlParser.SelectContext ctx) {
selectDto.setTopDirection(extractSymbol(ctx.topExpr()));
selectDto.setTopCount(Integer.parseInt(ctx.topExpr().INTEGER().getText()));
}
+ if (ctx.selectExpr().DISTINCT() != null) {
+ selectDto.setDistinct(true);
+ }
return selectDto;
}
@@ -132,6 +236,14 @@ public List visitSelectExpr(AqlParser.SelectExprContext ctx)
selectFieldDto.setName(ctx.IDENTIFIER().getText());
}
selectStatementDtos.add(selectFieldDto);
+ } else if (ctx.stdExpression() != null) {
+ if (ctx.stdExpression().function() != null) {
+ FunctionDto functionDto = visitFunction(ctx.stdExpression().function());
+ selectStatementDtos.add(functionDto);
+ if (ctx.IDENTIFIER() != null) {
+ functionDto.setName(ctx.IDENTIFIER().getText());
+ }
+ }
}
if (ctx.selectExpr() != null) {
@@ -140,6 +252,24 @@ public List visitSelectExpr(AqlParser.SelectExprContext ctx)
return selectStatementDtos;
}
+ @Override
+ public FunctionDto visitFunction(AqlParser.FunctionContext ctx) {
+
+ FunctionDto functionDto = new FunctionDto();
+
+ AQLFunction aqlFunction =
+ AQLFunction.valueOf(ctx.FUNCTION_IDENTIFIER().toString().toUpperCase(Locale.ROOT));
+
+ functionDto.setAqlFunction(aqlFunction);
+
+ if (ctx.identifiedPath() != null) {
+ functionDto.setParameters(
+ ctx.identifiedPath().stream().map(this::visitIdentifiedPath).collect(Collectors.toList()));
+ }
+
+ return functionDto;
+ }
+
@Override
public SelectFieldDto visitIdentifiedPath(AqlParser.IdentifiedPathContext ctx) {
SelectFieldDto selectStatementDto = new SelectFieldDto();
@@ -173,40 +303,18 @@ public ContainmentExpresionDto visitContainsExpression(AqlParser.ContainsExpress
}
}
- private ContainmentLogicalOperator buildContainmentLogicalOperator(List boolList) {
+ public ContainmentLogicalOperator buildContainmentLogicalOperator(List boolList) {
- ContainmentLogicalOperator currentOperator = new ContainmentLogicalOperator();
- ContainmentLogicalOperatorSymbol currentSymbol = (ContainmentLogicalOperatorSymbol) boolList.get(1);
- currentOperator.setSymbol(currentSymbol);
- currentOperator.setValues(new ArrayList<>());
- currentOperator.getValues().add((ContainmentExpresionDto) boolList.get(0));
- ContainmentLogicalOperator lowestOperator = currentOperator;
- for (int i = 2; i < boolList.size(); i = i + 2) {
- ContainmentLogicalOperatorSymbol nextSymbol =
- i + 1 < boolList.size() ? (ContainmentLogicalOperatorSymbol) boolList.get(i + 1) : null;
- if (nextSymbol == null || Objects.equals(currentSymbol, nextSymbol)) {
- currentOperator.getValues().add((ContainmentExpresionDto) boolList.get(i));
- currentSymbol = nextSymbol;
- } else {
- ContainmentLogicalOperator nextOperator = new ContainmentLogicalOperator();
- nextOperator.setSymbol(nextSymbol);
- nextOperator.setValues(new ArrayList<>());
-
- if (hasHigherPrecedence(currentSymbol, nextSymbol)) {
- currentOperator.getValues().add((ContainmentExpresionDto) boolList.get(i));
- nextOperator.getValues().add(currentOperator);
- lowestOperator = nextOperator;
- } else {
- nextOperator.getValues().add((ContainmentExpresionDto) boolList.get(i));
- currentOperator.getValues().add(nextOperator);
- lowestOperator = currentOperator;
- }
+ return (ContainmentLogicalOperator) buildLogicalOperator(boolList, (Function<
+ ContainmentLogicalOperatorSymbol,
+ LogicalOperatorDto>)
+ s -> {
+ ContainmentLogicalOperator conditionLogicalOperatorDto = new ContainmentLogicalOperator();
+ conditionLogicalOperatorDto.setSymbol(s);
+ conditionLogicalOperatorDto.setValues(new ArrayList<>());
- currentOperator = nextOperator;
- currentSymbol = nextSymbol;
- }
- }
- return lowestOperator;
+ return conditionLogicalOperatorDto;
+ });
}
@Override
@@ -280,30 +388,40 @@ public ConditionDto visitIdentifiedExpr(AqlParser.IdentifiedExprContext ctx) {
private ConditionLogicalOperatorDto buildConditionLogicalOperator(List boolList) {
- ConditionLogicalOperatorDto currentOperator = new ConditionLogicalOperatorDto();
- ConditionLogicalOperatorSymbol currentSymbol = (ConditionLogicalOperatorSymbol) boolList.get(1);
- currentOperator.setSymbol(currentSymbol);
- currentOperator.setValues(new ArrayList<>());
- currentOperator.getValues().add((ConditionDto) boolList.get(0));
- ConditionLogicalOperatorDto lowestOperator = currentOperator;
+ return (ConditionLogicalOperatorDto) buildLogicalOperator(boolList, (Function<
+ ConditionLogicalOperatorSymbol,
+ LogicalOperatorDto>)
+ s -> {
+ ConditionLogicalOperatorDto conditionLogicalOperatorDto = new ConditionLogicalOperatorDto();
+ conditionLogicalOperatorDto.setSymbol(s);
+ conditionLogicalOperatorDto.setValues(new ArrayList<>());
+
+ return conditionLogicalOperatorDto;
+ });
+ }
+
+ public static LogicalOperatorDto buildLogicalOperator(
+ List boolList, Function> creator) {
+
+ S currentSymbol = (S) boolList.get(1);
+ LogicalOperatorDto currentOperator = creator.apply(currentSymbol);
+ currentOperator.getValues().add((T) boolList.get(0));
+ LogicalOperatorDto lowestOperator = currentOperator;
for (int i = 2; i < boolList.size(); i = i + 2) {
- ConditionLogicalOperatorSymbol nextSymbol =
- i + 1 < boolList.size() ? (ConditionLogicalOperatorSymbol) boolList.get(i + 1) : null;
+ S nextSymbol = i + 1 < boolList.size() ? (S) boolList.get(i + 1) : null;
if (nextSymbol == null || Objects.equals(currentSymbol, nextSymbol)) {
- currentOperator.getValues().add((ConditionDto) boolList.get(i));
+ currentOperator.getValues().add((T) boolList.get(i));
currentSymbol = nextSymbol;
} else {
- ConditionLogicalOperatorDto nextOperator = new ConditionLogicalOperatorDto();
- nextOperator.setSymbol(nextSymbol);
- nextOperator.setValues(new ArrayList<>());
+ LogicalOperatorDto nextOperator = creator.apply(nextSymbol);
if (hasHigherPrecedence(currentSymbol, nextSymbol)) {
- currentOperator.getValues().add((ConditionDto) boolList.get(i));
- nextOperator.getValues().add(currentOperator);
+ currentOperator.getValues().add((T) boolList.get(i));
+ nextOperator.getValues().add((T) currentOperator);
lowestOperator = nextOperator;
} else {
- nextOperator.getValues().add((ConditionDto) boolList.get(i));
- currentOperator.getValues().add(nextOperator);
+ nextOperator.getValues().add((T) boolList.get(i));
+ currentOperator.getValues().add((T) nextOperator);
lowestOperator = currentOperator;
}
@@ -319,7 +437,7 @@ private ConditionLogicalOperatorSymbol extractSymbolTerminal(TerminalNode child)
return null;
}
- switch (child.getSymbol().getText()) {
+ switch (child.getSymbol().getText().toLowerCase(Locale.ROOT)) {
case "or":
return ConditionLogicalOperatorSymbol.OR;
case "and":
@@ -373,8 +491,17 @@ public ConditionDto visitIdentifiedEquality(AqlParser.IdentifiedEqualityContext
}
conditionDto = matchesOperatorDto;
+ } else if (ctx.EXISTS() != null) {
+ conditionDto = new ExistsConditionOperatorDto(visitIdentifiedPath(ctx.identifiedPath()));
+ }
+
+ if (ctx.NOT() != null
+ // "NOT" not belonging to is, in or between.
+ && (ctx.IS() == null && ctx.IN() == null && ctx.BETWEEN() == null)) {
+ return new NotConditionOperatorDto(conditionDto);
+ } else {
+ return conditionDto;
}
- return conditionDto;
}
@Override
@@ -461,7 +588,7 @@ private ConditionComparisonOperatorSymbol extractSymbol(AqlParser.IdentifiedEqua
}
}
- private boolean hasHigherPrecedence(
+ private static boolean hasHigherPrecedence(
LogicalOperatorSymbol operatorSymbol, LogicalOperatorSymbol nextOperatorSymbol) {
if (nextOperatorSymbol == null) {
return true;
diff --git a/web-template/src/main/java/org/ehrbase/webtemplate/util/CharSequenceHelper.java b/aql/src/main/java/org/ehrbase/aql/util/CharSequenceHelper.java
similarity index 98%
rename from web-template/src/main/java/org/ehrbase/webtemplate/util/CharSequenceHelper.java
rename to aql/src/main/java/org/ehrbase/aql/util/CharSequenceHelper.java
index a109f3421..fcccd83a0 100644
--- a/web-template/src/main/java/org/ehrbase/webtemplate/util/CharSequenceHelper.java
+++ b/aql/src/main/java/org/ehrbase/aql/util/CharSequenceHelper.java
@@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.ehrbase.webtemplate.util;
+package org.ehrbase.aql.util;
import java.nio.CharBuffer;
import org.apache.commons.lang3.StringUtils;
diff --git a/client/src/main/java/org/ehrbase/client/aql/condition/And.java b/aql/src/main/java/org/ehrbase/client/aql/condition/And.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/condition/And.java
rename to aql/src/main/java/org/ehrbase/client/aql/condition/And.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/condition/BinaryLogicalOperator.java b/aql/src/main/java/org/ehrbase/client/aql/condition/BinaryLogicalOperator.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/condition/BinaryLogicalOperator.java
rename to aql/src/main/java/org/ehrbase/client/aql/condition/BinaryLogicalOperator.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/condition/ComparisonOperator.java b/aql/src/main/java/org/ehrbase/client/aql/condition/ComparisonOperator.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/condition/ComparisonOperator.java
rename to aql/src/main/java/org/ehrbase/client/aql/condition/ComparisonOperator.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/condition/Condition.java b/aql/src/main/java/org/ehrbase/client/aql/condition/Condition.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/condition/Condition.java
rename to aql/src/main/java/org/ehrbase/client/aql/condition/Condition.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/condition/Equal.java b/aql/src/main/java/org/ehrbase/client/aql/condition/Equal.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/condition/Equal.java
rename to aql/src/main/java/org/ehrbase/client/aql/condition/Equal.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/condition/Exists.java b/aql/src/main/java/org/ehrbase/client/aql/condition/Exists.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/condition/Exists.java
rename to aql/src/main/java/org/ehrbase/client/aql/condition/Exists.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/condition/GreaterOrEqual.java b/aql/src/main/java/org/ehrbase/client/aql/condition/GreaterOrEqual.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/condition/GreaterOrEqual.java
rename to aql/src/main/java/org/ehrbase/client/aql/condition/GreaterOrEqual.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/condition/GreaterThan.java b/aql/src/main/java/org/ehrbase/client/aql/condition/GreaterThan.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/condition/GreaterThan.java
rename to aql/src/main/java/org/ehrbase/client/aql/condition/GreaterThan.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/condition/LessOrEqual.java b/aql/src/main/java/org/ehrbase/client/aql/condition/LessOrEqual.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/condition/LessOrEqual.java
rename to aql/src/main/java/org/ehrbase/client/aql/condition/LessOrEqual.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/condition/LessThan.java b/aql/src/main/java/org/ehrbase/client/aql/condition/LessThan.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/condition/LessThan.java
rename to aql/src/main/java/org/ehrbase/client/aql/condition/LessThan.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/condition/Matches.java b/aql/src/main/java/org/ehrbase/client/aql/condition/Matches.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/condition/Matches.java
rename to aql/src/main/java/org/ehrbase/client/aql/condition/Matches.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/condition/Not.java b/aql/src/main/java/org/ehrbase/client/aql/condition/Not.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/condition/Not.java
rename to aql/src/main/java/org/ehrbase/client/aql/condition/Not.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/condition/NotEqual.java b/aql/src/main/java/org/ehrbase/client/aql/condition/NotEqual.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/condition/NotEqual.java
rename to aql/src/main/java/org/ehrbase/client/aql/condition/NotEqual.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/condition/Or.java b/aql/src/main/java/org/ehrbase/client/aql/condition/Or.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/condition/Or.java
rename to aql/src/main/java/org/ehrbase/client/aql/condition/Or.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/containment/And.java b/aql/src/main/java/org/ehrbase/client/aql/containment/And.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/containment/And.java
rename to aql/src/main/java/org/ehrbase/client/aql/containment/And.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/containment/BinaryLogicalOperator.java b/aql/src/main/java/org/ehrbase/client/aql/containment/BinaryLogicalOperator.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/containment/BinaryLogicalOperator.java
rename to aql/src/main/java/org/ehrbase/client/aql/containment/BinaryLogicalOperator.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/containment/Containment.java b/aql/src/main/java/org/ehrbase/client/aql/containment/Containment.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/containment/Containment.java
rename to aql/src/main/java/org/ehrbase/client/aql/containment/Containment.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/containment/ContainmentExpression.java b/aql/src/main/java/org/ehrbase/client/aql/containment/ContainmentExpression.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/containment/ContainmentExpression.java
rename to aql/src/main/java/org/ehrbase/client/aql/containment/ContainmentExpression.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/containment/ContainmentPath.java b/aql/src/main/java/org/ehrbase/client/aql/containment/ContainmentPath.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/containment/ContainmentPath.java
rename to aql/src/main/java/org/ehrbase/client/aql/containment/ContainmentPath.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/containment/Or.java b/aql/src/main/java/org/ehrbase/client/aql/containment/Or.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/containment/Or.java
rename to aql/src/main/java/org/ehrbase/client/aql/containment/Or.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/field/AqlField.java b/aql/src/main/java/org/ehrbase/client/aql/field/AqlField.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/field/AqlField.java
rename to aql/src/main/java/org/ehrbase/client/aql/field/AqlField.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/field/AqlFieldImp.java b/aql/src/main/java/org/ehrbase/client/aql/field/AqlFieldImp.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/field/AqlFieldImp.java
rename to aql/src/main/java/org/ehrbase/client/aql/field/AqlFieldImp.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/field/EhrFields.java b/aql/src/main/java/org/ehrbase/client/aql/field/EhrFields.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/field/EhrFields.java
rename to aql/src/main/java/org/ehrbase/client/aql/field/EhrFields.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/field/ListAqlFieldImp.java b/aql/src/main/java/org/ehrbase/client/aql/field/ListAqlFieldImp.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/field/ListAqlFieldImp.java
rename to aql/src/main/java/org/ehrbase/client/aql/field/ListAqlFieldImp.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/field/ListSelectAqlField.java b/aql/src/main/java/org/ehrbase/client/aql/field/ListSelectAqlField.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/field/ListSelectAqlField.java
rename to aql/src/main/java/org/ehrbase/client/aql/field/ListSelectAqlField.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/field/NativeSelectAqlField.java b/aql/src/main/java/org/ehrbase/client/aql/field/NativeSelectAqlField.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/field/NativeSelectAqlField.java
rename to aql/src/main/java/org/ehrbase/client/aql/field/NativeSelectAqlField.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/field/SelectAqlField.java b/aql/src/main/java/org/ehrbase/client/aql/field/SelectAqlField.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/field/SelectAqlField.java
rename to aql/src/main/java/org/ehrbase/client/aql/field/SelectAqlField.java
diff --git a/aql/src/main/java/org/ehrbase/client/aql/funtion/AbstractFunction.java b/aql/src/main/java/org/ehrbase/client/aql/funtion/AbstractFunction.java
new file mode 100644
index 000000000..696dae06f
--- /dev/null
+++ b/aql/src/main/java/org/ehrbase/client/aql/funtion/AbstractFunction.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School.
+ *
+ * This file is part of project openEHR_SDK
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ehrbase.client.aql.funtion;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.ehrbase.aql.dto.select.AQLFunction;
+import org.ehrbase.client.aql.containment.Containment;
+import org.ehrbase.client.aql.field.SelectAqlField;
+
+/**
+ * @author Stefan Spiska
+ */
+public abstract class AbstractFunction implements Function, SelectAqlField {
+
+ private final List> parameters = new ArrayList<>();
+
+ private final AQLFunction function;
+
+ private final String name;
+
+ protected AbstractFunction(List> parameters, AQLFunction function, String name) {
+ this.parameters.addAll(parameters);
+ this.function = function;
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public List> getParameters() {
+ return parameters;
+ }
+
+ @Override
+ public String buildAQL(Containment ehrContainment) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(function.toString()).append("(");
+
+ if (parameters != null) {
+
+ sb.append(parameters.stream().map(f -> f.buildAQL(ehrContainment)).collect(Collectors.joining(",")));
+ }
+
+ sb.append(")");
+ return sb.toString();
+ }
+
+ protected static AbstractFunction create(
+ List> parameters, AQLFunction function, String name, Class aClass) {
+
+ return new AbstractFunction(parameters, function, name) {
+ @Override
+ public Containment getContainment() {
+ return null;
+ }
+
+ @Override
+ public String getPath() {
+ return function.name();
+ }
+
+ @Override
+ public Class> getEntityClass() {
+ return null;
+ }
+
+ @Override
+ public Class getValueClass() {
+ return aClass;
+ }
+
+ @Override
+ public boolean isMultiValued() {
+ return false;
+ }
+ };
+ }
+}
diff --git a/aql/src/main/java/org/ehrbase/client/aql/funtion/Function.java b/aql/src/main/java/org/ehrbase/client/aql/funtion/Function.java
new file mode 100644
index 000000000..f0cacedb7
--- /dev/null
+++ b/aql/src/main/java/org/ehrbase/client/aql/funtion/Function.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School.
+ *
+ * This file is part of project openEHR_SDK
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ehrbase.client.aql.funtion;
+
+import java.util.Collections;
+import java.util.List;
+import org.ehrbase.aql.dto.select.AQLFunction;
+import org.ehrbase.client.aql.field.SelectAqlField;
+
+/**
+ * @author Stefan Spiska
+ */
+public interface Function {
+
+ List> getParameters();
+
+ static AbstractFunction count(SelectAqlField> field, String as) {
+
+ return AbstractFunction.create(Collections.singletonList(field), AQLFunction.COUNT, as, Integer.class);
+ }
+
+ static AbstractFunction count(SelectAqlField> field) {
+
+ return count(field, null);
+ }
+
+ static AbstractFunction max(SelectAqlField> field, String as) {
+
+ return AbstractFunction.create(Collections.singletonList(field), AQLFunction.MAX, as, Integer.class);
+ }
+
+ static AbstractFunction max(SelectAqlField> field) {
+
+ return max(field, null);
+ }
+
+ static AbstractFunction min(SelectAqlField> field, String as) {
+
+ return AbstractFunction.create(Collections.singletonList(field), AQLFunction.MAX, as, Integer.class);
+ }
+
+ static AbstractFunction min(SelectAqlField> field) {
+
+ return min(field, null);
+ }
+
+ static AbstractFunction avg(SelectAqlField> field, String as) {
+
+ return AbstractFunction.create(Collections.singletonList(field), AQLFunction.AVG, as, Integer.class);
+ }
+
+ static AbstractFunction avg(SelectAqlField> field) {
+
+ return avg(field, null);
+ }
+}
diff --git a/client/src/main/java/org/ehrbase/client/aql/orderby/AbstractOrderBy.java b/aql/src/main/java/org/ehrbase/client/aql/orderby/AbstractOrderBy.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/orderby/AbstractOrderBy.java
rename to aql/src/main/java/org/ehrbase/client/aql/orderby/AbstractOrderBy.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/orderby/AndThen.java b/aql/src/main/java/org/ehrbase/client/aql/orderby/AndThen.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/orderby/AndThen.java
rename to aql/src/main/java/org/ehrbase/client/aql/orderby/AndThen.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/orderby/Ascending.java b/aql/src/main/java/org/ehrbase/client/aql/orderby/Ascending.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/orderby/Ascending.java
rename to aql/src/main/java/org/ehrbase/client/aql/orderby/Ascending.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/orderby/Descending.java b/aql/src/main/java/org/ehrbase/client/aql/orderby/Descending.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/orderby/Descending.java
rename to aql/src/main/java/org/ehrbase/client/aql/orderby/Descending.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/orderby/OrderByExpression.java b/aql/src/main/java/org/ehrbase/client/aql/orderby/OrderByExpression.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/orderby/OrderByExpression.java
rename to aql/src/main/java/org/ehrbase/client/aql/orderby/OrderByExpression.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/parameter/AqlValue.java b/aql/src/main/java/org/ehrbase/client/aql/parameter/AqlValue.java
similarity index 83%
rename from client/src/main/java/org/ehrbase/client/aql/parameter/AqlValue.java
rename to aql/src/main/java/org/ehrbase/client/aql/parameter/AqlValue.java
index 7b4c0075e..5c82ebab4 100644
--- a/client/src/main/java/org/ehrbase/client/aql/parameter/AqlValue.java
+++ b/aql/src/main/java/org/ehrbase/client/aql/parameter/AqlValue.java
@@ -18,12 +18,12 @@
package org.ehrbase.client.aql.parameter;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.nedap.archie.json.JacksonUtil;
import java.time.temporal.TemporalAccessor;
import java.util.Objects;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
-import org.ehrbase.client.exception.ClientException;
-import org.ehrbase.serialisation.jsonencoding.ArchieObjectMapperProvider;
+import org.ehrbase.util.exception.SdkException;
public class AqlValue {
@@ -47,13 +47,13 @@ public String buildAql() {
} else if (TemporalAccessor.class.isAssignableFrom(value.getClass())) {
String valueAsString;
try {
- valueAsString = ArchieObjectMapperProvider.getObjectMapper().writeValueAsString(value);
+ valueAsString = JacksonUtil.getObjectMapper().writeValueAsString(value);
} catch (JsonProcessingException e) {
- throw new ClientException(e.getMessage(), e);
+ throw new SdkException(e.getMessage(), e);
}
return StringUtils.wrap(valueAsString.replace("\"", ""), "'");
} else {
- throw new ClientException(String.format("%s is not an valid AQL Value", value.getClass()));
+ throw new SdkException(String.format("%s is not an valid AQL Value", value.getClass()));
}
}
}
diff --git a/client/src/main/java/org/ehrbase/client/aql/parameter/Parameter.java b/aql/src/main/java/org/ehrbase/client/aql/parameter/Parameter.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/parameter/Parameter.java
rename to aql/src/main/java/org/ehrbase/client/aql/parameter/Parameter.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/parameter/ParameterValue.java b/aql/src/main/java/org/ehrbase/client/aql/parameter/ParameterValue.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/parameter/ParameterValue.java
rename to aql/src/main/java/org/ehrbase/client/aql/parameter/ParameterValue.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/query/EntityQuery.java b/aql/src/main/java/org/ehrbase/client/aql/query/EntityQuery.java
similarity index 89%
rename from client/src/main/java/org/ehrbase/client/aql/query/EntityQuery.java
rename to aql/src/main/java/org/ehrbase/client/aql/query/EntityQuery.java
index 59f396195..231a0c675 100644
--- a/client/src/main/java/org/ehrbase/client/aql/query/EntityQuery.java
+++ b/aql/src/main/java/org/ehrbase/client/aql/query/EntityQuery.java
@@ -30,6 +30,7 @@
import org.ehrbase.client.aql.field.AqlField;
import org.ehrbase.client.aql.field.AqlFieldImp;
import org.ehrbase.client.aql.field.SelectAqlField;
+import org.ehrbase.client.aql.funtion.Function;
import org.ehrbase.client.aql.orderby.OrderByExpression;
import org.ehrbase.client.aql.parameter.Parameter;
import org.ehrbase.client.aql.record.Record;
@@ -49,6 +50,8 @@ public class EntityQuery implements Query {
private Integer limit;
private Integer offset;
+ private boolean isDistinct = false;
+
protected EntityQuery(ContainmentExpression containmentExpression, SelectAqlField>... fields) {
this(containmentExpression, new HashMap<>(), fields);
}
@@ -76,6 +79,19 @@ protected EntityQuery(
}
private SelectAqlField replace(SelectAqlField> selectAqlField) {
+
+ if (selectAqlField instanceof Function) {
+
+ List> parameters = ((Function) selectAqlField).getParameters();
+ List> replaceList =
+ parameters.stream().map(this::replace).collect(Collectors.toList());
+
+ parameters.clear();
+ parameters.addAll(replaceList);
+
+ return (SelectAqlField) selectAqlField;
+ }
+
if (selectAqlField.getContainment().getTypeName().equals("EHR")) {
return new AqlFieldImp(
selectAqlField.getEntityClass(),
@@ -92,6 +108,11 @@ private SelectAqlField replace(SelectAqlField> selectAqlField) {
public String buildAql() {
StringBuilder sb = new StringBuilder();
sb.append("Select ");
+
+ if (isDistinct) {
+ sb.append("DISTINCT").append(" ");
+ }
+
if (topExpresion != null) {
sb.append(topExpresion.buildAql()).append(" ");
}
@@ -107,10 +128,6 @@ public String buildAql() {
if (where != null) {
sb.append(" where ").append(where.buildAql(ehrContainment));
}
- if (orderByExpression != null) {
- sb.append(" order by ").append(orderByExpression.buildAql(ehrContainment));
- }
-
if (limit != null) {
sb.append(" LIMIT ").append(limit);
}
@@ -118,6 +135,11 @@ public String buildAql() {
if (offset != null) {
sb.append(" OFFSET ").append(offset);
}
+
+ if (orderByExpression != null) {
+ sb.append(" order by ").append(orderByExpression.buildAql(ehrContainment));
+ }
+
return sb.toString();
}
@@ -164,6 +186,11 @@ public EntityQuery top(TopExpresion topExpresion) {
return this;
}
+ public EntityQuery distinct(boolean isDistinct) {
+ this.isDistinct = isDistinct;
+ return this;
+ }
+
public EntityQuery limit(Integer limit) {
this.limit = limit;
return this;
diff --git a/client/src/main/java/org/ehrbase/client/aql/query/NativeQuery.java b/aql/src/main/java/org/ehrbase/client/aql/query/NativeQuery.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/query/NativeQuery.java
rename to aql/src/main/java/org/ehrbase/client/aql/query/NativeQuery.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/query/Query.java b/aql/src/main/java/org/ehrbase/client/aql/query/Query.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/query/Query.java
rename to aql/src/main/java/org/ehrbase/client/aql/query/Query.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/AbstractRecordImp.java b/aql/src/main/java/org/ehrbase/client/aql/record/AbstractRecordImp.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/AbstractRecordImp.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/AbstractRecordImp.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record1.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record1.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record1.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record1.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record10.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record10.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record10.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record10.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record11.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record11.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record11.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record11.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record12.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record12.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record12.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record12.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record13.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record13.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record13.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record13.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record14.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record14.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record14.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record14.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record15.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record15.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record15.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record15.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record16.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record16.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record16.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record16.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record17.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record17.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record17.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record17.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record18.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record18.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record18.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record18.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record19.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record19.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record19.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record19.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record2.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record2.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record2.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record2.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record20.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record20.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record20.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record20.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record21.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record21.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record21.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record21.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record3.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record3.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record3.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record3.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record4.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record4.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record4.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record4.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record5.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record5.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record5.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record5.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record6.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record6.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record6.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record6.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record7.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record7.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record7.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record7.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record8.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record8.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record8.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record8.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/Record9.java b/aql/src/main/java/org/ehrbase/client/aql/record/Record9.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/Record9.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/Record9.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/record/RecordImp.java b/aql/src/main/java/org/ehrbase/client/aql/record/RecordImp.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/record/RecordImp.java
rename to aql/src/main/java/org/ehrbase/client/aql/record/RecordImp.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/top/Direction.java b/aql/src/main/java/org/ehrbase/client/aql/top/Direction.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/top/Direction.java
rename to aql/src/main/java/org/ehrbase/client/aql/top/Direction.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/top/Top.java b/aql/src/main/java/org/ehrbase/client/aql/top/Top.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/top/Top.java
rename to aql/src/main/java/org/ehrbase/client/aql/top/Top.java
diff --git a/client/src/main/java/org/ehrbase/client/aql/top/TopExpresion.java b/aql/src/main/java/org/ehrbase/client/aql/top/TopExpresion.java
similarity index 100%
rename from client/src/main/java/org/ehrbase/client/aql/top/TopExpresion.java
rename to aql/src/main/java/org/ehrbase/client/aql/top/TopExpresion.java
diff --git a/web-template/src/test/java/org/ehrbase/webtemplate/parser/AqlPathTest.java b/aql/src/test/java/org/ehrbase/aql/dto/path/AqlPathTest.java
similarity index 97%
rename from web-template/src/test/java/org/ehrbase/webtemplate/parser/AqlPathTest.java
rename to aql/src/test/java/org/ehrbase/aql/dto/path/AqlPathTest.java
index e9ed68d01..5ddf53bbb 100644
--- a/web-template/src/test/java/org/ehrbase/webtemplate/parser/AqlPathTest.java
+++ b/aql/src/test/java/org/ehrbase/aql/dto/path/AqlPathTest.java
@@ -15,15 +15,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.ehrbase.webtemplate.parser;
+package org.ehrbase.aql.dto.path;
import static org.assertj.core.api.Assertions.assertThat;
+import java.util.Arrays;
+import java.util.stream.Collectors;
import org.junit.Assert;
import org.junit.Test;
public class AqlPathTest {
+ @Test
+ public void testSplit() {
+
+ String cut = "at001, name/value = 'dfd' or name/value= 'fdf'";
+
+ CharSequence[] ands = AqlPath.split(cut, null, true, "and", "or", ",");
+
+ assertThat(Arrays.stream(ands).map(CharSequence::toString).collect(Collectors.toList()))
+ .containsExactly("at001", ",", " name/value = 'dfd' ", "or", " name/value= 'fdf'");
+ }
+
@Test
public void testParse() {
String path = "/other_context[at0001]/items[at0006]";
@@ -407,7 +420,7 @@ public void testParseSpace() {
AqlPath path;
path = AqlPath.parse(
- "/content[openEHR-EHR-OBSERVATION.laboratory_test_result.v1, 'Einsenderstandort']/protocol[at0004]/items[at0094]/items[openEHR-EHR-CLUSTER.location.v1]");
+ "/content[openEHR-EHR-OBSERVATION.laboratory_test_result.v1,'Einsenderstandort']/protocol[at0004]/items[at0094]/items[openEHR-EHR-CLUSTER.location.v1]");
assertThat(path.format(AqlPath.OtherPredicatesFormat.FULL, true))
.isEqualTo(
"/content[openEHR-EHR-OBSERVATION.laboratory_test_result.v1 and name/value='Einsenderstandort']/protocol[at0004]/items[at0094]/items[openEHR-EHR-CLUSTER.location.v1]");
diff --git a/aql/src/test/java/org/ehrbase/aql/dto/path/predicate/PredicateHelperTest.java b/aql/src/test/java/org/ehrbase/aql/dto/path/predicate/PredicateHelperTest.java
new file mode 100644
index 000000000..141a5134b
--- /dev/null
+++ b/aql/src/test/java/org/ehrbase/aql/dto/path/predicate/PredicateHelperTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School.
+ *
+ * This file is part of project openEHR_SDK
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ehrbase.aql.dto.path.predicate;
+
+import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
+
+import org.ehrbase.aql.dto.path.AqlPath;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+/**
+ * @author Stefan Spiska
+ */
+class PredicateHelperTest {
+
+ private enum TestCase {
+ // T1_FULL("archetype_node_id=at001 and name/value='name1' or archetype_node_id=at001 and name/value='name2'",
+ // AqlPath.OtherPredicatesFormat.FULL,"archetype_node_id=at001 and name/value='name1' or archetype_node_id=at001
+ // and name/value='name2'"),
+ // T1_SHORT("archetype_node_id=at001 and name/value='name1' or archetype_node_id=at001 and name/value='name2'",
+ // AqlPath.OtherPredicatesFormat.SHORTED,"archetype_node_id=at001 and name/value='name1' or
+ // archetype_node_id=at001 and name/value='name2'"),
+ // T1_NODE("archetype_node_id=at001 and name/value='name1' or archetype_node_id=at001 and name/value='name2'",
+ // AqlPath.OtherPredicatesFormat.NONE,"at001, 'name1' or at001, 'name2'"),
+ T2_FULL(
+ "at001,'name1' and path/value='abc'",
+ AqlPath.OtherPredicatesFormat.FULL,
+ "at001 and name/value='name1' and path/value='abc'"),
+ T2_SHORT(
+ "at001,'name1' and path/value='abc'",
+ AqlPath.OtherPredicatesFormat.SHORTED,
+ "at001,'name1' and path/value='abc'"),
+ T2_NONE("at001,'name1' and path/value='abc'", AqlPath.OtherPredicatesFormat.NONE, "at001"),
+ T3_FULL(
+ "name/value='name1' and archetype_node_id=at001",
+ AqlPath.OtherPredicatesFormat.FULL,
+ "at001 and name/value='name1'"),
+ T3_SHORT(
+ "name/value='name1' and archetype_node_id=at001",
+ AqlPath.OtherPredicatesFormat.SHORTED,
+ "at001,'name1'"),
+ T3_NONE("name/value='name1' and archetype_node_id=at001", AqlPath.OtherPredicatesFormat.NONE, "at001");
+
+ final String input;
+ final AqlPath.OtherPredicatesFormat format;
+ final String expected;
+
+ TestCase(String input, AqlPath.OtherPredicatesFormat format, String expected) {
+ this.input = input;
+ this.format = format;
+ this.expected = expected;
+ }
+ }
+
+ @ParameterizedTest
+ @EnumSource(TestCase.class)
+ void roundTrip(TestCase testCase) {
+
+ PredicateDto predicateDto = PredicateHelper.buildPredicate(testCase.input);
+
+ StringBuilder sb = new StringBuilder();
+ PredicateHelper.format(sb, predicateDto, testCase.format);
+
+ assertThat(sb).hasToString(testCase.expected);
+ }
+}
diff --git a/aql/src/test/java/org/ehrbase/aql/parser/AqlToDtoParserTest.java b/aql/src/test/java/org/ehrbase/aql/parser/AqlToDtoParserTest.java
index 1317180cd..2bc86c549 100644
--- a/aql/src/test/java/org/ehrbase/aql/parser/AqlToDtoParserTest.java
+++ b/aql/src/test/java/org/ehrbase/aql/parser/AqlToDtoParserTest.java
@@ -37,12 +37,12 @@
import org.ehrbase.aql.dto.containment.ContainmentExpresionDto;
import org.ehrbase.aql.dto.containment.ContainmentLogicalOperator;
import org.ehrbase.aql.dto.select.SelectFieldDto;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
-public class AqlToDtoParserTest {
+class AqlToDtoParserTest {
@Test
- public void parse() {
+ void parse() {
String aql =
"Select c/context/other_context[at0001]/items[at0002]/value/value as Bericht_ID__value, d/ehr_id/value as ehr_id from EHR d contains COMPOSITION c[openEHR-EHR-COMPOSITION.report.v1]";
@@ -50,7 +50,7 @@ public void parse() {
}
@Test
- public void parseDoubleAlias() {
+ void parseDoubleAlias() {
String aql =
"Select e/ehr_id/value ,c0 as F1 from EHR e contains COMPOSITION c0[openEHR-EHR-COMPOSITION.report.v1]";
@@ -60,7 +60,7 @@ public void parseDoubleAlias() {
}
@Test
- public void parseDoubleAlias2() {
+ void parseDoubleAlias2() {
String aql =
"Select c0 as F1, e/ehr_id/value from EHR e contains COMPOSITION c0[openEHR-EHR-COMPOSITION.report.v1]";
@@ -70,14 +70,14 @@ public void parseDoubleAlias2() {
}
@Test
- public void parseObservation() {
+ void parseObservation() {
String aql = "SELECT o FROM EHR e CONTAINS OBSERVATION o";
testAql(aql, "Select o as F1 from EHR e contains OBSERVATION o");
}
@Test
- public void parseObservation2() {
+ void parseObservation2() {
String aql =
"Select e/ehr_id/value as F1, o/data[at0001]/events[at0002]/data[at0003]/items[at0022]/items[at0005]/value/value as F2, o/data[at0001]/events[at0002]/data[at0003]/items[at0022]/items[at0004]/value/value as F3 from EHR e contains (COMPOSITION c0 and SECTION s4[openEHR-EHR-SECTION.adhoc.v1] contains OBSERVATION o[openEHR-EHR-OBSERVATION.symptom_sign_screening.v0]) where (e/ehr_id/value matches {'47dc21a2-7076-4a57-89dc-bd83729ed52f'} and c0/archetype_details/template_id/value matches {'Corona_Anamnese'})";
@@ -85,7 +85,7 @@ public void parseObservation2() {
}
@Test
- public void parseMultiWhere() {
+ void parseMultiWhere() {
String aql =
"Select c0 as openEHR_EHR_COMPOSITION_self_monitoring_v0, c1 as openEHR_EHR_COMPOSITION_report_v1 from EHR e contains (COMPOSITION c0[openEHR-EHR-COMPOSITION.self_monitoring.v0] and COMPOSITION c1[openEHR-EHR-COMPOSITION.report.v1]) where (e/ehr_id/value matches {'b3a40b41-36e1-4802-8748-062d4000aaae'} and c0/archetype_details/template_id/value matches {'Corona_Anamnese'} and c1/archetype_details/template_id/value matches {'Corona_Anamnese'})";
@@ -93,7 +93,7 @@ public void parseMultiWhere() {
}
@Test
- public void parseMultiMixed() {
+ void parseMultiMixed() {
String aql =
"Select c0 as F1, e/ehr_id/value as F2 from EHR e contains COMPOSITION c0[openEHR-EHR-COMPOSITION.report.v1] where (e/ehr_id/value = $ehrid or (e/ehr_id/value = $ehrid2 and e/ehr_id/value = $ehrid3))";
@@ -101,7 +101,7 @@ public void parseMultiMixed() {
}
@Test
- public void parseMatches() {
+ void parseMatches() {
String aql =
"Select c/context/other_context[at0001]/items[at0002]/value/value as Bericht_ID__value, d/ehr_id/value as ehr_id from EHR d contains COMPOSITION c[openEHR-EHR-COMPOSITION.report.v1] where d/ehr_id/value matches {'f4da8646-8e36-4d9d-869c-af9dce5935c7','61861e76-1606-48c9-adcf-49ebbb2c6bbd'}";
@@ -109,7 +109,7 @@ public void parseMatches() {
}
@Test
- public void addMatches() {
+ void addMatches() {
String aql =
"Select o0/data[at0001]/events[at0002]/data[at0003]/items[at0004]/value/magnitude as Systolic__magnitude, e/ehr_id/value as ehr_id from EHR e contains OBSERVATION o0[openEHR-EHR-OBSERVATION.sample_blood_pressure.v1] where (o0/data[at0001]/events[at0002]/data[at0003]/items[at0004]/value/magnitude >= $magnitude and o0/data[at0001]/events[at0002]/data[at0003]/items[at0004]/value/magnitude < 1.1)";
@@ -150,14 +150,14 @@ public void addMatches() {
}
@Test
- public void parseWithoutContains() {
+ void parseWithoutContains() {
String aql = "SELECT e/ehr_id/value FROM EHR e";
testAql(aql, "Select e/ehr_id/value as F1 from EHR e");
}
@Test
- public void parseLimitOffset() {
+ void parseLimitOffset() {
String aql =
"Select c/context/other_context[at0001]/items[at0002]/value/value as Bericht_ID__value, d/ehr_id/value as ehr_id from EHR d contains COMPOSITION c[openEHR-EHR-COMPOSITION.report.v1] LIMIT 5 OFFSET 1";
@@ -165,7 +165,7 @@ public void parseLimitOffset() {
}
@Test
- public void parseError() {
+ void parseError() {
String aql =
"Select c/context/other_context[at0001]/items[at0002]/value/value as Bericht_ID__value, d/ehr_id/value as ehr_id EHR d contains COMPOSITION c[openEHR-EHR-COMPOSITION.report.v1]";
@@ -180,7 +180,7 @@ public void parseError() {
}
@Test
- public void parseWhere() {
+ void parseWhere() {
String aql =
"Select o0/data[at0001]/events[at0002]/data[at0003]/items[at0004]/value/magnitude as Systolic__magnitude, e/ehr_id/value as ehr_id from EHR e contains OBSERVATION o0[openEHR-EHR-OBSERVATION.sample_blood_pressure.v1] where (o0/data[at0001]/events[at0002]/data[at0003]/items[at0004]/value/magnitude >= $magnitude and o0/data[at0001]/events[at0002]/data[at0003]/items[at0004]/value/magnitude < 1.1)";
@@ -188,7 +188,7 @@ public void parseWhere() {
}
@Test
- public void parseTop() {
+ void parseTop() {
String aql =
"Select TOP 10 FORWARD o0/data[at0001]/events[at0002]/data[at0003]/items[at0004]/value/magnitude as Systolic__magnitude, e/ehr_id/value as ehr_id from EHR e contains OBSERVATION o0[openEHR-EHR-OBSERVATION.sample_blood_pressure.v1] where (o0/data[at0001]/events[at0002]/data[at0003]/items[at0004]/value/magnitude >= $magnitude and o0/data[at0001]/events[at0002]/data[at0003]/items[at0004]/value/magnitude < 1.1)";
@@ -201,7 +201,7 @@ public void parseTop() {
}
@Test
- public void parseOrderBy() {
+ void parseOrderBy() {
String aqlTwoOrderBy =
"Select e/ehr_id/value as ehr_id from EHR e contains OBSERVATION o0[openEHR-EHR-OBSERVATION.sample_blood_pressure.v1]"
@@ -232,7 +232,7 @@ public void parseOrderBy() {
+ " order by o0/data[at0001]/events[at0002]/data[at0003]/items[at0004]/value/magnitude DESCENDING, e/ehr_id/value ASCENDING");
}
- public void testAql(String aql, String expected) {
+ void testAql(String aql, String expected) {
AqlToDtoParser cut = new AqlToDtoParser();
AqlDto actual = cut.parse(aql);
@@ -244,7 +244,7 @@ public void testAql(String aql, String expected) {
}
@Test
- public void parseContains() {
+ void parseContains() {
String aql =
"Select c0/context/other_context[at0001]/items[at0002]/value/value as Bericht_ID__value from EHR e contains COMPOSITION c0[openEHR-EHR-COMPOSITION.report.v1] contains OBSERVATION o0[openEHR-EHR-OBSERVATION.sample_blood_pressure.v1]";
@@ -252,7 +252,7 @@ public void parseContains() {
}
@Test
- public void parseContainsLogical() {
+ void parseContainsLogical() {
String aql =
"Select c0/context/other_context[at0001]/items[at0002]/value/value as Bezeichnung_des_Symptoms_oder_Anzeichens___value, o3/data[at0001]/events[at0002]/data[at0042]/items[at0055]/value/value as Kommentar__value from EHR e contains COMPOSITION c0[openEHR-EHR-COMPOSITION.report.v1] contains (OBSERVATION o1[openEHR-EHR-OBSERVATION.story.v1] and OBSERVATION o2[openEHR-EHR-OBSERVATION.symptom_sign_screening.v0] or OBSERVATION o3[openEHR-EHR-OBSERVATION.exposure_assessment.v0])";
@@ -262,7 +262,7 @@ public void parseContainsLogical() {
}
@Test
- public void parseContainsLogical2() {
+ void parseContainsLogical2() {
String aql =
"Select c0/context/other_context[at0001]/items[at0002]/value/value as Bezeichnung_des_Symptoms_oder_Anzeichens___value, o3/data[at0001]/events[at0002]/data[at0042]/items[at0055]/value/value as Kommentar__value from EHR e contains COMPOSITION c0[openEHR-EHR-COMPOSITION.report.v1] contains (OBSERVATION o1[openEHR-EHR-OBSERVATION.story.v1] or OBSERVATION o2[openEHR-EHR-OBSERVATION.symptom_sign_screening.v0] and OBSERVATION o3[openEHR-EHR-OBSERVATION.exposure_assessment.v0])";
@@ -272,7 +272,7 @@ public void parseContainsLogical2() {
}
@Test
- public void parseContainsLogical3() {
+ void parseContainsLogical3() {
String aql =
"Select c0/context/other_context[at0001]/items[at0002]/value/value as Bezeichnung_des_Symptoms_oder_Anzeichens___value, o3/data[at0001]/events[at0002]/data[at0042]/items[at0055]/value/value as Kommentar__value from EHR e contains COMPOSITION c0[openEHR-EHR-COMPOSITION.report.v1] contains ((OBSERVATION o1[openEHR-EHR-OBSERVATION.story.v1] or OBSERVATION o2[openEHR-EHR-OBSERVATION.symptom_sign_screening.v0]) and OBSERVATION o3[openEHR-EHR-OBSERVATION.exposure_assessment.v0])";
@@ -281,7 +281,7 @@ public void parseContainsLogical3() {
"openEHR-EHR-COMPOSITION.report.v1 --> ((openEHR-EHR-OBSERVATION.story.v1 OR openEHR-EHR-OBSERVATION.symptom_sign_screening.v0) AND openEHR-EHR-OBSERVATION.exposure_assessment.v0)");
}
- public void testContains(String aql, String s) {
+ void testContains(String aql, String s) {
AqlToDtoParser cut = new AqlToDtoParser();
AqlDto actual = cut.parse(aql);
@@ -292,7 +292,7 @@ public void testContains(String aql, String s) {
}
@Test
- public void parseContainsLogical4() {
+ void parseContainsLogical4() {
String aql =
"Select c0/context/other_context[at0001]/items[at0002]/value/value as Bezeichnung_des_Symptoms_oder_Anzeichens___value, o3/data[at0001]/events[at0002]/data[at0042]/items[at0055]/value/value as Kommentar__value from EHR e contains COMPOSITION c0[openEHR-EHR-COMPOSITION.report.v1] contains (((OBSERVATION o1[openEHR-EHR-OBSERVATION.story.v1] contains CLUSTER) or OBSERVATION o2[openEHR-EHR-OBSERVATION.symptom_sign_screening.v0]) and OBSERVATION o3[openEHR-EHR-OBSERVATION.exposure_assessment.v0])";
@@ -301,7 +301,7 @@ public void parseContainsLogical4() {
"openEHR-EHR-COMPOSITION.report.v1 --> ((openEHR-EHR-OBSERVATION.story.v1 --> CLUSTER OR openEHR-EHR-OBSERVATION.symptom_sign_screening.v0) AND openEHR-EHR-OBSERVATION.exposure_assessment.v0)");
}
- private String render(ContainmentExpresionDto containmentExpresion) {
+ String render(ContainmentExpresionDto containmentExpresion) {
StringBuilder sb = new StringBuilder();
if (containmentExpresion instanceof ContainmentDto) {
sb.append(((ContainmentDto) containmentExpresion).getArchetypeId());
@@ -326,7 +326,7 @@ private String render(ContainmentExpresionDto containmentExpresion) {
}
@Test
- public void parseAqlLimitOffset() {
+ void parseAqlLimitOffset() {
var parser = new AqlToDtoParser();
var query1 = "select e/ehr_id/value "
@@ -377,7 +377,7 @@ public void parseAqlLimitOffset() {
}
@Test
- public void parseWhereClauseWithBoolean() {
+ void parseWhereClauseWithBoolean() {
String aql;
aql =
@@ -418,20 +418,21 @@ public void parseWhereClauseWithBoolean() {
}
@Test
- public void orderByAndLimitOrder() {
+ void orderByAndLimitOrder() {
var aql1 = "Select "
+ "c/name/value as Name, c/context/start_time as date_time, c/composer/name as Composer "
+ "from EHR e contains COMPOSITION c "
+ "order by c/context/start_time ASCENDING "
+ "LIMIT 10 OFFSET 10";
- testAql(aql1, aql1);
var aql2 = "Select "
+ "c/name/value as Name, c/context/start_time as date_time, c/composer/name as Composer "
+ "from EHR e contains COMPOSITION c "
+ "LIMIT 10 OFFSET 10 "
+ "order by c/context/start_time ASCENDING";
- testAql(aql2, aql1);
+ testAql(aql1, aql2);
+
+ testAql(aql2, aql2);
var aql3 = "Select "
+ "c/name/value as Name, c/context/start_time as date_time, c/composer/name as Composer "
diff --git a/aql/src/test/java/org/ehrbase/aql/roundtriptest/AqlExpressionTestCase.java b/aql/src/test/java/org/ehrbase/aql/roundtriptest/AqlExpressionTestCase.java
new file mode 100644
index 000000000..f6063cb49
--- /dev/null
+++ b/aql/src/test/java/org/ehrbase/aql/roundtriptest/AqlExpressionTestCase.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School.
+ *
+ * This file is part of project openEHR_SDK
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ehrbase.aql.roundtriptest;
+
+/**
+ * @author Stefan Spiska
+ * @see AqlExpressionTest
+ * in ehrbase
+ */
+public enum AqlExpressionTestCase implements AqlTestCase {
+ DUMP(
+ "SELECT o/data[at0002]/events[at0003] AS systolic "
+ + "FROM EHR [ehr_id/value='1234'] "
+ + "CONTAINS COMPOSITION c [openEHR-EHR-COMPOSITION.encounter.v1] "
+ + "CONTAINS OBSERVATION o [openEHR-EHR-OBSERVATION.blood_pressure.v1]"
+ + "WHERE o/data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/value > 140",
+ "Select o/data[at0002]/events[at0003] as systolic "
+ + "from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.encounter.v1] "
+ + "contains OBSERVATION o[openEHR-EHR-OBSERVATION.blood_pressure.v1] "
+ + "where (o/data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/value > 140 "
+ + "and e/ehr_id/value = '1234')",
+ "testDump"),
+ WHERE_EXPRESSION_WITH_PARENTHESIS(
+ "select e/ehr_id, a/data[at0002]/events[at0003]/data[at0001]/items[at0004]/value/magnitude, a_a/data[at0002]/events[at0003]/time/value "
+ + "from EHR e "
+ + "contains COMPOSITION a "
+ + "contains OBSERVATION a_a[openEHR-EHR-OBSERVATION.body_temperature.v1] "
+ + "where a_a/data[at0002]/events[at0003]/data[at0001]/items[at0004]/value/magnitude>38 "
+ + "AND a_a/data[at0002]/events[at0003]/data[at0001]/items[at0004]/value/units = '°C' "
+ + "AND e/ehr_id/value MATCHES {"
+ + "'849bf097-bd16-44fc-a394-10676284a012',"
+ + "'34b2e263-00eb-40b8-88f1-823c87096457'}"
+ + "OR (a_a/data[at0002]/events[at0003]/data[at0001]/items[at0004]/value/units = '°C' AND a_a/data[at0002]/events[at0003]/data[at0001]/items[at0004]/value/units = '°C')",
+ "Select e/ehr_id as F1, a/data[at0002]/events[at0003]/data[at0001]/items[at0004]/value/magnitude as F2, a_a/data[at0002]/events[at0003]/time/value as F3 "
+ + "from EHR e "
+ + "contains COMPOSITION a "
+ + "contains OBSERVATION a_a[openEHR-EHR-OBSERVATION.body_temperature.v1] "
+ + "where (("
+ + "a_a/data[at0002]/events[at0003]/data[at0001]/items[at0004]/value/magnitude > 38 "
+ + "and a_a/data[at0002]/events[at0003]/data[at0001]/items[at0004]/value/units = '°C' "
+ + "and e/ehr_id/value matches {'849bf097-bd16-44fc-a394-10676284a012','34b2e263-00eb-40b8-88f1-823c87096457'}) "
+ + "or (a_a/data[at0002]/events[at0003]/data[at0001]/items[at0004]/value/units = '°C' and a_a/data[at0002]/events[at0003]/data[at0001]/items[at0004]/value/units = '°C'))",
+ "testWhereExpressionWithParenthesis");
+
+ private final String testAql;
+ private final String expectedAql;
+
+ AqlExpressionTestCase(String testAql, String expectedAql, String description) {
+ this.testAql = testAql;
+ this.expectedAql = expectedAql;
+ }
+
+ @Override
+ public String getTestAql() {
+ return testAql;
+ }
+
+ @Override
+ public String getExpectedAql() {
+ return expectedAql;
+ }
+}
diff --git a/aql/src/test/java/org/ehrbase/aql/roundtriptest/AqlExpressionWithParameterTestCase.java b/aql/src/test/java/org/ehrbase/aql/roundtriptest/AqlExpressionWithParameterTestCase.java
new file mode 100644
index 000000000..bc47e3dcb
--- /dev/null
+++ b/aql/src/test/java/org/ehrbase/aql/roundtriptest/AqlExpressionWithParameterTestCase.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School.
+ *
+ * This file is part of project openEHR_SDK
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ehrbase.aql.roundtriptest;
+
+/**
+ * @author Stefan Spiska
+ * @see AqlExpressionWithParametersTest in ehrbase
+ */
+public enum AqlExpressionWithParameterTestCase implements AqlTestCase {
+ DUMP(
+ "select o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0005, $nameValue1]/value/magnitude as diastolic, o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0004, $nameValue2]/value/magnitude as systolic "
+ + "from EHR e[ehr_id/value=$ehrId] "
+ + "contains COMPOSITION a "
+ + "contains OBSERVATION o_bp[openEHR-EHR-OBSERVATION.blood_pressure.v1] "
+ + "where o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0005]/value/magnitude < $max_value "
+ + "and o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/magnitude > $another_value",
+ "Select o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0005,$nameValue1]/value/magnitude as diastolic, o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0004,$nameValue2]/value/magnitude as systolic "
+ + "from EHR e "
+ + "contains COMPOSITION a "
+ + "contains OBSERVATION o_bp[openEHR-EHR-OBSERVATION.blood_pressure.v1] "
+ + "where (o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0005]/value/magnitude < $max_value "
+ + "and o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/magnitude > $another_value "
+ + "and e/ehr_id/value = $ehrId)",
+ "testDump"),
+ DUMP2(
+ "select o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0005, $nameValue1]/value/magnitude as diastolic, o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0004, $nameValue2]/value/magnitude as systolic "
+ + "from EHR e[ehr_id/value=$ehrId and system_id/value != '123'] "
+ + "contains COMPOSITION a "
+ + "contains OBSERVATION o_bp[openEHR-EHR-OBSERVATION.blood_pressure.v1] "
+ + "where o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0005]/value/magnitude < $max_value "
+ + "and o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/magnitude > $another_value",
+ "Select o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0005,$nameValue1]/value/magnitude as diastolic, o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0004,$nameValue2]/value/magnitude as systolic "
+ + "from EHR e "
+ + "contains COMPOSITION a "
+ + "contains OBSERVATION o_bp[openEHR-EHR-OBSERVATION.blood_pressure.v1] "
+ + "where (o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0005]/value/magnitude < $max_value "
+ + "and o_bp/data[at0001]/events[at0006]/data[at0003]/items[at0004]/value/magnitude > $another_value "
+ + "and (e/ehr_id/value = $ehrId and e/system_id/value != '123'))",
+ "testDump");
+
+ private final String testAql;
+ private final String expectedAql;
+
+ AqlExpressionWithParameterTestCase(String testAql, String expectedAql, String description) {
+ this.testAql = testAql;
+ this.expectedAql = expectedAql;
+ }
+
+ @Override
+ public String getTestAql() {
+ return testAql;
+ }
+
+ @Override
+ public String getExpectedAql() {
+ return expectedAql;
+ }
+}
diff --git a/aql/src/test/java/org/ehrbase/aql/roundtriptest/AqlRoundTripTest.java b/aql/src/test/java/org/ehrbase/aql/roundtriptest/AqlRoundTripTest.java
new file mode 100644
index 000000000..1a6885f06
--- /dev/null
+++ b/aql/src/test/java/org/ehrbase/aql/roundtriptest/AqlRoundTripTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School.
+ *
+ * This file is part of project openEHR_SDK
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ehrbase.aql.roundtriptest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.ehrbase.aql.binder.AqlBinder;
+import org.ehrbase.aql.dto.AqlDto;
+import org.ehrbase.aql.parser.AqlToDtoParser;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
+
+/**
+ * @author Stefan Spiska
+ */
+class AqlRoundTripTest {
+
+ @ParameterizedTest
+ @EnumSource(AqlExpressionTestCase.class)
+ void testAqlExpressionTest(AqlExpressionTestCase testData) {
+
+ assertRoundTrip(testData);
+ }
+
+ @ParameterizedTest
+ @EnumSource(AqlExpressionWithParameterTestCase.class)
+ void testAqlExpressionWithParameterTest(AqlExpressionWithParameterTestCase testData) {
+
+ assertRoundTrip(testData);
+ }
+
+ @ParameterizedTest
+ @EnumSource(UCTestData.class)
+ void testUC(UCTestData testData) {
+
+ assertRoundTrip(testData);
+ }
+
+ @ParameterizedTest
+ @EnumSource(PerformanceTestCase.class)
+ void testPerformanceQuery(PerformanceTestCase testData) {
+
+ assertRoundTrip(testData);
+ }
+
+ void assertRoundTrip(AqlTestCase testData) {
+ AqlToDtoParser cut = new AqlToDtoParser();
+ AqlDto actual = cut.parse(testData.getTestAql());
+
+ assertThat(actual).isNotNull();
+
+ String actualAql = new AqlBinder().bind(actual).getLeft().buildAql();
+
+ assertThat(actualAql).isEqualTo(testData.getExpectedAql());
+ }
+}
diff --git a/aql/src/test/java/org/ehrbase/aql/roundtriptest/AqlTestCase.java b/aql/src/test/java/org/ehrbase/aql/roundtriptest/AqlTestCase.java
new file mode 100644
index 000000000..d0f74544a
--- /dev/null
+++ b/aql/src/test/java/org/ehrbase/aql/roundtriptest/AqlTestCase.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School.
+ *
+ * This file is part of project openEHR_SDK
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ehrbase.aql.roundtriptest;
+
+/**
+ * @author Stefan Spiska
+ */
+public interface AqlTestCase {
+
+ String getTestAql();
+
+ String getExpectedAql();
+}
diff --git a/aql/src/test/java/org/ehrbase/aql/roundtriptest/PerformanceTestCase.java b/aql/src/test/java/org/ehrbase/aql/roundtriptest/PerformanceTestCase.java
new file mode 100644
index 000000000..488f09056
--- /dev/null
+++ b/aql/src/test/java/org/ehrbase/aql/roundtriptest/PerformanceTestCase.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School.
+ *
+ * This file is part of project openEHR_SDK
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ehrbase.aql.roundtriptest;
+
+/**
+ * @author Stefan Spiska
+ */
+public enum PerformanceTestCase implements AqlTestCase {
+ QUERY_1(
+ "SELECT c/uid/value as composition_id, c/context/start_time/value as start_time, o/data[at0002]/events[at0003]/data[at0001]/items[at0004]/value/magnitude as body_temperature"
+ + " FROM EHR e"
+ + " CONTAINS COMPOSITION c"
+ + " CONTAINS OBSERVATION o [openEHR-EHR-OBSERVATION.body_temperature.v2]"
+ + " WHERE e/ehr_id/value = '96940df3-c979-4d96-9a51-34b6c5222777'"
+ + " AND o/data[at0002]/events[at0003]/data[at0001]/items[at0004]/value/magnitude > 37.5"
+ + " LIMIT 10 OFFSET 10"
+ + " ORDER BY c/context/start_time/value DESC",
+ "Select c/uid/value as composition_id, c/context/start_time/value as start_time, o/data[at0002]/events[at0003]/data[at0001]/items[at0004]/value/magnitude as body_temperature "
+ + "from EHR e "
+ + "contains COMPOSITION c "
+ + "contains OBSERVATION o[openEHR-EHR-OBSERVATION.body_temperature.v2] "
+ + "where (e/ehr_id/value = '96940df3-c979-4d96-9a51-34b6c5222777' "
+ + "and o/data[at0002]/events[at0003]/data[at0001]/items[at0004]/value/magnitude > 37.5) "
+ + "LIMIT 10 OFFSET 10 "
+ + "order by c/context/start_time/value DESCENDING",
+ "Get second page of measurements with page size 10 which indicated high temperature and their time"),
+ QUERY_2(
+ "SELECT c as composition"
+ + " FROM EHR e"
+ + " CONTAINS COMPOSITION c[openEHR-EHR-COMPOSITION.report.v1]"
+ + " WHERE e/ehr_id/value = '96940df3-c979-4d96-9a51-34b6c5222777' "
+ + " AND c/archetype_details/template_id/value = 'Corona_Anamnese'"
+ + " LIMIT 1"
+ + " ORDER BY c/context/start_time/value DESC",
+ "Select c as composition " + "from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.report.v1] "
+ + "where (e/ehr_id/value = '96940df3-c979-4d96-9a51-34b6c5222777' "
+ + "and c/archetype_details/template_id/value = 'Corona_Anamnese') "
+ + "LIMIT 1 "
+ + "order by c/context/start_time/value DESCENDING",
+ "Get latest 'Corona_Anamnese' document for a specific patient"),
+ QUERY_3(
+ "SELECT DISTINCT e/ehr_id/value "
+ + "FROM EHR e "
+ + "CONTAINS COMPOSITION c "
+ + "CONTAINS EVALUATION ev[openEHR-EHR-EVALUATION.problem_diagnosis.v1] "
+ + "WHERE c/context/health_care_facility/name/value='hcf0' "
+ + " AND ev/data[at0001]/items[at0002]/value/value='Problem/Diagnosis name 10'",
+ "Select DISTINCT e/ehr_id/value as F1 " + "from EHR e "
+ + "contains COMPOSITION c "
+ + "contains EVALUATION ev[openEHR-EHR-EVALUATION.problem_diagnosis.v1] "
+ + "where (c/context/health_care_facility/name/value = 'hcf0' "
+ + "and ev/data[at0001]/items[at0002]/value/value = 'Problem/Diagnosis name 10')",
+ "Get latest 'Corona_Anamnese' document for a specific patient");
+
+ private final String testAql;
+ private final String expectedAql;
+
+ PerformanceTestCase(String testAql, String expectedAql, String description) {
+ this.testAql = testAql;
+ this.expectedAql = expectedAql;
+ }
+
+ @Override
+ public String getTestAql() {
+ return testAql;
+ }
+
+ @Override
+ public String getExpectedAql() {
+ return expectedAql;
+ }
+}
diff --git a/aql/src/test/java/org/ehrbase/aql/roundtriptest/UCTestData.java b/aql/src/test/java/org/ehrbase/aql/roundtriptest/UCTestData.java
new file mode 100644
index 000000000..1052448d9
--- /dev/null
+++ b/aql/src/test/java/org/ehrbase/aql/roundtriptest/UCTestData.java
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2022 vitasystems GmbH and Hannover Medical School.
+ *
+ * This file is part of project openEHR_SDK
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ehrbase.aql.roundtriptest;
+
+/**
+ * @author Stefan Spiska
+ * @see UC*
+ * in ehrbase
+ */
+public enum UCTestData implements AqlTestCase {
+ UC1("select e/ehr_id/value from EHR e", "Select e/ehr_id/value as F1 from EHR e", "UC1"),
+ UC2(
+ "select e/ehr_id/value from EHR e " + "where e/ehr_id/value = '30580007'",
+ "Select e/ehr_id/value as F1 from EHR e " + "where e/ehr_id/value = '30580007'",
+ "UC1"),
+ UC3(
+ "select c/composer/name from EHR e " + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1]",
+ "Select c/composer/name as F1 from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1]",
+ "UC3"),
+ UC4(
+ "select c/composer/name from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "order by c/context/start_time/value DESC",
+ "Select c/composer/name as F1 from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "order by c/context/start_time/value DESCENDING",
+ "UC4"),
+ UC5(
+ "select a/description[at0001]/items[at0002]/value/value from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]",
+ "Select a/description[at0001]/items[at0002]/value/value as F1 from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]",
+ "UC5"),
+ UC6(
+ "select a/description[at0001]/items[at0002]/value/value as description from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]"
+ + "order by description ASC",
+ "Select a/description[at0001]/items[at0002]/value/value as description from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]"
+ + " order by a/description[at0001]/items[at0002]/value/value ASCENDING",
+ "UC6"),
+ UC7(
+ "select a/description[at0001]/items[at0002]/value/value from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]"
+ + "where a/description[at0001]/items[at0002]/value/value = 'Hepatitis A'",
+ "Select a/description[at0001]/items[at0002]/value/value as F1 from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]"
+ + " where a/description[at0001]/items[at0002]/value/value = 'Hepatitis A'",
+ "UC7"),
+ UC8(
+ "select c from EHR e contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1]",
+ "Select c as F1 from EHR e contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1]",
+ "UC8"),
+ UC9(
+ "select a from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]",
+ "Select a as F1 from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]",
+ "UC9"),
+ UC10(
+ "select e/ehr_id/value from EHR e LIMIT 10 OFFSET 5",
+ "Select e/ehr_id/value as F1 from EHR e LIMIT 10 OFFSET 5",
+ "UC10"),
+ UC11("select TOP 5 e/ehr_id/value from EHR e", "Select TOP 5 FORWARD e/ehr_id/value as F1 from EHR e", "UC11"),
+ UC12(
+ "select e/ehr_id/value from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]"
+ + "where a/description[at0001]/items[at0002]/value/value matches {'Hepatitis A','Hepatitis B'} ",
+ "Select e/ehr_id/value as F1 from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]"
+ + " where a/description[at0001]/items[at0002]/value/value matches {'Hepatitis A','Hepatitis B'}",
+ "UC12"),
+ UC13(
+ "select"
+ + " count (d/description[at0001]/items[at0004]/value/magnitude) as count_magnitude"
+ + " from EHR e"
+ + " contains COMPOSITION"
+ + " contains ACTION d[openEHR-EHR-ACTION.immunisation_procedure.v1]",
+ "Select"
+ + " COUNT(d/description[at0001]/items[at0004]/value/magnitude) as count_magnitude"
+ + " from EHR e"
+ + " contains COMPOSITION c0"
+ + " contains ACTION d[openEHR-EHR-ACTION.immunisation_procedure.v1]",
+ "UC13"),
+ UC14(
+ "select c/composer/name from EHR e " + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.unknown.v1]",
+ "Select c/composer/name as F1 from EHR e " + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.unknown.v1]",
+ "UC14"),
+
+ UC15(
+ "select e/ehr_status/other_details from EHR e[ehr_id/value='2a3b673f-d1b1-44c5-9e38-dcadf67ff2fc']",
+ "Select e/ehr_status/other_details as F1 from EHR e where e/ehr_id/value = '2a3b673f-d1b1-44c5-9e38-dcadf67ff2fc'",
+ "UC15"),
+
+ UC16(
+ "select a/description[at0001]/items[openEHR-EHR-CLUSTER.test_all_types.v1]/items[at0001]/items[at0002]/items[at0003]/value/value "
+ + "from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]"
+ + "WHERE a/description[at0001]/items[at0001]/items[at0002]/items[at0003]/value/value = true "
+ + "OR "
+ + "("
+ + "a/description[at0001]/items[at0001]/items[at0002]/items[at0003]/value/value = true "
+ + "AND"
+ + " a/description[at0001]/items[at0001]/items[at0002]/items[at0003]/value/value = true"
+ + ")",
+ "Select a/description[at0001]/items[openEHR-EHR-CLUSTER.test_all_types.v1]/items[at0001]/items[at0002]/items[at0003]/value/value as F1 "
+ + "from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]"
+ + " where (a/description[at0001]/items[at0001]/items[at0002]/items[at0003]/value/value = true "
+ + "or "
+ + "("
+ + "a/description[at0001]/items[at0001]/items[at0002]/items[at0003]/value/value = true "
+ + "and"
+ + " a/description[at0001]/items[at0001]/items[at0002]/items[at0003]/value/value = true"
+ + "))",
+ "UC16"),
+ UC17(
+ "select a from EHR e [ehr_id/value = '4a7c01cf-bb1c-4d3d-8385-4ae0674befb1']"
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]",
+ "Select a as F1 from EHR e contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1] where e/ehr_id/value = '4a7c01cf-bb1c-4d3d-8385-4ae0674befb1'",
+ "UC17"),
+ UC18(
+ "select a from EHR e [ehr_id/value = '4a7c01cf-bb1c-4d3d-8385-4ae0674befb1']"
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1] "
+ + "where c/template_id='openEHR-EHR-COMPOSITION.health_summary.v1'",
+ "Select a as F1 from EHR e contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1] where (c/template_id = 'openEHR-EHR-COMPOSITION.health_summary.v1' and e/ehr_id/value = '4a7c01cf-bb1c-4d3d-8385-4ae0674befb1')",
+ "UC18"),
+ UC19(
+ "select c/category from EHR e [ehr_id/value = '4a7c01cf-bb1c-4d3d-8385-4ae0674befb1']"
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1]",
+ "Select c/category as F1 from EHR e contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] where e/ehr_id/value = '4a7c01cf-bb1c-4d3d-8385-4ae0674befb1'",
+ "UC19"),
+ UC20(
+ "select c/category/defining_code from EHR e [ehr_id/value = '4a7c01cf-bb1c-4d3d-8385-4ae0674befb1']"
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1]",
+ "Select c/category/defining_code as F1 from EHR e contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] where e/ehr_id/value = '4a7c01cf-bb1c-4d3d-8385-4ae0674befb1'",
+ "UC20"),
+ UC21(
+ "select c/category/defining_code/terminology_id/value from EHR e [ehr_id/value = '4a7c01cf-bb1c-4d3d-8385-4ae0674befb1']"
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1]",
+ "Select c/category/defining_code/terminology_id/value as F1 from EHR e contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] where e/ehr_id/value = '4a7c01cf-bb1c-4d3d-8385-4ae0674befb1'",
+ "UC21"),
+ UC22(
+ "select count(a/description[at0001]/items[openEHR-EHR-CLUSTER.test_all_types.v1]/items[at0001]/items[at0002]/items[at0003]/value/value,"
+ + "a/description[at0001]/items[openEHR-EHR-CLUSTER.test_all_types.v1]/items[at0001]/items[at0002]/items[at0004]/value/value)"
+ + "from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]",
+ "Select COUNT(a/description[at0001]/items[openEHR-EHR-CLUSTER.test_all_types.v1]/items[at0001]/items[at0002]/items[at0003]/value/value) as F1 from EHR e contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]",
+ "UC22"),
+ UC23("SELECT count(e/time_created) FROM EHR e", "Select COUNT(e/time_created) as F1 from EHR e", "UC23"),
+
+ UC24(
+ "select c"
+ + " from EHR e"
+ + " contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1]"
+ + " WHERE NOT EXISTS c/content[openEHR-EHR-ADMIN_ENTRY.hospitalization.v0]",
+ "Select c as F1 from EHR e contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] where NOT EXISTS c/content[openEHR-EHR-ADMIN_ENTRY.hospitalization.v0]",
+ "UC24"),
+ UC25(
+ "select c/feeder_audit/originating_system_audit/system_id from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1]",
+ "Select c/feeder_audit/originating_system_audit/system_id as F1 from EHR e contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1]",
+ "UC25"),
+ /* unsupported CONTAINS syntax
+ UC26(
+ "select c from EHR e contains COMPOSITION c AND NOT CONTAINS ADMIN_ENTRY u[openEHR-EHR-ADMIN_ENTRY.hospitalization.v0]",
+ "Select c as F1 from EHR e contains COMPOSITION c AND NOT CONTAINS ADMIN_ENTRY u[openEHR-EHR-ADMIN_ENTRY.hospitalization.v0]",
+ "UC26"),
+ */
+ /*
+ IN not official aql feature
+ UC27(
+ "Select e/folders/name/value" + " from EHR e"
+ + " where e/ehr_id/value = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c'"
+ + " and 'case1' IN(e/folders/name/value)",
+ "Select e/folders/name/value" + " from EHR e"
+ + " where e/ehr_id/value = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c'"
+ + " and 'case1' IN(e/folders/name/value)",
+ "UC27"),
+
+ */
+ /*
+ SOME not official aql feature
+ UC28(
+ "Select e/folders/name/value\n" + " from EHR e\n"
+ + " where e/ehr_id/value = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c'\n"
+ + " and 'case1' = SOME(e/folders/name/value)",
+ "Select e/folders/name/value\n" + " from EHR e\n"
+ + " where e/ehr_id/value = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c'\n"
+ + " and 'case1' = SOME(e/folders/name/value)",
+ "UC28"),
+ */
+ /* any not official aql feature
+ UC29(
+ "Select e/folders/name/value\n" + " from EHR e\n"
+ + " where e/ehr_id/value = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c'\n"
+ + " and 'case1' = ANY(e/folders/name/value)",
+ "Select e/folders/name/value\n" + " from EHR e\n"
+ + " where e/ehr_id/value = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c'\n"
+ + " and 'case1' = ANY(e/folders/name/value)",
+ "UC29"),
+ */
+ /* ALL not official aql feature
+ UC30(
+ "Select e/folders/name/value\n" + " from EHR e\n"
+ + " where e/ehr_id/value = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c'\n"
+ + " and 'case1' = ALL(e/folders/name/value)",
+ "Select e/folders/name/value\n" + " from EHR e\n"
+ + " where e/ehr_id/value = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c'\n"
+ + " and 'case1' = ALL(e/folders/name/value)",
+ "UC30")
+ */
+ /* ALL not official aql feature
+ UC31(
+ "Select e/folders/name/value\n" + " from EHR e\n"
+ + " where 'case1' = ALL(e/folders/name/value)"
+ + " and e/ehr_id/value = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c'",
+ "Select e/folders/name/value\n" + " from EHR e\n"
+ + " where 'case1' = ALL(e/folders/name/value)"
+ + " and e/ehr_id/value = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c'",
+ "UC31"),
+ */
+ /*
+ UC32(
+ "Select e/folders/name/value\n" + " from EHR e\n"
+ + " where 'case1' IN (regexp_split_to_array('case1,case2', ','))"
+ + " and e/ehr_id/value = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c'",
+ "Select e/folders/name/value\n" + " from EHR e\n"
+ + " where 'case1' IN (regexp_split_to_array('case1,case2', ','))"
+ + " and e/ehr_id/value = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c'",
+ "UC32"),
+
+ */
+ /*
+ UC33(
+ "Select e/folders/name/value\n" + " from EHR e\n"
+ + " where 'case1' IN ('case1','case2')"
+ + " and e/ehr_id/value = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c'",
+ "Select e/folders/name/value\n" + " from EHR e\n"
+ + " where 'case1' IN ('case1','case2')"
+ + " and e/ehr_id/value = 'c2561bab-4d2b-4ffd-a893-4382e9048f8c'",
+ "UC33"),
+ */
+ UC34("SELECT min(e/time_created) FROM EHR e", "Select MAX(e/time_created) as F1 from EHR e", "UC34"),
+ UC35("SELECT e/directory FROM EHR e", "Select e/directory as F1 from EHR e", "UC35"),
+ UC36("SELECT e FROM EHR e", "Select e as F1 from EHR e", "UC36"),
+ UC37(
+ "select" + " avg (d/description[at0001]/items[at0004]/value/magnitude) as avg_magnitude"
+ + " from EHR e"
+ + " contains COMPOSITION"
+ + " contains ACTION d[openEHR-EHR-ACTION.immunisation_procedure.v1]",
+ "Select AVG(d/description[at0001]/items[at0004]/value/magnitude) as avg_magnitude from EHR e contains COMPOSITION c0 contains ACTION d[openEHR-EHR-ACTION.immunisation_procedure.v1]",
+ "UC37"),
+ UC38(
+ "select" + " min (d/description[at0001]/items[at0004]/value/magnitude) as min_magnitude"
+ + " from EHR e"
+ + " contains COMPOSITION"
+ + " contains ACTION d[openEHR-EHR-ACTION.immunisation_procedure.v1]",
+ "Select MAX(d/description[at0001]/items[at0004]/value/magnitude) as min_magnitude from EHR e contains COMPOSITION c0 contains ACTION d[openEHR-EHR-ACTION.immunisation_procedure.v1]",
+ "UC38"),
+ UC39(
+ "select" + " max (d/description[at0001]/items[at0004]/value/magnitude) as max_magnitude"
+ + " from EHR e"
+ + " contains COMPOSITION"
+ + " contains ACTION d[openEHR-EHR-ACTION.immunisation_procedure.v1]",
+ "Select MAX(d/description[at0001]/items[at0004]/value/magnitude) as max_magnitude from EHR e contains COMPOSITION c0 contains ACTION d[openEHR-EHR-ACTION.immunisation_procedure.v1]",
+ "UC39"),
+ /*
+ UC40("select" + " CAST (d/description[at0001]/items[at0004]/value/magnitude AS 'FLOAT') as max_magnitude"
+ + " from EHR e"
+ + " contains COMPOSITION"
+ + " contains ACTION d[openEHR-EHR-ACTION.immunisation_procedure.v1]", "", "UC40"),
+
+ */
+ /*
+ UC41("SELECT TRUE as constant FROM EHR e", "", "UC41"),
+
+ */
+ /*
+ UC42("SELECT\n" + " \t c[name/value = 'Diagnose']/uid/value as Diagnose,\n"
+ + " \t c[composer/external_ref/id/value = 'Dr Mabuse']/uid/value as MabuseComposition,\n"
+ + " \t c[context/start_time/value > '2020-01-01']/uid/value as NewerComposition\n"
+ + "\tFROM\n"
+ + " \t EHR e\n"
+ + " \t contains COMPOSITION c[openEHR-EHR-COMPOSITION.report-result.v1]",
+ "Select c['Diagnose']/uid/value as Diagnose, c/[composer/external_ref/id/value='Dr Mabuse']/uid/value as MabuseComposition," +
+ " c[context/start_time/value>'2020-01-01']/uid/value as NewerComposition " +
+ "from EHR e " +
+ "contains COMPOSITION c[openEHR-EHR-COMPOSITION.report-result.v1]", "UC42"),
+ UC43("select c[name/value = 'Laborbefund-1']/uid/value as uid1,\n"
+ + "\t\t\t c[name/value = 'Laborbefund-2']/uid/value as uid2\n"
+ + "\t\t\t\tfrom EHR e \n"
+ + "\t\t\t\tcontains COMPOSITION c[openEHR-EHR-COMPOSITION.report-result.v1]", "", "UC43"),
+
+ */
+ UC46(
+ "select DISTINCT "
+ + " a/description[at0001]/items[at0002]/value/value as description,"
+ + " a/time as timing"
+ + " from EHR e "
+ + "contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] "
+ + "contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1]"
+ + "order by description ASC",
+ "Select DISTINCT a/description[at0001]/items[at0002]/value/value as description, a/time as timing from EHR e contains COMPOSITION c[openEHR-EHR-COMPOSITION.health_summary.v1] contains ACTION a[openEHR-EHR-ACTION.immunisation_procedure.v1] order by a/description[at0001]/items[at0002]/value/value ASCENDING",
+ "UC46"),
+ ;
+
+ private final String testAql;
+ private final String expectedAql;
+
+ UCTestData(String testAql, String expectedAql, String description) {
+ this.testAql = testAql;
+ this.expectedAql = expectedAql;
+ }
+
+ @Override
+ public String getTestAql() {
+ return testAql;
+ }
+
+ @Override
+ public String getExpectedAql() {
+ return expectedAql;
+ }
+}
diff --git a/web-template/src/test/java/org/ehrbase/webtemplate/util/CharSequenceHelperTest.java b/aql/src/test/java/org/ehrbase/aql/util/CharSequenceHelperTest.java
similarity index 98%
rename from web-template/src/test/java/org/ehrbase/webtemplate/util/CharSequenceHelperTest.java
rename to aql/src/test/java/org/ehrbase/aql/util/CharSequenceHelperTest.java
index c8050774b..b08812b67 100644
--- a/web-template/src/test/java/org/ehrbase/webtemplate/util/CharSequenceHelperTest.java
+++ b/aql/src/test/java/org/ehrbase/aql/util/CharSequenceHelperTest.java
@@ -15,7 +15,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.ehrbase.webtemplate.util;
+package org.ehrbase.aql.util;
import static org.junit.Assert.*;
diff --git a/client/src/main/java/org/ehrbase/client/flattener/DtoFromCompositionWalker.java b/client/src/main/java/org/ehrbase/client/flattener/DtoFromCompositionWalker.java
index d3dcd832b..3906b2170 100644
--- a/client/src/main/java/org/ehrbase/client/flattener/DtoFromCompositionWalker.java
+++ b/client/src/main/java/org/ehrbase/client/flattener/DtoFromCompositionWalker.java
@@ -32,6 +32,7 @@
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.text.CaseUtils;
+import org.ehrbase.aql.dto.path.AqlPath;
import org.ehrbase.client.annotations.Entity;
import org.ehrbase.client.annotations.OptionFor;
import org.ehrbase.client.annotations.Path;
@@ -43,7 +44,6 @@
import org.ehrbase.util.exception.SdkException;
import org.ehrbase.util.reflection.ReflectionHelper;
import org.ehrbase.webtemplate.model.WebTemplateNode;
-import org.ehrbase.webtemplate.parser.AqlPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/client/src/main/java/org/ehrbase/client/flattener/DtoToCompositionWalker.java b/client/src/main/java/org/ehrbase/client/flattener/DtoToCompositionWalker.java
index faf48fd47..19493e105 100644
--- a/client/src/main/java/org/ehrbase/client/flattener/DtoToCompositionWalker.java
+++ b/client/src/main/java/org/ehrbase/client/flattener/DtoToCompositionWalker.java
@@ -36,6 +36,7 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.ehrbase.aql.dto.path.AqlPath;
import org.ehrbase.client.annotations.Entity;
import org.ehrbase.client.annotations.OptionFor;
import org.ehrbase.client.annotations.Path;
@@ -44,7 +45,6 @@
import org.ehrbase.serialisation.walker.Context;
import org.ehrbase.serialisation.walker.ToCompositionWalker;
import org.ehrbase.webtemplate.model.WebTemplateNode;
-import org.ehrbase.webtemplate.parser.AqlPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/client/src/main/java/org/ehrbase/client/flattener/DtoWithMatchingFields.java b/client/src/main/java/org/ehrbase/client/flattener/DtoWithMatchingFields.java
index 277dd34ad..7ede44a68 100644
--- a/client/src/main/java/org/ehrbase/client/flattener/DtoWithMatchingFields.java
+++ b/client/src/main/java/org/ehrbase/client/flattener/DtoWithMatchingFields.java
@@ -19,7 +19,7 @@
import java.lang.reflect.Field;
import java.util.Map;
-import org.ehrbase.webtemplate.parser.AqlPath;
+import org.ehrbase.aql.dto.path.AqlPath;
class DtoWithMatchingFields {
diff --git a/client/src/main/java/org/ehrbase/client/flattener/PathMatcher.java b/client/src/main/java/org/ehrbase/client/flattener/PathMatcher.java
index 323ef98d8..8ad281091 100644
--- a/client/src/main/java/org/ehrbase/client/flattener/PathMatcher.java
+++ b/client/src/main/java/org/ehrbase/client/flattener/PathMatcher.java
@@ -19,9 +19,9 @@
import java.util.Map;
import java.util.Objects;
+import org.ehrbase.aql.dto.path.AqlPath;
import org.ehrbase.serialisation.walker.Context;
import org.ehrbase.webtemplate.model.WebTemplateNode;
-import org.ehrbase.webtemplate.parser.AqlPath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/generator/src/main/java/org/ehrbase/client/classgenerator/DefaultNamingStrategy.java b/generator/src/main/java/org/ehrbase/client/classgenerator/DefaultNamingStrategy.java
index d192379f2..321a3bfb0 100644
--- a/generator/src/main/java/org/ehrbase/client/classgenerator/DefaultNamingStrategy.java
+++ b/generator/src/main/java/org/ehrbase/client/classgenerator/DefaultNamingStrategy.java
@@ -24,10 +24,10 @@
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.CaseUtils;
+import org.ehrbase.aql.dto.path.AqlPath;
import org.ehrbase.serialisation.util.SnakeCase;
import org.ehrbase.webtemplate.model.WebTemplateAnnotation;
import org.ehrbase.webtemplate.model.WebTemplateNode;
-import org.ehrbase.webtemplate.parser.AqlPath;
public class DefaultNamingStrategy implements NamingStrategy {
diff --git a/generator/src/main/java/org/ehrbase/client/classgenerator/FieldGenerator.java b/generator/src/main/java/org/ehrbase/client/classgenerator/FieldGenerator.java
index 4214cb1e3..0699b069c 100644
--- a/generator/src/main/java/org/ehrbase/client/classgenerator/FieldGenerator.java
+++ b/generator/src/main/java/org/ehrbase/client/classgenerator/FieldGenerator.java
@@ -21,6 +21,7 @@
import java.util.List;
import javax.lang.model.element.Modifier;
import org.apache.commons.lang3.StringUtils;
+import org.ehrbase.aql.dto.path.AqlPath;
import org.ehrbase.client.annotations.Archetype;
import org.ehrbase.client.annotations.Path;
import org.ehrbase.client.aql.containment.Containment;
@@ -29,7 +30,6 @@
import org.ehrbase.client.aql.field.ListSelectAqlField;
import org.ehrbase.client.aql.field.SelectAqlField;
import org.ehrbase.serialisation.util.SnakeCase;
-import org.ehrbase.webtemplate.parser.AqlPath;
public class FieldGenerator {
diff --git a/serialisation/src/main/java/org/ehrbase/serialisation/dbencoding/wrappers/json/writer/translator_db2raw/LinkedTreeMapAdapter.java b/serialisation/src/main/java/org/ehrbase/serialisation/dbencoding/wrappers/json/writer/translator_db2raw/LinkedTreeMapAdapter.java
index 8fa749793..449795536 100644
--- a/serialisation/src/main/java/org/ehrbase/serialisation/dbencoding/wrappers/json/writer/translator_db2raw/LinkedTreeMapAdapter.java
+++ b/serialisation/src/main/java/org/ehrbase/serialisation/dbencoding/wrappers/json/writer/translator_db2raw/LinkedTreeMapAdapter.java
@@ -28,10 +28,10 @@
import com.nedap.archie.rminfo.ArchieRMInfoLookup;
import java.io.IOException;
import java.util.*;
+import org.ehrbase.aql.dto.path.AqlPath;
import org.ehrbase.serialisation.dbencoding.CompositionSerializer;
import org.ehrbase.serialisation.dbencoding.wrappers.json.I_DvTypeAdapter;
import org.ehrbase.serialisation.util.SnakeCase;
-import org.ehrbase.webtemplate.parser.AqlPath;
/**
* GSON adapter for LinkedTreeMap
diff --git a/serialisation/src/main/java/org/ehrbase/serialisation/walker/ItemExtractor.java b/serialisation/src/main/java/org/ehrbase/serialisation/walker/ItemExtractor.java
index 2b7b1f7dd..30cf396c1 100644
--- a/serialisation/src/main/java/org/ehrbase/serialisation/walker/ItemExtractor.java
+++ b/serialisation/src/main/java/org/ehrbase/serialisation/walker/ItemExtractor.java
@@ -25,10 +25,10 @@
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
+import org.ehrbase.aql.dto.path.AqlPath;
import org.ehrbase.util.exception.SdkException;
import org.ehrbase.util.rmconstants.RmConstants;
import org.ehrbase.webtemplate.model.WebTemplateNode;
-import org.ehrbase.webtemplate.parser.AqlPath;
public class ItemExtractor {
private RMObject currentRM;
diff --git a/web-template/pom.xml b/web-template/pom.xml
index a5163c857..76a9f082f 100644
--- a/web-template/pom.xml
+++ b/web-template/pom.xml
@@ -39,6 +39,10 @@
org.ehrbase.openehr.sdk
opt-1.4
+
+ org.ehrbase.openehr.sdk
+ aql
+
org.ehrbase.openehr.sdk
util
diff --git a/web-template/src/main/java/org/ehrbase/webtemplate/model/AqlPathSerializer.java b/web-template/src/main/java/org/ehrbase/webtemplate/model/AqlPathSerializer.java
index 14e759c82..f5ddb8132 100644
--- a/web-template/src/main/java/org/ehrbase/webtemplate/model/AqlPathSerializer.java
+++ b/web-template/src/main/java/org/ehrbase/webtemplate/model/AqlPathSerializer.java
@@ -21,7 +21,7 @@
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
-import org.ehrbase.webtemplate.parser.AqlPath;
+import org.ehrbase.aql.dto.path.AqlPath;
public class AqlPathSerializer extends StdSerializer {
diff --git a/web-template/src/main/java/org/ehrbase/webtemplate/model/WebTemplate.java b/web-template/src/main/java/org/ehrbase/webtemplate/model/WebTemplate.java
index d1d54630c..c9d684422 100644
--- a/web-template/src/main/java/org/ehrbase/webtemplate/model/WebTemplate.java
+++ b/web-template/src/main/java/org/ehrbase/webtemplate/model/WebTemplate.java
@@ -21,7 +21,7 @@
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
-import org.ehrbase.webtemplate.parser.AqlPath;
+import org.ehrbase.aql.dto.path.AqlPath;
import org.ehrbase.webtemplate.parser.NodeId;
@JsonInclude(JsonInclude.Include.NON_NULL)
diff --git a/web-template/src/main/java/org/ehrbase/webtemplate/model/WebTemplateNode.java b/web-template/src/main/java/org/ehrbase/webtemplate/model/WebTemplateNode.java
index fc9788a49..fc9255a9a 100644
--- a/web-template/src/main/java/org/ehrbase/webtemplate/model/WebTemplateNode.java
+++ b/web-template/src/main/java/org/ehrbase/webtemplate/model/WebTemplateNode.java
@@ -33,7 +33,7 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
-import org.ehrbase.webtemplate.parser.AqlPath;
+import org.ehrbase.aql.dto.path.AqlPath;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class WebTemplateNode implements Serializable {
diff --git a/web-template/src/main/java/org/ehrbase/webtemplate/parser/OPTParser.java b/web-template/src/main/java/org/ehrbase/webtemplate/parser/OPTParser.java
index e9d847d55..7deda9967 100644
--- a/web-template/src/main/java/org/ehrbase/webtemplate/parser/OPTParser.java
+++ b/web-template/src/main/java/org/ehrbase/webtemplate/parser/OPTParser.java
@@ -51,6 +51,7 @@
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlTokenSource;
+import org.ehrbase.aql.dto.path.AqlPath;
import org.ehrbase.terminology.client.terminology.TermDefinition;
import org.ehrbase.terminology.client.terminology.TerminologyProvider;
import org.ehrbase.terminology.client.terminology.ValueSet;
diff --git a/web-template/src/main/java/org/ehrbase/webtemplate/path/flat/FlatPathParser.java b/web-template/src/main/java/org/ehrbase/webtemplate/path/flat/FlatPathParser.java
index 32859adf9..3100aa632 100644
--- a/web-template/src/main/java/org/ehrbase/webtemplate/path/flat/FlatPathParser.java
+++ b/web-template/src/main/java/org/ehrbase/webtemplate/path/flat/FlatPathParser.java
@@ -17,8 +17,8 @@
*/
package org.ehrbase.webtemplate.path.flat;
-import static org.ehrbase.webtemplate.util.CharSequenceHelper.removeStart;
-import static org.ehrbase.webtemplate.util.CharSequenceHelper.splitFirst;
+import static org.ehrbase.aql.util.CharSequenceHelper.removeStart;
+import static org.ehrbase.aql.util.CharSequenceHelper.splitFirst;
import org.apache.commons.lang3.StringUtils;