Search in sources :

Example 1 with ElementPath

use of au.csiro.pathling.fhirpath.element.ElementPath in project pathling by aehrc.

the class MemberOfFunction method validateInput.

private void validateInput(@Nonnull final NamedFunctionInput input) {
    final ParserContext context = input.getContext();
    checkUserInput(context.getTerminologyServiceFactory().isPresent(), "Attempt to call terminology function " + NAME + " when terminology service has not been configured");
    final FhirPath inputPath = input.getInput();
    checkUserInput(inputPath instanceof ElementPath && (((ElementPath) inputPath).getFhirType().equals(FHIRDefinedType.CODING) || ((ElementPath) inputPath).getFhirType().equals(FHIRDefinedType.CODEABLECONCEPT)), "Input to memberOf function is of unsupported type: " + inputPath.getExpression());
    checkUserInput(input.getArguments().size() == 1, "memberOf function accepts one argument of type String");
    final FhirPath argument = input.getArguments().get(0);
    checkUserInput(argument instanceof StringLiteralPath, "memberOf function accepts one argument of type String literal");
}
Also used : FhirPath(au.csiro.pathling.fhirpath.FhirPath) ElementPath(au.csiro.pathling.fhirpath.element.ElementPath) StringLiteralPath(au.csiro.pathling.fhirpath.literal.StringLiteralPath) ParserContext(au.csiro.pathling.fhirpath.parser.ParserContext)

Example 2 with ElementPath

use of au.csiro.pathling.fhirpath.element.ElementPath in project pathling by aehrc.

the class TranslateFunction method invoke.

@Nonnull
@Override
public FhirPath invoke(@Nonnull final NamedFunctionInput input) {
    validateInput(input);
    final ElementPath inputPath = (ElementPath) input.getInput();
    final ParserContext inputContext = input.getContext();
    final Column idColumn = inputPath.getIdColumn();
    final Column conceptColumn = inputPath.getValueColumn();
    final boolean isCodeableConcept = isCodeableConcept(inputPath);
    final Column codingArrayCol = isCodeableConcept ? conceptColumn.getField("coding") : when(conceptColumn.isNotNull(), array(conceptColumn)).otherwise(lit(null));
    // The definition of the result is always the Coding element.
    @SuppressWarnings("OptionalGetWithoutIsPresent") final ElementDefinition resultDefinition = isCodeableConcept ? inputPath.getChildElement("coding").get() : inputPath.getDefinition().get();
    // Prepare the data which will be used within the map operation. All of these things must be
    // Serializable.
    @SuppressWarnings("OptionalGetWithoutIsPresent") final TerminologyServiceFactory terminologyServiceFactory = inputContext.getTerminologyServiceFactory().get();
    final Arguments arguments = Arguments.of(input);
    final String conceptMapUrl = arguments.getValue(0, String.class);
    final boolean reverse = arguments.getValueOr(1, DEFAULT_REVERSE);
    final String equivalence = arguments.getValueOr(2, DEFAULT_EQUIVALENCE);
    final Dataset<Row> dataset = inputPath.getDataset();
    final MapperWithPreview<List<SimpleCoding>, Row[], ConceptTranslator> mapper = new TranslateMapperWithPreview(MDC.get("requestId"), terminologyServiceFactory, conceptMapUrl, reverse, Strings.parseCsvList(equivalence, wrapInUserInputError(ConceptMapEquivalence::fromCode)));
    final Dataset<Row> translatedDataset = SqlExtensions.mapWithPartitionPreview(dataset, codingArrayCol, SimpleCodingsDecoders::decodeList, mapper, StructField.apply("result", DataTypes.createArrayType(CodingEncoding.DATA_TYPE), true, Metadata.empty()));
    // The result is an array of translations per each input element, which we now
    // need to explode in the same way as for path traversal, creating unique element ids.
    final MutablePair<Column, Column> valueAndEidColumns = new MutablePair<>();
    final Dataset<Row> resultDataset = inputPath.explodeArray(translatedDataset, translatedDataset.col("result"), valueAndEidColumns);
    // Construct a new result expression.
    final String expression = expressionFromInput(input, NAME);
    return ElementPath.build(expression, resultDataset, idColumn, Optional.of(valueAndEidColumns.getRight()), valueAndEidColumns.getLeft(), false, inputPath.getCurrentResource(), inputPath.getThisColumn(), resultDefinition);
}
Also used : TerminologyServiceFactory(au.csiro.pathling.fhir.TerminologyServiceFactory) MutablePair(org.apache.commons.lang3.tuple.MutablePair) ElementPath(au.csiro.pathling.fhirpath.element.ElementPath) Column(org.apache.spark.sql.Column) ConceptTranslator(au.csiro.pathling.terminology.ConceptTranslator) SimpleCodingsDecoders(au.csiro.pathling.fhirpath.encoding.SimpleCodingsDecoders) List(java.util.List) ConceptMapEquivalence(org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence) ElementDefinition(au.csiro.pathling.fhirpath.element.ElementDefinition) Row(org.apache.spark.sql.Row) ParserContext(au.csiro.pathling.fhirpath.parser.ParserContext) Nonnull(javax.annotation.Nonnull)

