Search in sources :

Example 1 with ElementDefinition

use of au.csiro.pathling.fhirpath.element.ElementDefinition 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 2 with ElementDefinition

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

the class PathTraversalOperator method invoke.

/**
 * Invokes this operator with the specified inputs.
 *
 * @param input A {@link PathTraversalInput} object
 * @return A {@link FhirPath} object representing the resulting expression
 */
@Nonnull
public ElementPath invoke(@Nonnull final PathTraversalInput input) {
    checkUserInput(input.getLeft() instanceof NonLiteralPath, "Path traversal operator cannot be invoked on a literal value: " + input.getLeft().getExpression());
    final NonLiteralPath left = (NonLiteralPath) input.getLeft();
    final String right = input.getRight();
    // If the input expression is the same as the input context, the child will be the start of the
    // expression. This is to account for where we omit the expression that represents the input
    // expression, e.g. "gender" instead of "Patient.gender".
    final String inputContextExpression = input.getContext().getInputContext().getExpression();
    final String expression = left.getExpression().equals(inputContextExpression) ? right : left.getExpression() + "." + right;
    final Optional<ElementDefinition> optionalChild = left.getChildElement(right);
    checkUserInput(optionalChild.isPresent(), "No such child: " + expression);
    final ElementDefinition childDefinition = optionalChild.get();
    final Dataset<Row> leftDataset = left.getDataset();
    final Column field;
    if (ExtensionSupport.EXTENSION_ELEMENT_NAME().equals(right)) {
        // Lookup the extensions by _fid in the extension container.
        field = left.getExtensionContainerColumn().apply(getValueField(left, ExtensionSupport.FID_FIELD_NAME()));
    } else {
        field = getValueField(left, right);
    }
    // If the element has a max cardinality of more than one, it will need to be "exploded" out into
    // multiple rows.
    final boolean maxCardinalityOfOne = childDefinition.getMaxCardinality() == 1;
    final boolean resultSingular = left.isSingular() && maxCardinalityOfOne;
    final Column valueColumn;
    final Optional<Column> eidColumnCandidate;
    final Dataset<Row> resultDataset;
    if (maxCardinalityOfOne) {
        valueColumn = field;
        eidColumnCandidate = left.getEidColumn();
        resultDataset = leftDataset;
    } else {
        final MutablePair<Column, Column> valueAndEidColumns = new MutablePair<>();
        final Dataset<Row> explodedDataset = left.explodeArray(leftDataset, field, valueAndEidColumns);
        final DatasetWithColumnMap datasetWithColumnMap = createColumns(explodedDataset, valueAndEidColumns.getLeft(), valueAndEidColumns.getRight());
        resultDataset = datasetWithColumnMap.getDataset();
        valueColumn = datasetWithColumnMap.getColumn(valueAndEidColumns.getLeft());
        eidColumnCandidate = Optional.of(datasetWithColumnMap.getColumn(valueAndEidColumns.getRight()));
    }
    final Optional<Column> eidColumn = resultSingular ? Optional.empty() : eidColumnCandidate;
    // If there is an element ID column, we need to add it to the parser context so that it can
    // be used within joins in certain situations, e.g. extract.
    eidColumn.ifPresent(c -> input.getContext().getNodeIdColumns().putIfAbsent(expression, c));
    return ElementPath.build(expression, resultDataset, left.getIdColumn(), eidColumn, valueColumn, resultSingular, left.getCurrentResource(), left.getThisColumn(), childDefinition);
}
Also used : MutablePair(org.apache.commons.lang3.tuple.MutablePair) DatasetWithColumnMap(au.csiro.pathling.QueryHelpers.DatasetWithColumnMap) Column(org.apache.spark.sql.Column) ElementDefinition(au.csiro.pathling.fhirpath.element.ElementDefinition) Row(org.apache.spark.sql.Row) NonLiteralPath(au.csiro.pathling.fhirpath.NonLiteralPath) Nonnull(javax.annotation.Nonnull)

Example 3 with ElementDefinition

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

the class ExtensionFunctionTest method testExtensionOnElements.

