
org.ehrbase.aql.sql.queryimpl.IterativeNode Maven / Gradle / Ivy
/*
* Copyright (c) 2019 vitasystems GmbH and Hannover Medical School.
*
* This file is part of project EHRbase
*
* 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.sql.queryimpl;
import static org.ehrbase.aql.sql.queryimpl.IterativeNodeConstants.ENV_AQL_ARRAY_DEPTH;
import static org.ehrbase.aql.sql.queryimpl.IterativeNodeConstants.ENV_AQL_ARRAY_IGNORE_NODE;
import static org.ehrbase.aql.sql.queryimpl.QueryImplConstants.AQL_NODE_ITERATIVE_MARKER;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.ehrbase.dao.access.interfaces.I_DomainAccess;
import org.ehrbase.ehr.util.LocatableHelper;
import org.ehrbase.service.IntrospectService;
/**
* Created by christian on 5/9/2018.
*/
@SuppressWarnings({"java:S3776", "java:S3740", "java:S1452", "java:S1075", "java:S135"})
public class IterativeNode implements IIterativeNode {
public static final String SLASH = "/";
public static final String ACTIVITIES = "/activities";
public static final String COMPOSITION = "/composition";
public static final String EVENTS = "/events";
public static final String CONTENT = "/content";
private Optional ignorePattern;
private final List unbounded;
private final int depth;
private final I_DomainAccess domainAccess;
public IterativeNode(I_DomainAccess domainAccess, String templateId, IntrospectService introspectCache) {
this.domainAccess = domainAccess;
unbounded = introspectCache.multiValued(templateId);
ignorePattern = initIgnorePattern();
depth = initAqlDepth();
}
/**
* check if node at path is iterative (max > 1)
*
* @param segmentedPath
* @return
*/
int[] iterativeAt(List segmentedPath) {
if (unbounded.isEmpty()) {
return null;
}
SortedSet retarray = new TreeSet<>();
for (int i = unbounded.size() - 1; i >= 0; i--) {
String aqlPath = unbounded.get(i);
List aqlPathSegments = LocatableHelper.dividePathIntoSegments(aqlPath);
// check if this path is not excluded
boolean ignoreThisAqlPath = ignorePattern
.map(p -> p.matcher(aqlPathSegments.get(aqlPathSegments.size() - 1)))
.map(Matcher::matches)
.orElse(false);
if (ignoreThisAqlPath) {
continue;
}
String path = compact(segmentedPath).stream().collect(Collectors.joining(SLASH, SLASH, ""));
if (path.startsWith(aqlPath) && !StringUtils.endsWithAny(aqlPath, "value", "name")) {
int pos = aqlPathInJsonbArray(aqlPathSegments, segmentedPath);
retarray.add(pos);
if (retarray.size() >= depth) break;
}
}
return retarray.stream().mapToInt(Integer::intValue).toArray();
}
List clipInIterativeMarkers(List segmentedPath, int[] clipPos) {
List resultingPath = new ArrayList<>(segmentedPath);
// reverse order so adding entries does not move pending positions
ArrayUtils.reverse(clipPos);
Arrays.stream(clipPos).forEach(pos -> {
if (pos == resultingPath.size()) {
resultingPath.add(AQL_NODE_ITERATIVE_MARKER);
} else {
String segment = resultingPath.get(pos);
if (segment.equals(QueryImplConstants.AQL_NODE_NAME_PREDICATE_MARKER)) {
// skip
} else if (segment.equals("0")) {
// substitution
resultingPath.set(pos, AQL_NODE_ITERATIVE_MARKER);
} else {
resultingPath.add(pos, AQL_NODE_ITERATIVE_MARKER);
}
}
});
return resultingPath;
}
public List insertIterativeMarkers(List segmentedPath) {
int[] pos = iterativeAt(segmentedPath);
if (pos == null) {
return null;
}
if (ArrayUtils.isEmpty(pos)) {
return segmentedPath;
}
return clipInIterativeMarkers(segmentedPath, pos);
}
/**
* make the path usable to perform JsonPath queries
*
* @param segmentedPath
* @return
*/
static List compact(List segmentedPath) {
return segmentedPath.stream()
.filter(item -> !StringUtils.isNumeric(item))
.filter(item -> !item.startsWith(COMPOSITION))
// skip structure containers that are specific to DB encoding (that is:
// /events/events[openEHR...])
// this also applies to /activities
.filter(item -> !Set.of(EVENTS, ACTIVITIES).contains(item))
.map(item -> StringUtils.removeStart(item, SLASH))
.collect(Collectors.toList());
}
static int aqlPathInJsonbArray(List aqlSegmented, List jsonbSegmented) {
int jsonbIndex = 0;
int aqlSegIndex = 0;
for (; aqlSegIndex < aqlSegmented.size(); jsonbIndex++) {
String segment = jsonbSegmented.get(jsonbIndex);
if (segment.startsWith(IterativeNode.COMPOSITION) || StringUtils.isNumeric(segment)) {
continue;
}
if (StringUtils.isNumeric(segment)) {
continue;
}
if (Set.of(EVENTS, ACTIVITIES).contains(segment)) {
// skip this structural item
continue;
}
try {
if (segment.startsWith(SLASH)) {
assert segment.substring(1).equals(aqlSegmented.get(aqlSegIndex));
} else {
assert segment.equals(aqlSegmented.get(aqlSegIndex));
}
} catch (AssertionError e1) {
throw new AssertionError("Drift in locating array marker: aql:" + aqlSegmented.get(aqlSegIndex)
+ ", jsonb:" + segment + ", @index:" + jsonbIndex);
}
aqlSegIndex++;
}
return jsonbIndex;
}
private Optional initIgnorePattern() {
String ignoreIterativeNode = System.getenv(ENV_AQL_ARRAY_IGNORE_NODE);
if (ignoreIterativeNode == null) {
ignoreIterativeNode = domainAccess.getServerConfig().getAqlIterationSkipList();
if (StringUtils.isBlank(ignoreIterativeNode)) {
ignoreIterativeNode = CONTENT + "," + EVENTS;
}
}
return Optional.ofNullable(ignoreIterativeNode)
.filter(StringUtils::isNotBlank)
.map(String::trim)
.map(s -> s.replace(',', '|'))
.map(s -> "^(" + s + ").*")
.map(Pattern::compile);
}
private int initAqlDepth() {
if (System.getenv(ENV_AQL_ARRAY_DEPTH) != null) {
return Integer.parseInt(System.getenv(ENV_AQL_ARRAY_DEPTH));
} else if (domainAccess.getServerConfig().getAqlDepth() != null) {
return domainAccess.getServerConfig().getAqlDepth();
} else {
return 1;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy