fix(sqlite): negate token :not at the resource level - #480
Open
angela-helios wants to merge 1 commit into
Open
Conversation
The :not modifier wrapped the row predicate in NOT(...) inside the per-parameter subquery, which asks "does any indexed row differ from the value" instead of FHIR's "does no value match". Multi-valued elements (identifier arrays, CodeableConcept codings, communication.language) leaked back in through their non-matching rows, and resources without the element at all -- matches, per the spec -- never joined the index and were silently dropped. The token handler now always builds the positive predicate and the query builder flips the subquery membership to resource_id NOT IN (...) when the modifier is :not, which handles both failure modes in one place. Postgres already implemented these semantics; this aligns sqlite with it. Closes #473
Code reviewFound 2 issues — both regressions in contained-resource handling introduced by this PR's |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #473
The bug
On sqlite, the token
:notmodifier negated the row predicate inside the per-parameter subquery:That asks "does any indexed row differ from the value", not FHIR's "does no value of the parameter match". Two failure modes, both seen in the #448 sweep:
Patient?language:not=urn:ietf:bcp:47|en-USreturned all 27 patients — every patient with a second communication row (or a second coding, or a second identifier) satisfiedNOT(row = x)via its other rows. Same story forObservation?combo-code:not(1400/1400) andExplanationOfBenefit?status:not=active(matched a phantom contained-resource row).Encounter?reason-code:not=xreturned 245 instead of ≥370 — but the spec is explicit that absent-element resources match:not.Only single-row scalar params (
Patient?gender:not=male) happened to behave.The fix
The token handler now always builds the positive predicate, and the query builder negates at the resource level by flipping the subquery membership:
One change handles both failure modes: a resource is excluded iff any of its rows matches, and resources with no rows for the parameter fall out of the subquery and are kept. Multi-value
:not=a,bcomposes correctly too (the OR'd positives sit inside the NOT IN).Postgres already implements these semantics (
postgres_integration_search_not_modifierasserts the missing-element case); this aligns sqlite with it.Tests
:notfragment stays positive (negation is the builder's job).gender:not=malereturns the no-gender patient) and multi-valued exclusion (an Observation coded both LOINC and SNOMED must not leak through its SNOMED coding).