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);
}
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);
}
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);
}
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);
}
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);
}
Aggregations