Skip to content

Commit

Permalink
Issue #2163 - throw exception when filter cannot be applied / created
Browse files Browse the repository at this point in the history
Signed-off-by: John T.E. Timm <johntimm@us.ibm.com>
  • Loading branch information
JohnTimm committed Mar 30, 2021
1 parent 67e3e1f commit 1a1d434
Show file tree
Hide file tree
Showing 16 changed files with 1,187 additions and 963 deletions.
7 changes: 7 additions & 0 deletions fhir-term-graph/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@
<artifactId>fhir-term</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>fhir-term</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.janusgraph</groupId>
<artifactId>janusgraph-core</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
package com.ibm.fhir.term.graph.loader.impl;

import static com.ibm.fhir.term.graph.loader.util.FHIRTermGraphLoaderUtil.toMap;
import static com.ibm.fhir.term.graph.util.FHIRTermGraphUtil.toLong;
import static com.ibm.fhir.term.graph.util.FHIRTermGraphUtil.toObject;
import static com.ibm.fhir.term.util.CodeSystemSupport.getConcepts;
import static com.ibm.fhir.term.util.CodeSystemSupport.normalize;
import static com.ibm.fhir.term.util.CodeSystemSupport.toLong;
import static com.ibm.fhir.term.util.CodeSystemSupport.toObject;

import java.io.FileInputStream;
import java.io.InputStream;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
package com.ibm.fhir.term.graph.provider;

import static com.ibm.fhir.model.type.String.string;
import static com.ibm.fhir.term.graph.util.FHIRTermGraphUtil.toLong;
import static com.ibm.fhir.term.graph.util.FHIRTermGraphUtil.toObject;
import static com.ibm.fhir.term.util.CodeSystemSupport.convertsToBoolean;
import static com.ibm.fhir.term.util.CodeSystemSupport.getCodeSystemPropertyType;
import static com.ibm.fhir.term.util.CodeSystemSupport.hasCodeSystemProperty;
import static com.ibm.fhir.term.util.CodeSystemSupport.isCaseSensitive;
import static com.ibm.fhir.term.util.CodeSystemSupport.normalize;
import static com.ibm.fhir.term.util.CodeSystemSupport.toElement;
import static com.ibm.fhir.term.util.CodeSystemSupport.toLong;
import static com.ibm.fhir.term.util.CodeSystemSupport.toObject;
import static java.util.Objects.requireNonNull;

import java.util.ArrayList;
Expand All @@ -25,8 +25,6 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;

import org.apache.commons.configuration.Configuration;
Expand All @@ -51,6 +49,7 @@
import com.ibm.fhir.model.type.Element;
import com.ibm.fhir.model.type.Uri;
import com.ibm.fhir.model.type.code.CodeSystemHierarchyMeaning;
import com.ibm.fhir.model.type.code.FilterOperator;
import com.ibm.fhir.model.type.code.IssueSeverity;
import com.ibm.fhir.model.type.code.IssueType;
import com.ibm.fhir.model.type.code.PropertyType;
Expand All @@ -64,8 +63,6 @@
* Graph-based implementation of the {@link FHIRTermServiceProvider} interface using {@link FHIRTermGraph}
*/
public class GraphTermServiceProvider implements FHIRTermServiceProvider {
private static final Logger log = Logger.getLogger(GraphTermServiceProvider.class.getName());

public static final int DEFAULT_TIME_LIMIT = 90000; // 90 seconds
private static final int DEFAULT_COUNT = 1000;

Expand Down Expand Up @@ -166,18 +163,18 @@ public Set<Concept> getConcepts(CodeSystem codeSystem, List<Filter> filters) {
break;
case GENERALIZES:
// ancestors and self
g = applyGeneralizesFilter(codeSystem, filter, g);
g = applyGeneralizesFilter(codeSystem, filter, first, g);
break;
case IN:
g = applyInFilter(codeSystem, filter, first, g);
break;
case IS_A:
// descendants and self
g = applyIsAFilter(codeSystem, filter, g);
g = applyIsAFilter(codeSystem, filter, first, g);
break;
case IS_NOT_A:
// not descendants or self
g = applyIsNotAFilter(codeSystem, filter, g);
g = applyIsNotAFilter(codeSystem, filter, first, g);
break;
case NOT_IN:
g = applyNotInFilter(codeSystem, filter, first, g);
Expand Down Expand Up @@ -295,7 +292,7 @@ private GraphTraversal<Vertex, Vertex> applyDescendentOfFilter(CodeSystem codeSy
.dedup())
.emit();
}
return filterNotApplied(filter, g);
throw filterNotApplied(filter);
}

