Repository navigation
$validate-code: a code not in an enumerated include is looked up once, not once per listed code - #339
Conversation
…ce per listed code When the code is not in an include's concept list, checkConceptSet tries each listed concept as the same concept written in another form (ICD-11's short code and entity id), calling sameConceptDifferentForm for each. That function first locates the code and asks whether it has a classification code at all; only if so does it locate the listed concept and compare. The first part depends only on the code, so it was repeated, with the same answer, once per listed concept: a locate per listed code on every miss. otherFormMatch asks that part once, and compares the listed concepts only when the code has another form. For every code and list it picks the same listed concept (or none) as the old loop; the new test checks that over 200 lists on a two-spelling code system. An empty list still locates nothing. On tx-dev.fhir.org a miss against a 1,185-code RxNorm list took 99 s, and one against a 33,341-code list got no answer in 600 s. A miss against a 300-code list located the code 301 times; it now locates it once or twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
More numbers. These are local, comparing main 9059fb9 with this branch on the same RxNorm and VSAC data. The request is a
On main, the cost grows with the length of the list. The loop at validate.js:1729–1736 calls When the code is unknown to RxNorm altogether (999999999, as in the tx-dev example), each
|
…of the code When the code is not one of an include's listed codes, checkConceptSet asks the code system, for each listed code, whether it is the same concept written another way (sameConceptDifferentForm). That is a locate() per listed code - 33,341 for VSAC Medication Brand Name - and the loop had no deadline check, so it ran to the end however long that took. It now calls deadCheck like the loop after it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Register row M2.
What was wrong
When the code being validated is not in a value set's list, the answer takes about 90 ms for each code in the list (RxNorm on tx-dev). That's 13.6 s for a 148-code VSAC list, 99 s for a 1,185-code list, and no answer within 600 s for VSAC Medication Brand Name (33,341 codes).
On tx-dev (3 Nov) this gives
resultfalse after 13.6 s. Withcode=1000001, which is in the list, it givesresulttrue in 0.1 s. After this change a miss costs about as much as a hit, and the answers are the same.Code path
tx/workers/validate.json main:ValueSetChecker.checkConceptSet(line 1643). When the include has aconceptlist without the code, it tries each listed concept as the code written in another form, at lines 1729–1737:ValueSetChecker.sameConceptDifferentForm(cs, code, enumerated)(line 1609):So for each listed concept, the function asks two questions:
cs, samecode.Step (1) never fires inside the loop, since the loop runs only when no listed code equals
code.For any code system other than ICD-11, (2) always says no, because
classificationCodeis null (cs-api.js:100). Onlycs-icd11.js:307overrides it, and it reads only the located concept:ctxt.codefor a concept, orctxt.classificationCodefor an expression. Nothing in it can see the listed concept. So for RxNorm, LOINC, SNOMED CT and the rest, main does a fulllocate(code)per listed code and then returns false every time.Change
A new
otherFormMatch(cs, code, concepts)asks (2) once. It runs the same loop oversameConceptDifferentFormonly when the answer is yes, and it returns the same listed concept, or null:altMatchis null.otherFormMatchreturns null.conceptlist, or one that contains the code, returns null without a lookup, as main did. (An empty list matters because ICD-11'slocatecan throw for a malformed expression. Main never located the code there, and this doesn't either.)checkConceptSetcallsotherFormMatchin place of the inline loop. Nothing else changes.ICD-11 behaves exactly as before. One thing I noticed but did not change: an ICD-11 postcoordinated expression with a part that has no short code gets no classification code, so (2) says no and other orderings of it aren't tried. That is main's rule, unchanged here.
Test
tests/tx/validate-enumerated-miss.test.js:$validate-codemisses against a 300-code enumerated include, once for a code in the code system and once for a code not in it. Each locates the code fewer than 10 times. Main locates it 301 times. A listed code still validates.otherFormMatchagainst main's loop on a fake two-spelling code system (entity ids, most with a short code, one without). Every spelling, plus unknown codes, is checked against 200 random lists. It picks the same listed concept (or none) every time, and more than 50 of those are real matches.The integration and unit tests fail on main and pass with the change. The HL7 tx-ecosystem ICD-11 cases that validate one spelling against a value set that lists the other (
vs-validate-adhoc-id,vs-validate-adhoc-uri,vs-validate-coding-cc-short) pass unchanged.Results
The touched suites, run in band (
validate-enumerated-miss,validate,validate-issues): 35 of 35 pass. HL7 tx-ecosystem test cases (validator 7.0.3), with this branch merged with the other three from #332's split: 5,048 of 5,061 pass. The 13 failures are the same 13 main 9059fb9 fails:cs-batch-validate,-badand-codings(R4 and R5, and R5 cached), andtranslate-reverse-r5+and-a(R5, plain and cached).Added: a time-limit check in this loop
The loop that compares each listed code with the code being validated had no deadline check. With a slow code system it ran to the end, however long that took, before the next loop noticed the time limit had passed.
otherFormMatchnow callsdeadCheck('checkConceptSet#3')for each listed code, in the same style as the other checks.tests/tx/validate-deadline.test.jsuses a fake slow code system whose codes have another form. It checks that the request is refused atcheckConceptSet#3well before it would have finished, and it fails without the check.🤖 Generated with Claude Code