@Test
public void testExtensionOnElements() {
    final ParserContext parserContext = new ParserContextBuilder(spark, fhirContext).build();
    // Construct element dataset from the resource dataset so that the resource path
    // can be used as the current resource for this element path
    // Note: this resource path is not singular as this will be a base for elements.
    final Dataset<Row> resourceLikeDataset = new ResourceDatasetBuilder(spark).withIdColumn().withEidColumn().withStructColumn("name", DataTypes.StringType).withStructColumn("_fid", DataTypes.IntegerType).withStructValueColumn().withExtensionColumn().withRow("observation-1", makeEid(0), RowFactory.create("name1", 0), oneEntryMap(0, MANY_MY_EXTENSIONS)).withRow("observation-2", makeEid(0), RowFactory.create("name2", 1), oneEntryMap(1, ONE_MY_EXTENSION)).withRow("observation-3", makeEid(0), RowFactory.create("name3", 2), oneEntryMap(2, NO_MY_EXTENSIONS)).withRow("observation-4", makeEid(0), RowFactory.create("name4", 3), oneEntryMap(3, ONE_MY_EXTENSION)).withRow("observation-4", makeEid(1), RowFactory.create("name5", 4), oneEntryMap(3, ONE_MY_EXTENSION)).withRow("observation-5", makeEid(0), null, null).withRow("observation-5", makeEid(1), null, null).build();
    when(database.read(ResourceType.OBSERVATION)).thenReturn(resourceLikeDataset);
    final ResourcePath baseResourcePath = ResourcePath.build(fhirContext, database, ResourceType.OBSERVATION, "Observation", false);
    final Dataset<Row> elementDataset = toElementDataset(resourceLikeDataset, baseResourcePath);
    final ElementDefinition codeDefinition = checkPresent(FhirHelpers.getChildOfResource(fhirContext, "Observation", "code"));
    final ElementPath inputPath = new ElementPathBuilder(spark).fhirType(FHIRDefinedType.CODEABLECONCEPT).definition(codeDefinition).dataset(elementDataset).idAndEidAndValueColumns().expression("code").singular(false).currentResource(baseResourcePath).buildDefined();
    final StringLiteralPath argumentExpression = StringLiteralPath.fromString("'" + "uuid:myExtension" + "'", inputPath);
    final NamedFunctionInput extensionInput = new NamedFunctionInput(parserContext, inputPath, Collections.singletonList(argumentExpression));
    final NamedFunction extension = NamedFunction.getInstance("extension");
    final FhirPath result = extension.invoke(extensionInput);
    final Dataset<Row> expectedResult = new DatasetBuilder(spark).withIdColumn().withEidColumn().withStructTypeColumns(DatasetBuilder.SIMPLE_EXTENSION_TYPE).withRow("observation-1", makeEid(0, 0), null).withRow("observation-1", makeEid(0, 1), null).withRow("observation-1", makeEid(0, 2), MANY_EXT_ROW_1).withRow("observation-1", makeEid(0, 3), MANY_EXT_ROW_2).withRow("observation-2", makeEid(0, 0), null).withRow("observation-2", makeEid(0, 1), ONE_EXT_ROW_1).withRow("observation-3", makeEid(0, 0), null).withRow("observation-3", makeEid(0, 1), null).withRow("observation-4", makeEid(0, 0), null).withRow("observation-4", makeEid(0, 1), ONE_EXT_ROW_1).withRow("observation-4", makeEid(1, 0), null).withRow("observation-5", makeEid(0, 0), null).withRow("observation-5", makeEid(1, 0), null).buildWithStructValue();
    assertThat(result).hasExpression("code.extension('uuid:myExtension')").isNotSingular().isElementPath(ElementPath.class).hasFhirType(FHIRDefinedType.EXTENSION).selectOrderedResultWithEid().hasRows(expectedResult);
}
Also used : StringLiteralPath(au.csiro.pathling.fhirpath.literal.StringLiteralPath) FhirPath(au.csiro.pathling.fhirpath.FhirPath) ParserContextBuilder(au.csiro.pathling.test.builders.ParserContextBuilder) ResourcePath(au.csiro.pathling.fhirpath.ResourcePath) ElementPath(au.csiro.pathling.fhirpath.element.ElementPath) ResourceDatasetBuilder(au.csiro.pathling.test.builders.ResourceDatasetBuilder) Row(org.apache.spark.sql.Row) ElementDefinition(au.csiro.pathling.fhirpath.element.ElementDefinition) 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 4 with ElementDefinition

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

the class ResolveFunctionTest method polymorphicResolve.

