Search in sources :

Example 1 with ReferencePath

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

the class ReverseResolveFunction method invoke.

@Nonnull
@Override
public FhirPath invoke(@Nonnull final NamedFunctionInput input) {
    checkUserInput(input.getInput() instanceof ResourcePath, "Input to " + NAME + " function must be a resource: " + input.getInput().getExpression());
    final ResourcePath inputPath = (ResourcePath) input.getInput();
    final String expression = NamedFunction.expressionFromInput(input, NAME);
    checkUserInput(input.getArguments().size() == 1, "reverseResolve function accepts a single argument: " + expression);
    final FhirPath argument = input.getArguments().get(0);
    checkUserInput(argument instanceof ReferencePath, "Argument to reverseResolve function must be a Reference: " + argument.getExpression());
    final ReferencePath referencePath = (ReferencePath) argument;
    // Check that the input type is one of the possible types specified by the argument.
    final Set<ResourceType> argumentTypes = ((ReferencePath) argument).getResourceTypes();
    final ResourceType inputType = inputPath.getResourceType();
    checkUserInput(argumentTypes.contains(inputType), "Reference in argument to reverseResolve does not support input resource type: " + expression);
    // Do a left outer join from the input to the argument dataset using the reference field in the
    // argument.
    final Column joinCondition = referencePath.getResourceEquality(inputPath);
    final Dataset<Row> dataset = join(referencePath.getDataset(), inputPath.getDataset(), joinCondition, JoinType.RIGHT_OUTER);
    // Check the argument for information about the current resource that it originated from - if it
    // is not present, reverse reference resolution will not be possible.
    final NonLiteralPath nonLiteralArgument = (NonLiteralPath) argument;
    checkUserInput(nonLiteralArgument.getCurrentResource().isPresent(), "Argument to reverseResolve must be an element that is navigable from a " + "target resource type: " + expression);
    final ResourcePath currentResource = nonLiteralArgument.getCurrentResource().get();
    final Optional<Column> thisColumn = inputPath.getThisColumn();
    // TODO: Consider removing in the future once we separate ordering from element ID.
    // Create an synthetic element ID column for reverse resolved resources.
    final Column currentResourceValue = currentResource.getValueColumn();
    final WindowSpec windowSpec = Window.partitionBy(inputPath.getIdColumn(), inputPath.getOrderingColumn()).orderBy(currentResourceValue);
    // row_number() is 1-based, and we use 0-based indexes - thus (minus(1)).
    final Column currentResourceIndex = when(currentResourceValue.isNull(), lit(null)).otherwise(row_number().over(windowSpec).minus(lit(1)));
    // We need to add the synthetic EID column to the parser context so that it can be used within
    // joins in certain situations, e.g. extract.
    final Column syntheticEid = inputPath.expandEid(currentResourceIndex);
    final DatasetWithColumn datasetWithEid = QueryHelpers.createColumn(dataset, syntheticEid);
    input.getContext().getNodeIdColumns().putIfAbsent(expression, datasetWithEid.getColumn());
    final ResourcePath result = currentResource.copy(expression, datasetWithEid.getDataset(), inputPath.getIdColumn(), Optional.of(syntheticEid), currentResource.getValueColumn(), false, thisColumn);
    result.setCurrentResource(currentResource);
    return result;
}
Also used : FhirPath(au.csiro.pathling.fhirpath.FhirPath) ResourceType(org.hl7.fhir.r4.model.Enumerations.ResourceType) ResourcePath(au.csiro.pathling.fhirpath.ResourcePath) DatasetWithColumn(au.csiro.pathling.QueryHelpers.DatasetWithColumn) Column(org.apache.spark.sql.Column) DatasetWithColumn(au.csiro.pathling.QueryHelpers.DatasetWithColumn) ReferencePath(au.csiro.pathling.fhirpath.element.ReferencePath) Row(org.apache.spark.sql.Row) NonLiteralPath(au.csiro.pathling.fhirpath.NonLiteralPath) WindowSpec(org.apache.spark.sql.expressions.WindowSpec) Nonnull(javax.annotation.Nonnull)

Example 2 with ReferencePath

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

the class UntypedResourcePathBuilder method build.

@Nonnull
public UntypedResourcePath build() {
    final ReferencePath referencePath = mock(ReferencePath.class);
    when(referencePath.getValueColumn()).thenReturn(valueColumn);
    when(referencePath.isSingular()).thenReturn(singular);
    when(referencePath.getThisColumn()).thenReturn(Optional.ofNullable(thisColumn));
    return UntypedResourcePath.build(referencePath, expression, dataset, idColumn, eidColumn, typeColumn);
}
Also used : ReferencePath(au.csiro.pathling.fhirpath.element.ReferencePath) Nonnull(javax.annotation.Nonnull)

Example 3 with ReferencePath

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

the class ResolveFunction method resolveMonomorphicReference.