Example 3 with ElementPath

use of au.csiro.pathling.fhirpath.element.ElementPath in project pathling by aehrc.

the class ExtensionFunction method invoke.

@Nonnull
@Override
public FhirPath invoke(@Nonnull final NamedFunctionInput input) {
    final String expression = NamedFunction.expressionFromInput(input, NAME);
    checkUserInput(input.getArguments().size() == 1, "extension function must have one argument: " + expression);
    final FhirPath urlArgument = input.getArguments().get(0);
    checkUserInput(urlArgument instanceof StringLiteralPath, "extension function must have argument of type String literal: " + expression);
    final NonLiteralPath inputPath = input.getInput();
    final ElementPath extensionPath = new PathTraversalOperator().invoke(new PathTraversalInput(input.getContext(), inputPath, ExtensionSupport.EXTENSION_ELEMENT_NAME()));
    // Now we need to create a correct argument context for the `where` call.
    final ParserContext argumentContext = input.getContext();
    final FhirPath extensionUrlPath = new PathTraversalOperator().invoke(new PathTraversalInput(argumentContext, extensionPath.toThisPath(), "url"));
    final FhirPath extensionUrCondition = new ComparisonOperator(ComparisonOperation.EQUALS).invoke(new OperatorInput(argumentContext, extensionUrlPath, urlArgument));
    // Override the expression in the function input.
    return new WhereFunction().invoke(new NamedFunctionInput(input.getContext(), extensionPath, Collections.singletonList(extensionUrCondition), expression));
}
Also used : PathTraversalInput(au.csiro.pathling.fhirpath.operator.PathTraversalInput) ComparisonOperator(au.csiro.pathling.fhirpath.operator.ComparisonOperator) FhirPath(au.csiro.pathling.fhirpath.FhirPath) StringLiteralPath(au.csiro.pathling.fhirpath.literal.StringLiteralPath) ElementPath(au.csiro.pathling.fhirpath.element.ElementPath) OperatorInput(au.csiro.pathling.fhirpath.operator.OperatorInput) PathTraversalOperator(au.csiro.pathling.fhirpath.operator.PathTraversalOperator) ParserContext(au.csiro.pathling.fhirpath.parser.ParserContext) NonLiteralPath(au.csiro.pathling.fhirpath.NonLiteralPath) Nonnull(javax.annotation.Nonnull)

Example 4 with ElementPath

use of au.csiro.pathling.fhirpath.element.ElementPath in project pathling by aehrc.

the class CountFunctionTest method doesNotCountNullElements.