@Test
void polymorphicResolve() {
    final Optional<ElementDefinition> optionalDefinition = FhirHelpers.getChildOfResource(fhirContext, "Encounter", "subject");
    assertTrue(optionalDefinition.isPresent());
    final ElementDefinition definition = optionalDefinition.get();
    final Dataset<Row> referenceDataset = new DatasetBuilder(spark).withIdColumn().withStructTypeColumns(referenceStructType()).withRow("encounter-1", RowFactory.create(null, "Patient/patient-1", null)).withRow("encounter-2", RowFactory.create(null, "Patient/patient-3", null)).withRow("encounter-3", RowFactory.create(null, "Patient/patient-2", null)).withRow("encounter-4", RowFactory.create(null, "Patient/patient-2", null)).withRow("encounter-5", RowFactory.create(null, "Group/group-1", null)).buildWithStructValue();
    final ElementPath referencePath = new ElementPathBuilder(spark).expression("Encounter.subject").dataset(referenceDataset).idAndValueColumns().singular(true).definition(definition).buildDefined();
    final Dataset<Row> patientDataset = new ResourceDatasetBuilder(spark).withIdColumn().withColumn(DataTypes.StringType).withColumn(DataTypes.BooleanType).withRow("patient-1", "female", true).withRow("patient-2", "female", false).withRow("patient-3", "male", true).build();
    when(database.read(ResourceType.PATIENT)).thenReturn(patientDataset);
    final Dataset<Row> groupDataset = new ResourceDatasetBuilder(spark).withIdColumn().withColumn(DataTypes.StringType).withColumn(DataTypes.BooleanType).withRow("group-1", "Some group", true).build();
    when(database.read(ResourceType.GROUP)).thenReturn(groupDataset);
    final NamedFunctionInput resolveInput = buildFunctionInput(referencePath);
    final FhirPath result = invokeResolve(resolveInput);
    assertTrue(result instanceof UntypedResourcePath);
    assertThat((UntypedResourcePath) result).hasExpression("Encounter.subject.resolve()").isSingular();
    final Dataset<Row> expectedDataset = new DatasetBuilder(spark).withIdColumn().withTypeColumn().withStructTypeColumns(referenceStructType()).withRow("encounter-1", "Patient", RowFactory.create(null, "Patient/patient-1", null)).withRow("encounter-2", "Patient", RowFactory.create(null, "Patient/patient-3", null)).withRow("encounter-3", "Patient", RowFactory.create(null, "Patient/patient-2", null)).withRow("encounter-4", "Patient", RowFactory.create(null, "Patient/patient-2", null)).withRow("encounter-5", "Group", RowFactory.create(null, "Group/group-1", null)).buildWithStructValue();
    assertThat((UntypedResourcePath) result).selectUntypedResourceResult().hasRows(expectedDataset);
}
Also used : ElementPath(au.csiro.pathling.fhirpath.element.ElementPath) FhirPath(au.csiro.pathling.fhirpath.FhirPath) ResourceDatasetBuilder(au.csiro.pathling.test.builders.ResourceDatasetBuilder) ElementDefinition(au.csiro.pathling.fhirpath.element.ElementDefinition) Row(org.apache.spark.sql.Row) DatasetBuilder(au.csiro.pathling.test.builders.DatasetBuilder) ResourceDatasetBuilder(au.csiro.pathling.test.builders.ResourceDatasetBuilder) UntypedResourcePath(au.csiro.pathling.fhirpath.UntypedResourcePath) ElementPathBuilder(au.csiro.pathling.test.builders.ElementPathBuilder) Test(org.junit.jupiter.api.Test) SpringBootTest(org.springframework.boot.test.context.SpringBootTest)

Example 5 with ElementDefinition

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

the class ResolveFunctionTest method polymorphicResolveAnyType.