private GraphTraversal<Vertex, Vertex> applyEqualsFilter(CodeSystem codeSystem, Filter filter, boolean first, GraphTraversal<Vertex, Vertex> g) {
Expand All @@ -309,7 +306,7 @@ private GraphTraversal<Vertex, Vertex> applyEqualsFilter(CodeSystem codeSystem,
applyChildEqualsFilter(codeSystem, filter, first, g) :
applyPropertyEqualsFilter(codeSystem, filter, first, g);
}
return filterNotApplied(filter, g);
throw filterNotApplied(filter);
}

private GraphTraversal<Vertex, Vertex> applyExistsFilter(CodeSystem codeSystem, Filter filter, boolean first, GraphTraversal<Vertex, Vertex> g) {
Expand All @@ -321,23 +318,32 @@ private GraphTraversal<Vertex, Vertex> applyExistsFilter(CodeSystem codeSystem,
// not exists
applyPropertyNotExistsFilter(codeSystem, filter, g);
}
return filterNotApplied(filter, g);
throw filterNotApplied(filter);
}

@SuppressWarnings("unchecked")
private GraphTraversal<Vertex, Vertex> applyGeneralizesFilter(CodeSystem codeSystem, Filter filter, GraphTraversal<Vertex, Vertex> g) {
private GraphTraversal<Vertex, Vertex> applyGeneralizesFilter(CodeSystem codeSystem, Filter filter, boolean first, GraphTraversal<Vertex, Vertex> g) {
boolean caseSensitive = isCaseSensitive(codeSystem);
Code property = filter.getProperty();
com.ibm.fhir.model.type.String value = filter.getValue();
if ("concept".equals(property.getValue()) && CodeSystemHierarchyMeaning.IS_A.equals(codeSystem.getHierarchyMeaning())) {
return whereCodeSystem(hasCode(g, value.getValue(), caseSensitive), codeSystem)
.union(__.identity(), whereCodeSystem(hasCode(vertices(), value.getValue(), caseSensitive), codeSystem)
.repeat(__.out(FHIRTermGraph.IS_A)
.simplePath()
.dedup())
.emit());
if ("concept".equals(property.getValue())) {
if (codeSystem.getHierarchyMeaning() == null) {
// hierarchy meaning is not defined
return applyConceptInFilter(codeSystem, Filter.builder()
.property(property)
.op(FilterOperator.IN)
.value(value)
.build(), first, g);
} else if (CodeSystemHierarchyMeaning.IS_A.equals(codeSystem.getHierarchyMeaning())) {
return whereCodeSystem(hasCode(g, value.getValue(), caseSensitive), codeSystem)
.union(__.identity(), whereCodeSystem(hasCode(vertices(), value.getValue(), caseSensitive), codeSystem)
.repeat(__.out(FHIRTermGraph.IS_A)
.simplePath()
.dedup())
.emit());
}
}
return filterNotApplied(filter, g);
throw filterNotApplied(filter);
}

private GraphTraversal<Vertex, Vertex> applyInFilter(CodeSystem codeSystem, Filter filter, boolean first, GraphTraversal<Vertex, Vertex> g) {
Expand All @@ -347,36 +353,54 @@ private GraphTraversal<Vertex, Vertex> applyInFilter(CodeSystem codeSystem, Filt
applyConceptInFilter(codeSystem, filter, first, g) :
applyPropertyInFilter(codeSystem, filter, first, g);
}
return filterNotApplied(filter, g);
throw filterNotApplied(filter);
}

@SuppressWarnings("unchecked")
private GraphTraversal<Vertex, Vertex> applyIsAFilter(CodeSystem codeSystem, Filter filter, GraphTraversal<Vertex, Vertex> g) {
private GraphTraversal<Vertex, Vertex> applyIsAFilter(CodeSystem codeSystem, Filter filter, boolean first, GraphTraversal<Vertex, Vertex> g) {
boolean caseSensitive = isCaseSensitive(codeSystem);
Code property = filter.getProperty();
com.ibm.fhir.model.type.String value = filter.getValue();
if ("concept".equals(property.getValue()) && CodeSystemHierarchyMeaning.IS_A.equals(codeSystem.getHierarchyMeaning())) {
return whereCodeSystem(hasCode(g, value.getValue(), caseSensitive), codeSystem)
.union(__.identity(), whereCodeSystem(hasCode(vertices(), value.getValue(), caseSensitive), codeSystem)
.repeat(__.in(FHIRTermGraph.IS_A)
.simplePath()
.dedup())
.emit());
if ("concept".equals(property.getValue())) {
if (codeSystem.getHierarchyMeaning() == null) {
// hierarchy meaning is not defined
return applyConceptInFilter(codeSystem, Filter.builder()
.property(property)
.op(FilterOperator.IN)
.value(value)
.build(), first, g);
} else if (CodeSystemHierarchyMeaning.IS_A.equals(codeSystem.getHierarchyMeaning())) {
return whereCodeSystem(hasCode(g, value.getValue(), caseSensitive), codeSystem)
.union(__.identity(), whereCodeSystem(hasCode(vertices(), value.getValue(), caseSensitive), codeSystem)
.repeat(__.in(FHIRTermGraph.IS_A)
.simplePath()
.dedup())
.emit());
}
}
return filterNotApplied(filter, g);
throw filterNotApplied(filter);
}

private GraphTraversal<Vertex, Vertex> applyIsNotAFilter(CodeSystem codeSystem, Filter filter, GraphTraversal<Vertex, Vertex> g) {
private GraphTraversal<Vertex, Vertex> applyIsNotAFilter(CodeSystem codeSystem, Filter filter, boolean first, GraphTraversal<Vertex, Vertex> g) {
boolean caseSensitive = isCaseSensitive(codeSystem);
Code property = filter.getProperty();
com.ibm.fhir.model.type.String value = filter.getValue();
if ("concept".equals(property.getValue()) && CodeSystemHierarchyMeaning.IS_A.equals(codeSystem.getHierarchyMeaning())) {
return whereCodeSystem(g.not(__.repeat(__.out(FHIRTermGraph.IS_A))
.until(hasCode(value.getValue(), caseSensitive)))
.not(hasCode(value.getValue(), caseSensitive))
.hasLabel("Concept"), codeSystem);
if ("concept".equals(property.getValue())) {
if (codeSystem.getHierarchyMeaning() == null) {
// hierarchy meaning is not defined
return applyConceptNotInFilter(codeSystem, Filter.builder()
.property(property)
.op(FilterOperator.NOT_IN)
.value(value)
.build(), first, g);
} else if (CodeSystemHierarchyMeaning.IS_A.equals(codeSystem.getHierarchyMeaning())) {
return whereCodeSystem(g.not(__.repeat(__.out(FHIRTermGraph.IS_A))
.until(hasCode(value.getValue(), caseSensitive)))
.not(hasCode(value.getValue(), caseSensitive))
.hasLabel("Concept"), codeSystem);
}
}
return filterNotApplied(filter, g);
throw filterNotApplied(filter);
}

private GraphTraversal<Vertex, Vertex> applyNotInFilter(CodeSystem codeSystem, Filter filter, boolean first, GraphTraversal<Vertex, Vertex> g) {
Expand All @@ -386,7 +410,7 @@ private GraphTraversal<Vertex, Vertex> applyNotInFilter(CodeSystem codeSystem, F
applyConceptNotInFilter(codeSystem, filter, first, g) :
applyPropertyNotInFilter(codeSystem, filter, first, g);
}
return filterNotApplied(filter, g);
throw filterNotApplied(filter);
}

@SuppressWarnings({ "unchecked", "rawtypes" })
Expand Down Expand Up @@ -465,7 +489,7 @@ private GraphTraversal<Vertex, Vertex> applyRegexFilter(CodeSystem codeSystem, F
whereCodeSystem(g.has(getPropertyKey(type), Text.textRegex(value.getValue())).in("property_"), codeSystem) :
whereCodeSystem(g.where(__.out("property_").has(getPropertyKey(type), Text.textRegex(value.getValue()))), codeSystem);
}
return filterNotApplied(filter, g);
throw filterNotApplied(filter);
}

private void checkArgument(Code code, String message) {
Expand Down Expand Up @@ -559,13 +583,18 @@ private Property createProperty(Map<Object, Object> elementMap) {
.build();
}

private GraphTraversal<Vertex, Vertex> filterNotApplied(Filter filter, GraphTraversal<Vertex, Vertex> g) {
log.log(Level.WARNING,
String.format("Filter not applied (property: %s, op: %s, value: %s)",
filter.getProperty().getValue(),
filter.getOp().getValue(),
filter.getValue().getValue()));
return g;
private FHIRTermServiceException filterNotApplied(Filter filter) {
String message = String.format("Filter not applied (property: %s, op: %s, value: %s)",
filter.getProperty().getValue(),
filter.getOp().getValue(),
filter.getValue().getValue());
throw new FHIRTermServiceException(message, Collections.singletonList(Issue.builder()
.severity(IssueSeverity.ERROR)
.code(IssueType.NOT_SUPPORTED)
.details(CodeableConcept.builder()
.text(string(message))
.build())
.build()));
}

private Concept getConcept(CodeSystem codeSystem, Code code, boolean includeDesignations, boolean includeProperties) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,60 +6,14 @@

package com.ibm.fhir.term.graph.util;

import static com.ibm.fhir.model.util.ModelSupport.FHIR_BOOLEAN;
import static com.ibm.fhir.model.util.ModelSupport.FHIR_INTEGER;
import static com.ibm.fhir.model.util.ModelSupport.FHIR_STRING;

import java.time.LocalDate;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.TemporalAccessor;

import org.slf4j.LoggerFactory;

import com.ibm.fhir.model.type.Code;
import com.ibm.fhir.model.type.DateTime;
import com.ibm.fhir.model.type.Decimal;
import com.ibm.fhir.model.type.Element;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;

public class FHIRTermGraphUtil {
private FHIRTermGraphUtil() { }

/**
* Convert the given element value to an object value that is compatible with the graph schema.
*
* @param value
* the element value
* @return
* an object value that is compatible with the graph schema
*/
public static Object toObject(Element value) {
if (value.is(FHIR_BOOLEAN)) {
return value.as(FHIR_BOOLEAN).getValue();
}
if (value.is(Code.class)) {
return value.as(Code.class).getValue();
}
if (value.is(DateTime.class)) {
return DateTime.PARSER_FORMATTER.format(value.as(DateTime.class).getValue());
}
if (value.is(Decimal.class)) {
return value.as(Decimal.class).getValue().doubleValue();
}
if (value.is(FHIR_INTEGER)) {
return value.as(FHIR_INTEGER).getValue();
}
if (value.is(FHIR_STRING)) {
return value.as(FHIR_STRING).getValue();
}
throw new IllegalArgumentException();
}

/**
* Sets the root logger level for the logback classic root logger. See:
* <a href="https://docs.janusgraph.org/basics/common-questions/#debug-level-logging-slows-execution">JanusGraph common questions</a>
Expand All @@ -72,33 +26,4 @@ public static void setRootLoggerLevel(Level level) {
Logger rootLogger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
rootLogger.setLevel(level);
}

/**
* Convert the {@link DateTime} value to a Long value that is compatible with the graph schema.
*
* @param dateTime
* the dateTime value
* @return
* the Long equivalent value (milliseconds from the epoch)
*/
public static Long toLong(DateTime dateTime) {
TemporalAccessor value = dateTime.getValue();
if (value instanceof ZonedDateTime) {
ZonedDateTime zonedDateTime = (ZonedDateTime) value;
return zonedDateTime.toInstant().toEpochMilli();
}
if (value instanceof LocalDate) {
LocalDate localDate = (LocalDate) value;
return localDate.atStartOfDay().atZone(ZoneOffset.UTC).toInstant().toEpochMilli();
}
if (value instanceof YearMonth) {
YearMonth yearMonth = (YearMonth) value;
return yearMonth.atDay(1).atStartOfDay().atZone(ZoneOffset.UTC).toInstant().toEpochMilli();
}
if (value instanceof Year) {
Year year = (Year) value;
return year.atMonth(1).atDay(1).atStartOfDay().atZone(ZoneOffset.UTC).toInstant().toEpochMilli();
}
throw new IllegalArgumentException();
}
}
Loading

0 comments on commit 1a1d434

Please sign in to comment.