@Test
void doesNotCountNullElements() {
    final Dataset<Row> dataset = new DatasetBuilder(spark).withIdColumn().withColumn("gender", DataTypes.StringType).withRow("patient-1", "female").withRow("patient-2", null).withRow("patient-3", "male").build();
    final ElementPath inputPath = new ElementPathBuilder(spark).expression("gender").fhirType(FHIRDefinedType.CODE).dataset(dataset).idAndValueColumns().build();
    final ParserContext parserContext = new ParserContextBuilder(spark, fhirContext).idColumn(inputPath.getIdColumn()).groupingColumns(Collections.emptyList()).build();
    final NamedFunctionInput countInput = new NamedFunctionInput(parserContext, inputPath, Collections.emptyList());
    final NamedFunction count = NamedFunction.getInstance("count");
    final FhirPath result = count.invoke(countInput);
    final Dataset<Row> expectedDataset = new DatasetBuilder(spark).withIdColumn().withColumn(DataTypes.LongType).withRow("patient-1", 2L).build();
    assertThat(result).hasExpression("gender.count()").isSingular().isElementPath(IntegerPath.class).hasFhirType(FHIRDefinedType.UNSIGNEDINT).selectOrderedResult().hasRows(expectedDataset);
}
Also used : ElementPath(au.csiro.pathling.fhirpath.element.ElementPath) FhirPath(au.csiro.pathling.fhirpath.FhirPath) IntegerPath(au.csiro.pathling.fhirpath.element.IntegerPath) ParserContextBuilder(au.csiro.pathling.test.builders.ParserContextBuilder) Row(org.apache.spark.sql.Row) DatasetBuilder(au.csiro.pathling.test.builders.DatasetBuilder) ResourceDatasetBuilder(au.csiro.pathling.test.builders.ResourceDatasetBuilder) ParserContext(au.csiro.pathling.fhirpath.parser.ParserContext) ElementPathBuilder(au.csiro.pathling.test.builders.ElementPathBuilder) Test(org.junit.jupiter.api.Test) SpringBootTest(org.springframework.boot.test.context.SpringBootTest)

Example 5 with ElementPath

use of au.csiro.pathling.fhirpath.element.ElementPath in project pathling by aehrc.

the class CountFunctionTest method inputMustNotContainArguments.

@Test
void inputMustNotContainArguments() {
    final ElementPath inputPath = new ElementPathBuilder(spark).build();
    final ElementPath argumentPath = new ElementPathBuilder(spark).build();
    final ParserContext parserContext = new ParserContextBuilder(spark, fhirContext).build();
    final NamedFunctionInput countInput = new NamedFunctionInput(parserContext, inputPath, Collections.singletonList(argumentPath));
    final InvalidUserInputError error = assertThrows(InvalidUserInputError.class, () -> NamedFunction.getInstance("count").invoke(countInput));
    assertEquals("Arguments can not be passed to count function", error.getMessage());
}
Also used : InvalidUserInputError(au.csiro.pathling.errors.InvalidUserInputError) ElementPath(au.csiro.pathling.fhirpath.element.ElementPath) ParserContextBuilder(au.csiro.pathling.test.builders.ParserContextBuilder) ParserContext(au.csiro.pathling.fhirpath.parser.ParserContext) ElementPathBuilder(au.csiro.pathling.test.builders.ElementPathBuilder) Test(org.junit.jupiter.api.Test) SpringBootTest(org.springframework.boot.test.context.SpringBootTest)

Aggregations

ElementPath (au.csiro.pathling.fhirpath.element.ElementPath)97 ElementPathBuilder (au.csiro.pathling.test.builders.ElementPathBuilder)93 Test (org.junit.jupiter.api.Test)72 SpringBootTest (org.springframework.boot.test.context.SpringBootTest)72 ParserContextBuilder (au.csiro.pathling.test.builders.ParserContextBuilder)57 ParserContext (au.csiro.pathling.fhirpath.parser.ParserContext)51 Row (org.apache.spark.sql.Row)49 DatasetBuilder (au.csiro.pathling.test.builders.DatasetBuilder)47 InvalidUserInputError (au.csiro.pathling.errors.InvalidUserInputError)43 FhirPath (au.csiro.pathling.fhirpath.FhirPath)39 StringLiteralPath (au.csiro.pathling.fhirpath.literal.StringLiteralPath)24 NamedFunctionInput (au.csiro.pathling.fhirpath.function.NamedFunctionInput)15 ResourcePath (au.csiro.pathling.fhirpath.ResourcePath)13 ElementDefinition (au.csiro.pathling.fhirpath.element.ElementDefinition)13 ResourceDatasetBuilder (au.csiro.pathling.test.builders.ResourceDatasetBuilder)13 NonLiteralPath (au.csiro.pathling.fhirpath.NonLiteralPath)11 ResourcePathBuilder (au.csiro.pathling.test.builders.ResourcePathBuilder)10 ParameterizedTest (org.junit.jupiter.params.ParameterizedTest)10 MethodSource (org.junit.jupiter.params.provider.MethodSource)10 UntypedResourcePath (au.csiro.pathling.fhirpath.UntypedResourcePath)6