Match
Text-to-Concept resolution. Default normalize + exact lookup; replaceable with fuzzy, locale-aware, or server-side matchers.
Match
isa.zyins.medications.match(), isa.zyins.conditions.match(), and
isa.zyins.concepts.match() resolve free text to a single canonical
Concept. The default MatchAlgorithm is _makeKey + exact lookup. It
normalizes the text the same way the server does, so the SDK and server always
agree on a match. Unknown text never throws — it flows through as an
UnknownConcept with inputText preserved.
For a deeper tour of the three accessors and the Concept shape, see
Reference matching. This page documents the
MatchAlgorithm adapter itself.
The MatchAlgorithm interface
MatchAlgorithm interfaceimport type { Concept, UnknownConcept } from 'isa-sdk';
interface MatchAlgorithm {
match(query: string, candidates: readonly Concept[]): Concept | UnknownConcept;
}Implementations MUST be pure and synchronous. Never throw on unknown input; return an UnknownConcept instead.
_makeKey — the default normalization
_makeKey — the default normalizationBoth query and candidate id flow through _makeKey, which:
- Uppercases the input.
- Strips every character that is not ASCII alphanumeric.
So "high blood pressure", "High Blood Pressure!", and
"HIGHBLOODPRESSURE" all normalize to "HIGHBLOODPRESSURE". The
server-side MakeKey does the same thing — that's the parity contract.
Concept | UnknownConcept
Concept | UnknownConceptA successful match returns the canonical Concept:
import type {
ConditionConcept,
MedicationConcept,
ReferenceSort as Sort,
} from 'isa-sdk';
interface Concept {
readonly id: string | null;
readonly name: string;
readonly kind: 'condition' | 'medication' | 'nicotine' | 'unknown';
readonly isKnown: boolean;
readonly inputText: string;
conditions(sort?: Sort): readonly ConditionConcept[];
medications(sort?: Sort): readonly MedicationConcept[];
equals(other: Concept): boolean;
}An UnknownConcept has the same shape with id: null, kind: 'unknown',
isKnown: false, and empty accessors:
import { Isa } from 'isa-sdk';
const isa = await Isa.withBearer();
const unknown = isa.zyins.conditions.match('Postural orthostatic tachycardia syndrome');
unknown.id; // null
unknown.kind; // 'unknown'
unknown.isKnown; // false
unknown.inputText; // 'Postural orthostatic tachycardia syndrome'
unknown.medications(); // []You can still pass this straight into prequalify. The engine canonicalizes
server-side when it can, and treats inputText as raw user input when it
cannot.
Symmetric traversal
Every Concept exposes both medications(sort?) and conditions(sort?):
| Call | Returns |
|---|---|
condition.medications(Sort.MostCommonFirst) | Drugs that treat the condition, prescription-count desc. |
medication.conditions(Sort.MostCommonFirst) | Conditions the drug treats, prescription-count desc. |
condition.medications(Sort.Alphabetical) | Same set, alphabetical asc. |
The accessors read the inline treated_with[] / used_for[] arrays from
the dataset row. Empty until the SDK has fetched the bundle once — call
await isa.zyins.datasets.get() first.
Typo-tolerant matching (built-in)
The default matcher does exact, normalized, word-order-invariant lookup — fast
and predictable, but a misspelling misses. The SDK also ships an opt-in
FuzzyMatchAlgorithm that recovers typos for you, so you don't have to build
one. Pass it at construction time:
import { Isa, FuzzyMatchAlgorithm } from 'isa-sdk';
const isa = await Isa.withBearer(undefined, undefined, {
matchAlgorithm: new FuzzyMatchAlgorithm(),
});
await isa.zyins.datasets.get();
// A misspelling that the default (exact) matcher would miss now resolves:
const cond = isa.zyins.conditions.match('corhns'); // recovers Crohn's
const med = isa.zyins.medications.match('sertaline'); // recovers sertralineIt matches in tiers — exact → prefix → Optimal String Alignment (OSA) → Double Metaphone →
synonym. It picks the first tier that has any hit (no blended scoring across
tiers), then ranks within that winning tier by lower edit distance first, and
only breaks a remaining tie by how common the term is:
- Optimal String Alignment (OSA) catches single-edit typos including transpositions a
plain edit-distance misses (corhn's→ Crohn's). - Double Metaphone catches phonetic misses edit-distance can't
(sertaline→ sertraline,tylonol→ tylenol). - synonym matches on a concept's
aliases. It is inert today — the SDK
Conceptsurface does not exposealiases— but the tier is wired for when
the catalog surfaces them, so no signature change is needed later.
Like the default, it never throws: an unrecoverable string returns an
UnknownConcept with the original text preserved, so freeform input always
passes through to the engine. Pass frequencies (a concept id → count map —
keyed by Concept.id, e.g. TS ReadonlyMap<string, number> — from your own
usage data) to bias ranking toward the terms your users actually pick; keying it
by name instead has no effect. Omit it and ties break deterministically by name.
FuzzyMatchAlgorithm ships in all five SDKs (TypeScript, Python, Go, C#, PHP)
and is held to a shared cross-language vector fixture, so a given input resolves
identically in every language.
Replacing the matcher
For needs beyond the built-in fuzzy matcher — a different normalization
(locale-aware casefolding, transliteration), an embedding model, or a
server-side matching service — plug your own in at constructor time:
import { Isa, type MatchAlgorithm, type Concept, type UnknownConcept } from 'isa-sdk';
// Your similarity function and unknown-result builder — the two pieces a
// custom matcher supplies. `match` MUST be pure and synchronous, and MUST
// return an UnknownConcept (never throw) on a miss.
declare function trigramSimilarity(a: string, b: string): number;
declare function makeUnknown(query: string): UnknownConcept;
class TrigramMatcher implements MatchAlgorithm {
match(query: string, candidates: readonly Concept[]): Concept | UnknownConcept {
let best: { c: Concept; score: number } | null = null;
for (const c of candidates) {
const s = trigramSimilarity(query, c.name);
if (!best || s > best.score) best = { c, score: s };
}
if (best && best.score >= 0.8) return best.c;
return makeUnknown(query);
}
}
const isa = await Isa.withBearer(undefined, undefined, { matchAlgorithm: new TrigramMatcher() });
const cond = isa.zyins.conditions.match('hyprtension');Substitute when:
- The product needs fuzzy matching (Levenshtein, trigram, embedding).
- The catalog uses a different normalization (locale-aware casefolding, transliteration of Greek/Cyrillic).
- You want to replace the lookup strategy while keeping the traversal accessors (
medications()/conditions()) — they read the bundle's inline rows, not the matcher.
Direct access — isa.zyins.matcher
isa.zyins.matcherThe matcher is exposed as isa.zyins.matcher for callers that want to
match against an arbitrary candidate pool (e.g. a subset filtered by
state):
import { Isa } from 'isa-sdk';
const isa = await Isa.withBearer();
await isa.zyins.datasets.get();
const conditions = isa.zyins.conditions.list();
const cardiac = conditions.filter((c) => c.name.includes('HEART'));
const hit = isa.zyins.matcher.match('chest pain', cardiac);Code samples
TypeScript
import { Isa } from 'isa-sdk';
const isa = await Isa.withBearer();
await isa.zyins.datasets.get();
const hbp = isa.zyins.conditions.match('high blood pressure');
hbp.id; // 'cond_01KSR2WVAGC05ZGR6FA4QYEA8X'
hbp.isKnown; // true
hbp.kind; // 'condition'Python
await isa.zyins.datasets.get()
hbp = isa.zyins.conditions.match('high blood pressure')
print(hbp.id, hbp.is_known, hbp.kind)Go
ctx := context.Background()
isa.Zyins.Datasets.Get(ctx)
hbp := isa.Zyins.Conditions.Match("high blood pressure")
fmt.Println(hbp.ID, hbp.IsKnown, hbp.Kind)PHP
$isa->zyins->datasets->get();
$hbp = $isa->zyins->conditions->match('high blood pressure');
echo $hbp->id, ' ', var_export($hbp->isKnown, true), ' ', $hbp->kind, PHP_EOL;curl
match is an SDK-side operation; there is no dedicated REST endpoint. The
canonical data flows through GET /v3/datasets.
See also
- Reference matching — the three accessors and Concept tour.
- Autocomplete — ranked Suggestion lists.
- Autocorrect — pre-match typo correction.
Updated 8 days ago