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