fhir-persistence 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +1 -1
- package/dist/cjs/index.cjs +19 -10
- package/dist/cjs/index.cjs.map +2 -2
- package/dist/cjs/index.d.ts +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.mjs +19 -10
- package/dist/esm/index.mjs.map +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/lib/migration/migration-generator.d.ts.map +1 -1
- package/dist/lib/search/where-builder.d.ts +2 -2
- package/dist/lib/search/where-builder.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.0] - 2025-03-16
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
#### PostgreSQL Migration Path Fixes
|
|
13
|
+
|
|
14
|
+
- **`migration-generator.ts`** — IG migration path now creates PostgreSQL extensions (`pg_trgm`, `btree_gin`) and helper function (`token_array_to_text`) before generating GIN indexes, fixing "no default operator class for access method gin" error
|
|
15
|
+
- **`where-builder.ts`** — Lookup table EXISTS subqueries now use fully-qualified `"ResourceType"."id"` instead of ambiguous `"id"`, fixing PostgreSQL "operator does not exist: text = integer" error in name/address/telecom searches
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **`buildWhereFragment` (v1)** — Added optional `resourceType` parameter, passed to `buildLookupTableFragment`
|
|
20
|
+
- **`buildWhereFragmentV2` (v2)** — Added optional `resourceType` parameter, passed to `buildLookupTableFragmentV2`
|
|
21
|
+
- Both `buildLookupTableFragment*` functions now generate `outerIdRef = "ResourceType"."id"` to eliminate column name ambiguity in PostgreSQL
|
|
22
|
+
|
|
23
|
+
### Test Coverage
|
|
24
|
+
|
|
25
|
+
- **1014 total tests** (1006 passing, 8 skipped) across 56 test files — no regressions
|
|
26
|
+
- Updated 7 lookup table test assertions to use qualified column references
|
|
27
|
+
|
|
8
28
|
## [0.4.0] - 2025-03-15
|
|
9
29
|
|
|
10
30
|
### Fixed
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ Embedded FHIR R4 persistence layer — CRUD, search, indexing, and schema migrat
|
|
|
5
5
|
[](https://www.npmjs.com/package/fhir-persistence)
|
|
6
6
|
[](./LICENSE)
|
|
7
7
|
|
|
8
|
-
> **v0.
|
|
8
|
+
> **v0.5.0** — PostgreSQL migration path complete: GIN extensions + lookup table fixes
|
|
9
9
|
|
|
10
10
|
## Features
|
|
11
11
|
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -5202,12 +5202,12 @@ function arrayContainsLikeV2(col, value, dialect) {
|
|
|
5202
5202
|
}
|
|
5203
5203
|
return { sql: `EXISTS (SELECT 1 FROM json_each(${col}) WHERE json_each.value LIKE ?)`, values: [value] };
|
|
5204
5204
|
}
|
|
5205
|
-
function buildWhereFragmentV2(impl, param, dialect) {
|
|
5205
|
+
function buildWhereFragmentV2(impl, param, dialect, resourceType) {
|
|
5206
5206
|
if (param.modifier === "missing") {
|
|
5207
5207
|
return buildMissingFragmentV2(impl, param);
|
|
5208
5208
|
}
|
|
5209
5209
|
if (impl.strategy === "lookup-table") {
|
|
5210
|
-
return buildLookupTableFragmentV2(impl, param);
|
|
5210
|
+
return buildLookupTableFragmentV2(impl, param, resourceType ?? "Resource");
|
|
5211
5211
|
}
|
|
5212
5212
|
if (impl.strategy === "token-column") {
|
|
5213
5213
|
return buildTokenColumnFragmentV2(impl, param, dialect);
|
|
@@ -5235,7 +5235,7 @@ function buildMissingFragmentV2(impl, param) {
|
|
|
5235
5235
|
const col = quoteColumn(impl.columnName);
|
|
5236
5236
|
return { sql: isMissing ? `${col} IS NULL` : `${col} IS NOT NULL`, values: [] };
|
|
5237
5237
|
}
|
|
5238
|
-
function buildLookupTableFragmentV2(impl, param) {
|
|
5238
|
+
function buildLookupTableFragmentV2(impl, param, resourceType) {
|
|
5239
5239
|
const mapping = LOOKUP_TABLE_MAP[impl.code];
|
|
5240
5240
|
if (!mapping) {
|
|
5241
5241
|
const sortCol = quoteColumn(`__${impl.columnName}Sort`);
|
|
@@ -5245,25 +5245,26 @@ function buildLookupTableFragmentV2(impl, param) {
|
|
|
5245
5245
|
}
|
|
5246
5246
|
const { table, column } = mapping;
|
|
5247
5247
|
const colRef = `__lookup."${column}"`;
|
|
5248
|
+
const outerIdRef = `"${resourceType}"."id"`;
|
|
5248
5249
|
if (param.modifier === "exact") {
|
|
5249
5250
|
if (param.values.length === 1) {
|
|
5250
|
-
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" =
|
|
5251
|
+
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" = ${outerIdRef} AND ${colRef} = ?)`, values: [param.values[0]] };
|
|
5251
5252
|
}
|
|
5252
5253
|
const conds2 = param.values.map(() => `${colRef} = ?`);
|
|
5253
|
-
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" =
|
|
5254
|
+
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" = ${outerIdRef} AND (${conds2.join(" OR ")}))`, values: [...param.values] };
|
|
5254
5255
|
}
|
|
5255
5256
|
if (param.modifier === "contains") {
|
|
5256
5257
|
if (param.values.length === 1) {
|
|
5257
|
-
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" =
|
|
5258
|
+
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" = ${outerIdRef} AND LOWER(${colRef}) LIKE ?)`, values: [`%${param.values[0].toLowerCase()}%`] };
|
|
5258
5259
|
}
|
|
5259
5260
|
const conds2 = param.values.map(() => `LOWER(${colRef}) LIKE ?`);
|
|
5260
|
-
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" =
|
|
5261
|
+
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" = ${outerIdRef} AND (${conds2.join(" OR ")}))`, values: param.values.map((v) => `%${v.toLowerCase()}%`) };
|
|
5261
5262
|
}
|
|
5262
5263
|
if (param.values.length === 1) {
|
|
5263
|
-
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" =
|
|
5264
|
+
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" = ${outerIdRef} AND LOWER(${colRef}) LIKE ?)`, values: [`${param.values[0].toLowerCase()}%`] };
|
|
5264
5265
|
}
|
|
5265
5266
|
const conds = param.values.map(() => `LOWER(${colRef}) LIKE ?`);
|
|
5266
|
-
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" =
|
|
5267
|
+
return { sql: `EXISTS (SELECT 1 FROM "${table}" __lookup WHERE __lookup."resourceId" = ${outerIdRef} AND (${conds.join(" OR ")}))`, values: param.values.map((v) => `${v.toLowerCase()}%`) };
|
|
5267
5268
|
}
|
|
5268
5269
|
function buildStringFragmentV2(impl, param) {
|
|
5269
5270
|
const col = quoteColumn(impl.columnName);
|
|
@@ -5432,7 +5433,7 @@ function buildWhereClauseV2(params, registry, resourceType, dialect) {
|
|
|
5432
5433
|
}
|
|
5433
5434
|
const impl = resolveImplV2(param, registry, resourceType);
|
|
5434
5435
|
if (!impl) continue;
|
|
5435
|
-
const fragment = buildWhereFragmentV2(impl, param, dialect);
|
|
5436
|
+
const fragment = buildWhereFragmentV2(impl, param, dialect, resourceType);
|
|
5436
5437
|
if (fragment) {
|
|
5437
5438
|
fragments.push(fragment);
|
|
5438
5439
|
}
|
|
@@ -7280,6 +7281,14 @@ function generateMigration(deltas, dialect) {
|
|
|
7280
7281
|
const down = [];
|
|
7281
7282
|
const reindexDeltas = [];
|
|
7282
7283
|
const descriptions = [];
|
|
7284
|
+
const needsPgExtensions = deltas.some((d) => d.kind === "ADD_TABLE" || d.kind === "ADD_INDEX");
|
|
7285
|
+
if (dialect === "postgres" && needsPgExtensions) {
|
|
7286
|
+
up.push("CREATE EXTENSION IF NOT EXISTS pg_trgm;");
|
|
7287
|
+
up.push("CREATE EXTENSION IF NOT EXISTS btree_gin;");
|
|
7288
|
+
up.push(
|
|
7289
|
+
`CREATE OR REPLACE FUNCTION token_array_to_text(arr text[]) RETURNS text LANGUAGE sql IMMUTABLE AS $$ SELECT array_to_string(arr, ' ') $$;`
|
|
7290
|
+
);
|
|
7291
|
+
}
|
|
7283
7292
|
for (const delta of deltas) {
|
|
7284
7293
|
switch (delta.kind) {
|
|
7285
7294
|
case "ADD_TABLE": {
|