use of org.hl7.fhir.r5.model.ExpressionNode.Function in project pathling by aehrc.
the class ResolveFunction method invoke.
@Nonnull
@Override
public FhirPath invoke(@Nonnull final NamedFunctionInput input) {
checkUserInput(input.getInput() instanceof ReferencePath, "Input to " + NAME + " function must be a Reference: " + input.getInput().getExpression());
checkNoArguments(NAME, input);
final ReferencePath inputPath = (ReferencePath) input.getInput();
final Database database = input.getContext().getDatabase();
// Get the allowed types for the input reference. This gives us the set of possible resource
// types that this reference could resolve to.
Set<ResourceType> referenceTypes = inputPath.getResourceTypes();
// If the type is Resource, all resource types need to be looked at.
if (referenceTypes.contains(ResourceType.RESOURCE)) {
referenceTypes = FhirServer.supportedResourceTypes();
}
check(referenceTypes.size() > 0);
final boolean isPolymorphic = referenceTypes.size() > 1;
final String expression = expressionFromInput(input, NAME);
if (isPolymorphic) {
return resolvePolymorphicReference(input, database, referenceTypes, expression);
} else {
final FhirContext fhirContext = input.getContext().getFhirContext();
return resolveMonomorphicReference(input, database, fhirContext, referenceTypes, expression);
}
}
use of org.hl7.fhir.r5.model.ExpressionNode.Function in project pathling by aehrc.
the class AggregateFunction method buildAggregateResult.
@Nonnull
private <T extends FhirPath> T buildAggregateResult(@Nonnull final Dataset<Row> dataset, @Nonnull final ParserContext parserContext, @Nonnull final Collection<FhirPath> inputs, @Nonnull final Column valueColumn, @Nonnull final String expression, @Nonnull final ResultPathFactory<T> resultPathFactory) {
checkArgument(!inputs.isEmpty(), "Collection of inputs cannot be empty");
// Use an ID column from any of the inputs.
final Column idColumn = inputs.stream().findFirst().get().getIdColumn();
// Get the grouping columns from the parser context.
final List<Column> groupByList = parserContext.getGroupingColumns();
// Drop the requested grouping columns that are not present in the provided dataset.
// This handles the situation where `%resource` is used in `where()`.
// The columns requested for aggregation may include $this element ID, which is not present in
// datasets originating from `%resource` and thus should not be actually used for evaluation of
// the aggregation.
final Set<String> existingColumns = Stream.of(dataset.columns()).collect(Collectors.toSet());
final Column[] groupBy = groupByList.stream().filter(c -> existingColumns.contains(c.toString())).toArray(Column[]::new);
// The selection will be the first function applied to each column except the grouping columns,
// plus the value column.
final Predicate<Column> groupingFilter = column -> !groupByList.contains(column);
final List<Column> selection = Stream.of(dataset.columns()).map(functions::col).filter(groupingFilter).map(column -> first(column, true).alias(column.toString())).collect(Collectors.toList());
selection.add(valueColumn.alias("value"));
final Column firstSelection = checkPresent(selection.stream().limit(1).findFirst());
final Column[] remainingSelection = selection.stream().skip(1).toArray(Column[]::new);
// Get any this columns that may be present in the inputs.
// TODO: This is very error prone as a collection can be passed here instead of an array.
// How can we make it more stringent?
@SuppressWarnings("ConfusingArgumentToVarargsMethod") final Optional<Column> thisColumn = NonLiteralPath.findThisColumn(inputs.toArray(new FhirPath[0]));
final Dataset<Row> finalDataset = dataset.groupBy(groupBy).agg(firstSelection, remainingSelection);
final Column finalValueColumn = col("value");
// Clear out the node ID columns in the parser context - as they are no longer valid for joining.
parserContext.getNodeIdColumns().clear();
// empty eid column as the result is singular
return resultPathFactory.create(expression, finalDataset, idColumn, Optional.empty(), finalValueColumn, true, thisColumn);
}
use of org.hl7.fhir.r5.model.ExpressionNode.Function in project quality-measure-and-cohort-service by Alvearie.
the class CqlEvaluatorIntegrationTest method testUOMEquivalence_demonstrateConversionOnOneSide.
@Test
public void testUOMEquivalence_demonstrateConversionOnOneSide() throws Exception {
Patient patient = getPatient("123", Enumerations.AdministrativeGender.FEMALE, "1983-12-02");
CqlEvaluator evaluator = setupTestFor(patient, "cql.uomequivalence");
String expression = "AreEquivalent";
CqlEvaluationResult actual = evaluator.evaluate(new CqlVersionedIdentifier("TestUOMCompare", "1.0.0"), null, newPatientContext("123"), Collections.singleton(expression));
Map<String, Object> expected = new HashMap<>();
// you can use the *convert* function to change the
// units of a quantity to a known value
expected.put(expression, true);
Assert.assertEquals(expected, actual.getExpressionResults());
}
use of org.hl7.fhir.r5.model.ExpressionNode.Function in project quality-measure-and-cohort-service by Alvearie.
the class CqlEvaluatorIntegrationTest method testConditionDateRangeCriteriaMatched.
@Test
public void testConditionDateRangeCriteriaMatched() throws Exception {
Patient patient = getPatient("123", Enumerations.AdministrativeGender.FEMALE, null);
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse("2000-01-01");
Condition condition = new Condition();
condition.setId("condition");
condition.setSubject(new Reference("Patient/123"));
condition.setRecordedDate(date);
// Wiremock does not support request matching withQueryParam() function does not support
// the same parameter multiple times, so we do some regex work and try to make it
// somewhat order independent while still readable.
// @see https://github.com/tomakehurst/wiremock/issues/398
MappingBuilder builder = get(urlMatching("/Condition\\?(recorded-date=[lg]e.*&){2}subject=Patient%2F123&_format=json"));
mockFhirResourceRetrieval(builder, condition);
FhirServerConfig fhirConfig = getFhirServerConfig();
CqlEvaluator evaluator = setupTestFor(patient, fhirConfig, "cql.condition", ClasspathCqlLibraryProvider.FHIR_HELPERS_CLASSPATH);
Map<String, Parameter> parameters = new HashMap<>();
parameters.put("MeasurementPeriod", new IntervalParameter(new DatetimeParameter("1999-01-01T00:00:00-05:00"), true, new DatetimeParameter("2000-01-01T00:00:00-05:00"), false));
String expression = "ConditionInInterval";
CqlEvaluationResult actual = evaluator.evaluate(new CqlVersionedIdentifier("TestDateQuery", "1.0.0"), parameters, newPatientContext("123"), Collections.singleton(expression));
Assert.assertEquals(1, actual.getExpressionResults().size());
List<Object> value = (List) actual.getExpressionResults().get(expression);
Assert.assertEquals(1, value.size());
assertFhirEquals(condition, (IBaseResource) value.get(0));
}
use of org.hl7.fhir.r5.model.ExpressionNode.Function in project quality-measure-and-cohort-service by Alvearie.
the class AnyColumnVisitor method visitFunctionRef.
@Override
public Object visitFunctionRef(FunctionRef elm, AnyColumnContext context) {
if (AnyColumnFunctions.FUNCTION_NAMES.contains(elm.getName())) {
if (elm.getOperand().size() == 2) {
QName dataType = ((As) elm.getOperand().get(0)).getOperand().getResultTypeName();
// TODO - validate that the first operand is a model object. We really should be doing that at the
// method declaration level instead of Choice<Any>, but that will require the model
// to have a base class that everything extends from.
String columnMatchLogic = null;
if (elm.getOperand().get(1) instanceof Literal) {
columnMatchLogic = ((Literal) elm.getOperand().get(1)).getValue();
} else {
throw new IllegalArgumentException(String.format("Second argument to %s function at %s must be a literal", elm.getName(), elm.getLocator()));
}
StringMatcher matcher = null;
if (elm.getName().equals(AnyColumnFunctions.FUNC_ANY_COLUMN)) {
matcher = new PrefixStringMatcher(columnMatchLogic);
} else if (elm.getName().equals(AnyColumnFunctions.FUNC_ANY_COLUMN_REGEX)) {
matcher = new RegexStringMatcher(columnMatchLogic);
} else {
throw new IllegalArgumentException(String.format("Found declared, but unsupported AnyColumn function %s at %s", elm.getName(), elm.getLocator()));
}
context.reportAnyColumn(dataType, matcher);
} else {
throw new IllegalArgumentException(String.format("%s function at %s should have exactly two arguments", elm.getName(), elm.getLocator()));
}
}
return super.visitFunctionRef(elm, context);
}
Aggregations