@Nonnull
private FhirPath resolveMonomorphicReference(@Nonnull final NamedFunctionInput input, @Nonnull final Database database, @Nonnull final FhirContext fhirContext, @Nonnull final Collection<ResourceType> referenceTypes, final String expression) {
    final ReferencePath referencePath = (ReferencePath) input.getInput();
    // If this is a monomorphic reference, we just need to retrieve the appropriate table and
    // create a dataset with the full resources.
    final ResourceType resourceType = (ResourceType) referenceTypes.toArray()[0];
    final ResourcePath resourcePath = ResourcePath.build(fhirContext, database, resourceType, expression, referencePath.isSingular());
    // Join the resource dataset to the reference dataset.
    final Column joinCondition = referencePath.getResourceEquality(resourcePath);
    final Dataset<Row> dataset = join(referencePath.getDataset(), resourcePath.getDataset(), joinCondition, JoinType.LEFT_OUTER);
    final Column inputId = referencePath.getIdColumn();
    final Optional<Column> inputEid = referencePath.getEidColumn();
    // We need to add the resource ID column to the parser context so that it can be used within
    // joins in certain situations, e.g. extract.
    input.getContext().getNodeIdColumns().putIfAbsent(expression, resourcePath.getElementColumn("id"));
    final ResourcePath result = resourcePath.copy(expression, dataset, inputId, inputEid, resourcePath.getValueColumn(), referencePath.isSingular(), referencePath.getThisColumn());
    result.setCurrentResource(resourcePath);
    return result;
}
Also used : ResourcePath(au.csiro.pathling.fhirpath.ResourcePath) UntypedResourcePath(au.csiro.pathling.fhirpath.UntypedResourcePath) Column(org.apache.spark.sql.Column) ReferencePath(au.csiro.pathling.fhirpath.element.ReferencePath) ResourceType(org.hl7.fhir.r4.model.Enumerations.ResourceType) Row(org.apache.spark.sql.Row) Nonnull(javax.annotation.Nonnull)

Example 4 with ReferencePath

use of au.csiro.pathling.fhirpath.element.ReferencePath 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);
    }
}
Also used : FhirContext(ca.uhn.fhir.context.FhirContext) Database(au.csiro.pathling.io.Database) ReferencePath(au.csiro.pathling.fhirpath.element.ReferencePath) ResourceType(org.hl7.fhir.r4.model.Enumerations.ResourceType) Nonnull(javax.annotation.Nonnull)

Example 5 with ReferencePath

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

the class ResolveFunction method resolvePolymorphicReference.

@Nonnull
private static FhirPath resolvePolymorphicReference(@Nonnull final NamedFunctionInput input, @Nonnull final Database database, @Nonnull final Iterable<ResourceType> referenceTypes, final String expression) {
    final ReferencePath referencePath = (ReferencePath) input.getInput();
    // If this is a polymorphic reference, create a dataset for each reference type, and union
    // them together to produce the target dataset. The dataset will not contain the resources
    // themselves, only a type and identifier for later resolution.
    final Collection<Dataset<Row>> typeDatasets = new ArrayList<>();
    for (final ResourceType referenceType : referenceTypes) {
        if (FhirServer.supportedResourceTypes().contains(referenceType)) {
            // We can't include the full content of the resource, as you can't union two datasets with
            // different schema. The content of the resource is added later, when ofType is invoked.
            final Dataset<Row> typeDatasetWithColumns = database.read(referenceType);
            final Column idColumn = typeDatasetWithColumns.col("id");
            Dataset<Row> typeDataset = typeDatasetWithColumns.withColumn("type", lit(referenceType.toCode()));
            typeDataset = typeDataset.select(idColumn, typeDataset.col("type"));
            typeDatasets.add(typeDataset);
        }
    }
    checkUserInput(!typeDatasets.isEmpty(), "No types within reference are available, cannot resolve: " + referencePath.getExpression());
    final String idColumnName = randomAlias();
    final String targetColumnName = randomAlias();
    Dataset<Row> targetDataset = union(typeDatasets);
    Column targetId = targetDataset.col(targetDataset.columns()[0]);
    Column targetType = targetDataset.col(targetDataset.columns()[1]);
    targetDataset = targetDataset.withColumn(idColumnName, targetId).withColumn(targetColumnName, targetType);
    targetId = targetDataset.col(idColumnName);
    targetType = targetDataset.col(targetColumnName);
    targetDataset = targetDataset.select(targetId, targetType);
    checkNotNull(targetId);
    final Column joinCondition = referencePath.getResourceEquality(targetId, targetType);
    final Dataset<Row> dataset = join(referencePath.getDataset(), targetDataset, joinCondition, JoinType.LEFT_OUTER);
    final Column inputId = referencePath.getIdColumn();
    final Optional<Column> inputEid = referencePath.getEidColumn();
    return UntypedResourcePath.build(referencePath, expression, dataset, inputId, inputEid, targetType);
}
Also used : Column(org.apache.spark.sql.Column) Dataset(org.apache.spark.sql.Dataset) ArrayList(java.util.ArrayList) ReferencePath(au.csiro.pathling.fhirpath.element.ReferencePath) ResourceType(org.hl7.fhir.r4.model.Enumerations.ResourceType) Row(org.apache.spark.sql.Row) Nonnull(javax.annotation.Nonnull)

Aggregations

ReferencePath (au.csiro.pathling.fhirpath.element.ReferencePath)5 Nonnull (javax.annotation.Nonnull)5 ResourceType (org.hl7.fhir.r4.model.Enumerations.ResourceType)4 Column (org.apache.spark.sql.Column)3 Row (org.apache.spark.sql.Row)3 ResourcePath (au.csiro.pathling.fhirpath.ResourcePath)2 DatasetWithColumn (au.csiro.pathling.QueryHelpers.DatasetWithColumn)1 FhirPath (au.csiro.pathling.fhirpath.FhirPath)1 NonLiteralPath (au.csiro.pathling.fhirpath.NonLiteralPath)1 UntypedResourcePath (au.csiro.pathling.fhirpath.UntypedResourcePath)1 Database (au.csiro.pathling.io.Database)1 FhirContext (ca.uhn.fhir.context.FhirContext)1 ArrayList (java.util.ArrayList)1 Dataset (org.apache.spark.sql.Dataset)1 WindowSpec (org.apache.spark.sql.expressions.WindowSpec)1