aehrc / aehrc/pathling

Support subsetOf() and supersetOf() FHIRPath functions

Open
#2,706 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Java
Stars
134
Forks
24
Avg merge
1d 15h
Merged PRs (30d)
8

Description

Spec link: https://hl7.org/fhirpath/#subsetofother--collection--boolean and https://hl7.org/fhirpath/#supersetofother--collection--boolean

Split out of #2385, which implemented the other 7 FHIRPath existence functions (all, allTrue, allFalse, anyTrue, anyFalse, isDistinct, distinct).

Why these two are separate

subsetOf(other : collection) and supersetOf(other : collection) each take a plain collection-typed argument that must be evaluated against the surrounding iteration focus ($this), not against %context (the root resource) — per the FHIRPath spec's general argument-evaluation rule (only expression-typed parameters like where/select/all's criteria get their own $this scope; everything else, including collection-typed arguments and operator operands, inherits whatever $this already is). This is confirmed empirically by the fhirpath.js reference implementation (src/fhirpath.js, makeParam, the "AnyAtRoot"/"Any" branch: const $this = ctx.$this || ctx.dataRoot;) — the same code path used by subsetOf, supersetOf, union, combine, and every infix operator.

Pathling's FunctionParameterResolver.resolveArgument (fhirpath/src/main/java/au/csiro/pathling/fhirpath/function/resolver/FunctionParameterResolver.java) instead evaluates plain Collection-typed arguments against evaluationContext.getInputContext(), which is fixed at construction to the root resource for the whole query — not the current iteration item. Concretely:

Patient.name.select(('James').subsetOf(given))

resolves given against the root Patient, not the current name item, because given doesn't exist on Patient this returns the wrong result rather than the per-name given array.

This is not a new problem: it's the exact same gap that union()/combine() (#2384, PR #2587) hit and deliberately worked around via parser-level desugaring into EvalOperator ASTs rather than fixing the resolver. See the archived design doc at openspec/changes/archive/2026-04-11-add-combining-functions/design.md for the full write-up, including the explicit Non-Goal: "Refactoring Composite/EvalFunction/FunctionParameterResolver to propagate an 'outer focus' through chained expressions. That is a broader architectural change that would benefit future Collection-argument functions but is out of scope for this issue."

subsetOf/supersetOf are the first functions since union/combine to need this — unlike union, there's no existing operator to desugar into, so this would need a new operator (or pair of operators, since subsetOf/supersetOf aren't the same relation reversed at the AST level the way combine mirrors |) plus a parser desugaring rule, following the same pattern CombineOperator established.

Scope

  • Design a fix (or accept the same operator-desugaring pattern) for subsetOf(other) and supersetOf(other), ensuring x.subsetOf(y) and x.supersetOf(y) correctly resolve y against the enclosing iteration focus, matching the spec's worked examples and the fhirpath-js reference corpus (fhirpath/src/test/resources/fhirpath-js/cases/5.1_existence.yaml, sections 5.1.9/5.1.10).
  • This is a FHIRPath-framework-level change (touches parser/ and/or operator/), so per this project's implement-pathling skill it should go through an OpenSpec change before code exists, the same way #2587 did.

Implementation reference

A draft, spec-correct ExistenceLogic.subsetOf(Collection, Collection) helper (forall/exists over the comparator-aware equality machinery, with the empty-collection short-circuit rules from the spec) was written and verified correct in isolation while implementing #2385, then reverted once the argument-scoping bug was found — see that PR's history for the reasoning and a working reference implementation of the membership-testing logic itself (the part that isn't affected by this gap).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with FunctionParameterResolver.java, the parser/ and operator/ implementations, and the archived combine design at openspec/changes/archive/2026-04-11-add-combining-functions/design.md. Compare the fhirpath-js cases in fhirpath/src/test/resources/fhirpath-js/cases/5.1_existence.yaml with the existing CombineOperator pattern and the reverted #2385 implementation. Done means subsetOf() and supersetOf() resolve collection arguments against the enclosing iteration focus and pass the relevant spec examples.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend, compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.