@Test
void polymorphicResolveAnyType() {
    final Optional<ElementDefinition> optionalDefinition = FhirHelpers.getChildOfResource(fhirContext, "Condition", "evidence").flatMap(child -> child.getChildElement("detail"));
    assertTrue(optionalDefinition.isPresent());
    final ElementDefinition definition = optionalDefinition.get();
    TestHelpers.mockAllEmptyResources(database, spark, fhirEncoders);
    final Dataset<Row> referenceDataset = new DatasetBuilder(spark).withIdColumn().withEidColumn().withStructTypeColumns(referenceStructType()).withRow("condition-1", makeEid(0), RowFactory.create(null, "Observation/observation-1", null)).withRow("condition-2", makeEid(0), RowFactory.create(null, "ClinicalImpression/clinicalimpression-1", null)).buildWithStructValue();
    final ElementPath referencePath = new ElementPathBuilder(spark).expression("Condition.evidence.detail").dataset(referenceDataset).idAndEidAndValueColumns().singular(false).definition(definition).buildDefined();
    final Dataset<Row> observationDataset = new ResourceDatasetBuilder(spark).withIdColumn().withColumn(DataTypes.StringType).withRow("observation-1", "registered").build();
    when(database.read(ResourceType.OBSERVATION)).thenReturn(observationDataset);
    final Dataset<Row> clinicalImpressionDataset = new ResourceDatasetBuilder(spark).withIdColumn().withColumn(DataTypes.StringType).withRow("clinicalimpression-1", "in-progress").build();
    when(database.read(ResourceType.CLINICALIMPRESSION)).thenReturn(clinicalImpressionDataset);
    final NamedFunctionInput resolveInput = buildFunctionInput(referencePath);
    final FhirPath result = invokeResolve(resolveInput);
    assertTrue(result instanceof UntypedResourcePath);
    assertThat((UntypedResourcePath) result).hasExpression("Condition.evidence.detail.resolve()").isNotSingular();
    final Dataset<Row> expectedDataset = new DatasetBuilder(spark).withIdColumn().withTypeColumn().withStructTypeColumns(referenceStructType()).withRow("condition-1", "Observation", RowFactory.create(null, "Observation/observation-1", null)).withRow("condition-2", "ClinicalImpression", RowFactory.create(null, "ClinicalImpression/clinicalimpression-1", null)).buildWithStructValue();
    assertThat((UntypedResourcePath) result).selectUntypedResourceResult().hasRows(expectedDataset);
}
Also used : ElementPath(au.csiro.pathling.fhirpath.element.ElementPath) FhirPath(au.csiro.pathling.fhirpath.FhirPath) ResourceDatasetBuilder(au.csiro.pathling.test.builders.ResourceDatasetBuilder) ElementDefinition(au.csiro.pathling.fhirpath.element.ElementDefinition) Row(org.apache.spark.sql.Row) DatasetBuilder(au.csiro.pathling.test.builders.DatasetBuilder) ResourceDatasetBuilder(au.csiro.pathling.test.builders.ResourceDatasetBuilder) UntypedResourcePath(au.csiro.pathling.fhirpath.UntypedResourcePath) ElementPathBuilder(au.csiro.pathling.test.builders.ElementPathBuilder) Test(org.junit.jupiter.api.Test) SpringBootTest(org.springframework.boot.test.context.SpringBootTest)

Aggregations

ElementDefinition (au.csiro.pathling.fhirpath.element.ElementDefinition)18 ElementPathBuilder (au.csiro.pathling.test.builders.ElementPathBuilder)16 Row (org.apache.spark.sql.Row)16 Test (org.junit.jupiter.api.Test)15 SpringBootTest (org.springframework.boot.test.context.SpringBootTest)15 FhirPath (au.csiro.pathling.fhirpath.FhirPath)14 DatasetBuilder (au.csiro.pathling.test.builders.DatasetBuilder)14 ElementPath (au.csiro.pathling.fhirpath.element.ElementPath)13 ParserContext (au.csiro.pathling.fhirpath.parser.ParserContext)12 ParserContextBuilder (au.csiro.pathling.test.builders.ParserContextBuilder)11 ResourceDatasetBuilder (au.csiro.pathling.test.builders.ResourceDatasetBuilder)8 NamedFunctionInput (au.csiro.pathling.fhirpath.function.NamedFunctionInput)6 StringLiteralPath (au.csiro.pathling.fhirpath.literal.StringLiteralPath)6 ResourcePath (au.csiro.pathling.fhirpath.ResourcePath)5 UntypedResourcePath (au.csiro.pathling.fhirpath.UntypedResourcePath)3 CodingPath (au.csiro.pathling.fhirpath.element.CodingPath)3 ConceptTranslator (au.csiro.pathling.terminology.ConceptTranslator)3 Column (org.apache.spark.sql.Column)3 ConceptMapEquivalence (org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence)3 InvalidUserInputError (au.csiro.pathling.errors.InvalidUserInputError)2