use of au.csiro.pathling.fhirpath.NonLiteralPath 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.NonLiteralPath in project pathling by aehrc.
the class WhereFunction method invoke.
@Nonnull
@Override
public FhirPath invoke(@Nonnull final NamedFunctionInput input) {
checkUserInput(input.getArguments().size() == 1, "where function accepts one argument");
final NonLiteralPath inputPath = input.getInput();
checkUserInput(input.getArguments().get(0) instanceof NonLiteralPath, "Argument to where function cannot be a literal: " + input.getArguments().get(0).getExpression());
final NonLiteralPath argumentPath = (NonLiteralPath) input.getArguments().get(0);
checkUserInput(argumentPath instanceof BooleanPath && argumentPath.isSingular(), "Argument to where function must be a singular Boolean: " + argumentPath.getExpression());
checkUserInput(argumentPath.getThisColumn().isPresent(), "Argument to where function must be navigable from collection item (use $this): " + argumentPath.getExpression());
final Column argumentValue = argumentPath.getValueColumn();
// The result is the input value if it is equal to true, or null otherwise (signifying the
// absence of a value).
final Column idColumn = argumentPath.getIdColumn();
final Column thisValue = checkPresent(argumentPath.getThisValueColumn());
final Column thisEid = checkPresent(argumentPath.getThisOrderingColumn());
final Column valueColumn = when(argumentValue.equalTo(true), thisValue).otherwise(lit(null));
final String expression = expressionFromInput(input, NAME);
return inputPath.copy(expression, argumentPath.getDataset(), idColumn, inputPath.getEidColumn().map(c -> thisEid), valueColumn, inputPath.isSingular(), inputPath.getThisColumn());
}
use of au.csiro.pathling.fhirpath.NonLiteralPath in project pathling by aehrc.
the class SubsumesFunction method invoke.
@Nonnull
@Override
public FhirPath invoke(@Nonnull final NamedFunctionInput input) {
validateInput(input);
final NonLiteralPath inputFhirPath = input.getInput();
final Dataset<Row> idAndCodingSet = createJoinedDataset(input.getInput(), input.getArguments().get(0));
// Process the subsumption operation per partition, adding a result column to the dataset.
final Column codingPairCol = struct(idAndCodingSet.col(COL_INPUT_CODINGS), idAndCodingSet.col(COL_ARG_CODINGS));
@SuppressWarnings({ "OptionalGetWithoutIsPresent", "TypeMayBeWeakened" }) final SubsumptionMapperWithPreview mapper = new SubsumptionMapperWithPreview(MDC.get("requestId"), input.getContext().getTerminologyServiceFactory().get(), inverted);
final Dataset<Row> resultDataset = SqlExtensions.mapWithPartitionPreview(idAndCodingSet, codingPairCol, SimpleCodingsDecoders::decodeListPair, mapper, StructField.apply("result", DataTypes.BooleanType, true, Metadata.empty()));
final Column resultColumn = col("result");
// Construct a new result expression.
final String expression = expressionFromInput(input, functionName);
return ElementPath.build(expression, resultDataset, inputFhirPath.getIdColumn(), inputFhirPath.getEidColumn(), resultColumn, inputFhirPath.isSingular(), inputFhirPath.getCurrentResource(), inputFhirPath.getThisColumn(), FHIRDefinedType.BOOLEAN);
}
use of au.csiro.pathling.fhirpath.NonLiteralPath in project pathling by aehrc.
the class CombineOperator method invoke.
@Nonnull
@Override
public FhirPath invoke(@Nonnull final OperatorInput input) {
final String expression = Operator.buildExpression(input, NAME);
final FhirPath left = input.getLeft();
final FhirPath right = input.getRight();
final Dataset<Row> leftTrimmed = left.getUnionableDataset(right);
final Dataset<Row> rightTrimmed = right.getUnionableDataset(left);
final int valueColumnIndex = Arrays.asList(leftTrimmed.columns()).indexOf(left.getValueColumn().toString());
final Dataset<Row> dataset = leftTrimmed.union(rightTrimmed);
final String columnName = dataset.columns()[valueColumnIndex];
final DatasetWithColumn datasetWithColumn = createColumn(dataset, dataset.col("`" + columnName + "`"));
final Optional<Column> eidColumn = Optional.of(array(monotonically_increasing_id()));
final Optional<Column> thisColumn = left instanceof NonLiteralPath ? ((NonLiteralPath) left).getThisColumn() : Optional.empty();
return left.combineWith(right, datasetWithColumn.getDataset(), expression, left.getIdColumn(), eidColumn, datasetWithColumn.getColumn(), false, thisColumn);
}
use of au.csiro.pathling.fhirpath.NonLiteralPath 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);
}
Aggregations