From 528d5750e22029b1444ef49db596eb1b7c233967 Mon Sep 17 00:00:00 2001 From: Robin Arnold Date: Thu, 25 Nov 2021 15:50:57 +0000 Subject: [PATCH 1/2] issue 3044 - use correct TransformerFactory impl class Signed-off-by: Robin Arnold --- .../main/java/com/ibm/fhir/model/util/XMLSupport.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/fhir-model/src/main/java/com/ibm/fhir/model/util/XMLSupport.java b/fhir-model/src/main/java/com/ibm/fhir/model/util/XMLSupport.java index d6a9ce694a9..7d3cd8b900b 100644 --- a/fhir-model/src/main/java/com/ibm/fhir/model/util/XMLSupport.java +++ b/fhir-model/src/main/java/com/ibm/fhir/model/util/XMLSupport.java @@ -226,15 +226,8 @@ private static XMLOutputFactory createXMLOutputFactory() { private static TransformerFactory createTransformerFactory() { try { - boolean isSet = System.getProperty(PROP_TRANSFORMER_FACTORY) != null; - if (!isSet) { - System.setProperty(PROP_TRANSFORMER_FACTORY, TRANSFORMER_FACTORY_IMPL); - } - TransformerFactory factory = TransformerFactory.newInstance(); - if (!isSet) { - System.clearProperty(PROP_TRANSFORMER_FACTORY); - } - + // Always get the correct TransformerFactory + TransformerFactory factory = TransformerFactory.newInstance(TRANSFORMER_FACTORY_IMPL, null); factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); return factory; } catch (Exception e) { From de310ff37347d1c1d16fed582df52f16d483605e Mon Sep 17 00:00:00 2001 From: Robin Arnold Date: Mon, 29 Nov 2021 17:30:45 +0000 Subject: [PATCH 2/2] issue 3044 make xml input and output factory configurable Signed-off-by: Robin Arnold --- .../com/ibm/fhir/model/util/XMLSupport.java | 54 ++-- fhir-persistence-schema/README.md | 279 ++++++++++++++++++ 2 files changed, 315 insertions(+), 18 deletions(-) diff --git a/fhir-model/src/main/java/com/ibm/fhir/model/util/XMLSupport.java b/fhir-model/src/main/java/com/ibm/fhir/model/util/XMLSupport.java index 7d3cd8b900b..cc55b9ae880 100644 --- a/fhir-model/src/main/java/com/ibm/fhir/model/util/XMLSupport.java +++ b/fhir-model/src/main/java/com/ibm/fhir/model/util/XMLSupport.java @@ -26,12 +26,16 @@ public final class XMLSupport { public static final String FHIR_NS_URI = "http://hl7.org/fhir"; public static final String XHTML_NS_URI = "http://www.w3.org/1999/xhtml"; - + + // System properties used to control XML factory behavior private static final String PROP_XML_INPUT_FACTORY = "javax.xml.stream.XMLInputFactory"; + private static final String PROP_XML_INPUT_FACTORY_CONFIG = "com.ibm.fhir.xml.input.factory"; private static final String PROP_XML_OUTPUT_FACTORY = "javax.xml.stream.XMLOutputFactory"; - private static final String PROP_TRANSFORMER_FACTORY = "javax.xml.stream.XMLTransformerFactory"; + private static final String PROP_XML_OUTPUT_FACTORY_CONFIG = "com.ibm.fhir.xml.output.factory"; + private static final String XML_INPUT_FACTORY_ID = "com.ibm.fhir.xml.input.factory"; private static final String XML_INPUT_FACTORY_IMPL = "com.sun.xml.internal.stream.XMLInputFactoryImpl"; + private static final String XML_OUTPUT_FACTORY_ID = "com.ibm.fhir.xml.output.factory"; private static final String XML_OUTPUT_FACTORY_IMPL = "com.sun.xml.internal.stream.XMLOutputFactoryImpl"; private static final String TRANSFORMER_FACTORY_IMPL = "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"; @@ -190,17 +194,25 @@ public void close() throws XMLStreamException { private static XMLInputFactory createXMLInputFactory() { try { - boolean isSet = System.getProperty(PROP_XML_INPUT_FACTORY) != null; - if (!isSet) { - System.setProperty(PROP_XML_INPUT_FACTORY, XML_INPUT_FACTORY_IMPL); - } - XMLInputFactory factory = XMLInputFactory.newFactory(); - if (!isSet) { - System.clearProperty(PROP_XML_INPUT_FACTORY); + final XMLInputFactory factory; + if (System.getProperty(PROP_XML_INPUT_FACTORY_CONFIG) != null) { + // Use [java_home]/conf/stax.properties to specify the impl class + // using the value of XML_INPUT_FACTORY_ID as a key + factory = XMLInputFactory.newFactory(XML_INPUT_FACTORY_ID, null); + } else { + boolean isSet = System.getProperty(PROP_XML_INPUT_FACTORY) != null; + if (!isSet) { + System.setProperty(PROP_XML_INPUT_FACTORY, XML_INPUT_FACTORY_IMPL); + } + factory = XMLInputFactory.newFactory(); + if (!isSet) { + System.clearProperty(PROP_XML_INPUT_FACTORY); + } } - + factory.setProperty(XMLInputFactory.SUPPORT_DTD, false); factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false); + return factory; } catch (Exception e) { throw new Error(e); @@ -209,15 +221,21 @@ private static XMLInputFactory createXMLInputFactory() { private static XMLOutputFactory createXMLOutputFactory() { try { - boolean isSet = System.getProperty(PROP_XML_OUTPUT_FACTORY) != null; - if (!isSet) { - System.setProperty(PROP_XML_OUTPUT_FACTORY, XML_OUTPUT_FACTORY_IMPL); - } - XMLOutputFactory factory = XMLOutputFactory.newFactory(); - if (!isSet) { - System.clearProperty(PROP_XML_OUTPUT_FACTORY); + final XMLOutputFactory factory; + if (System.getProperty(PROP_XML_OUTPUT_FACTORY_CONFIG) != null) { + // Use [java_home]/conf/stax.properties to specify the impl class + // using the value of XML_OUTPUT_FACTORY_ID as a key + factory = XMLOutputFactory.newFactory(XML_OUTPUT_FACTORY_ID, null); + } else { + boolean isSet = System.getProperty(PROP_XML_OUTPUT_FACTORY) != null; + if (!isSet) { + System.setProperty(PROP_XML_OUTPUT_FACTORY, XML_OUTPUT_FACTORY_IMPL); + } + factory = XMLOutputFactory.newFactory(); + if (!isSet) { + System.clearProperty(PROP_XML_OUTPUT_FACTORY); + } } - return factory; } catch (Exception e) { throw new Error(e); diff --git a/fhir-persistence-schema/README.md b/fhir-persistence-schema/README.md index 274f2e6889d..205436b7560 100644 --- a/fhir-persistence-schema/README.md +++ b/fhir-persistence-schema/README.md @@ -493,3 +493,282 @@ If there is data in the DOMAINRESOURCE and RESOURCE table groups, which is unexp FHIR® is the registered trademark of HL7 and is used with the permission of HL7. + + +# Physical Data Model + +_Note_: the following description is based on the standard Derby/PostgreSQL variant of the schema. The Db2 schema uses table partitioning and row-based access control (RBAC) to support multi-tenancy. The logical design is the same, except all the primary and foreign keys are prefixed with a multi-tenant identifier `mt_id`. + +By convention, tables are named using the plural form of the data they represent. + +## Finding and Reading a Resource + +The name of the FHIR resource type is normalized and stored in the `resource_types` table. The `resource_type_id` is then used as a foreign key to reference the resource type throughout the schema. + +``` +fhirdb=> \d fhirdata.resource_types + Table "fhirdata.resource_types" + Column | Type | Collation | Nullable | Default +------------------+-----------------------+-----------+----------+--------- + resource_type_id | integer | | not null | + resource_type | character varying(64) | | not null | +Indexes: + "resource_types_pk" PRIMARY KEY, btree (resource_type_id) + "idx_unq_resource_types_rt" UNIQUE, btree (resource_type) +``` + +Resources of all types will each have a single record in the `logical_resources` table. The primary key for this table is `logical_resource_id` but the application (or business key) for the table is the tuple `{resource_type_id, logical_id}`. Per the FHIR specification, the logical id for a resource only needs to be unique for a given resource type. Thus, `Patient/abc123` and `Observation/abc123` is valid, and refer to two different resources. + + +``` +fhirdb=> \d fhirdata.logical_resources + Table "fhirdata.logical_resources" + Column | Type | Collation | Nullable | Default +---------------------+-----------------------------+-----------+----------+------------------- + logical_resource_id | bigint | | not null | + resource_type_id | integer | | not null | + logical_id | character varying(255) | | not null | + reindex_tstamp | timestamp without time zone | | not null | CURRENT_TIMESTAMP + reindex_txid | bigint | | not null | 0 + last_updated | timestamp without time zone | | | + is_deleted | character(1) | | not null | 'X'::bpchar + parameter_hash | character varying(44) | | | +Indexes: + "logical_resources_pk" PRIMARY KEY, btree (logical_resource_id) + "unq_logical_resources" UNIQUE, btree (resource_type_id, logical_id) + "idx_logical_resources_lupd" btree (last_updated) + "idx_logical_resources_rits" btree (reindex_tstamp DESC) +``` + +Rows in the `logical_resources` are locked for updated during ingestion to protect the data integrity of the data model during the ingestion procedure. Only the rows for the resources being changed are locked. The IBM FHIR Server tries hard to apply locks in a deterministic order to avoid deadlocks but this isn't always possible. Deadlocks may sometimes occur when processing transaction bundles involving overlapping data. + +Each logical resource also has a record in a resource-specific table. This table shares the same `logical_resource_id` value for its primary key as the global `logical_resources` table. This table is used as the "parent table" for the search parameter table foreign keys. Using resource-specific tables for search parameters is an optimization of the schema design and provides the following benefits: + +1. The search parameter tables and their indexes do not need to include a `resource_type_id` column, saving space; +2. Separating parameter tables by resource type improves cardinality estimation which helps the database to optimize search queries. + +``` +fhirdb=> \d fhirdata.patient_logical_resources + Table "fhirdata.patient_logical_resources" + Column | Type | Collation | Nullable | Default +---------------------+-----------------------------+-----------+----------+------------- + logical_resource_id | bigint | | not null | + logical_id | character varying(255) | | not null | + current_resource_id | bigint | | | + is_deleted | character(1) | | not null | 'X'::bpchar + last_updated | timestamp without time zone | | | + version_id | integer | | | +Indexes: + "patient_logical_resources_pk" PRIMARY KEY, btree (logical_resource_id) + "idx_patient_logical_resourcescurrent_resource_id" btree (current_resource_id) + "idx_patient_logical_resourceslogical_id" btree (logical_id) +``` + +Each version of a resource is stored in a resource-specific table with the `_resources` suffix: + +``` +fhirdb=> \d fhirdata.patient_resources + Table "fhirdata.patient_resources" + Column | Type | Collation | Nullable | Default +---------------------+-----------------------------+-----------+----------+--------- + resource_id | bigint | | not null | + logical_resource_id | bigint | | not null | + version_id | integer | | not null | + last_updated | timestamp without time zone | | not null | + is_deleted | character(1) | | not null | + data | bytea | | | +Indexes: + "patient_resources_pk" PRIMARY KEY, btree (resource_id) + "patient_resources_prf_in1" UNIQUE, btree (resource_id) + "idx_patient_resources_lupd" btree (last_updated) + "idx_patient_resourceslogical_resource_id" btree (logical_resource_id) +``` + +The first version of a resource is given a `version_id` value of 1 and each subsequent version increments this value by 1 leaving no gaps. + +To optimize certain queries, the `resource_id` for the most recent version of a resource is referenced from the `[resourceType]_logical_resources` table with the `current_resource_id` column. This is not enforced by a foreign key because the `[resourceType]_logical_resources` record is created first and already contains the intended value of the current `resource_id` which has been obtained from a sequence. This approach avoids an `UPDATE` on `[resourceType]_logical_resources` which is expensive during ingestion. + +The IBM FHIR Server uses soft-delete when processing a FHIR `DELETE` interaction. This creates a new version of the resource with a minimal resource data value and the `IS_DELETED` flag = `Y`. Soft-deletes do not delete existing database records. The Patient Erase custom operation can be used to remove all traces of a patient from a database. + +All timestamps stored in the IBM FHIR Server schema are UTC. + +For more details on how the resource payload data is stored, see the next section. + +## Scanning Resources + +The IBM FHIR Server implements the whole-system `_history` endpoint to fetch resources in the order they were ingested into the system. This endpoint is described detail in the IBM FHIR Server [Conformance Guide](https://ibm.github.io/FHIR/Conformance/#whole-system-history) guide. This service is backed by the `resource_change_log` table which records the identity of each resource version as it is ingested. The table is indexed on `change_tstamp` which reflects the UTC last-modified timestamp of the version of the resource. + +``` +fhirdb=> \d fhirdata.resource_change_log + Table "fhirdata.resource_change_log" + Column | Type | Collation | Nullable | Default +---------------------+-----------------------------+-----------+----------+--------- + resource_id | bigint | | not null | + resource_type_id | integer | | not null | + logical_resource_id | bigint | | not null | + change_tstamp | timestamp without time zone | | not null | + version_id | integer | | not null | + change_type | character(1) | | not null | +Indexes: + "resource_change_log_pk" PRIMARY KEY, btree (resource_id) + "unq_resource_change_log_ctrtri" UNIQUE, btree (change_tstamp, resource_type_id, resource_id) +``` + +As reflected in the whole-system-history REST API, the `resource_change_log` table can be scanned in two ways: + +1. Ordered by `resource_id`, following the natural order of the primary key +2. Ordered by `change_tstamp`, following the timeline of resources as they arrive + +Filtering and ordering based on `resource_id` is the simplest approach because there are no duplicates to deal with although care is still required when reading data near the current time (within the window of the maximum transaction timeout time). This is because ids are allocated before the transaction commits. It is therefore possible for records with a smaler `resource_id` value to appear after records which have already been committed. This is not a limitation of the IBM FHIR Server, but just a side-effect that is common in systems like this. + +The `change_tstamp` column can be used to scan from a point in time and a LIMIT clause can be used to restrict the size of the result set. The last returned `change_tstamp` value can be used in the next fetch to iterate forwards over all the data. Note that because two or more resources may share the same `change_tstamp` value, it's important to scan `WHERE change_tstamp >= ?` not just `WHERE change_tstamp > ?`. The reader must be prepared to handle resources which appeared in previous scan showing up again. It is reasonable to assume that only a handful of resources will share the same timestamp, but it is important to make sure that the LIMIT value exceeds this number to avoid being stuck in a continuous loop. + +The following example query will return the first 100 resource version meta-data values for resources ingested since the beginning of 2021. + +``` + SELECT c.resource_id, rt.resource_type, lr.logical_id, c.change_tstamp, c.version_id, c.change_type + FROM fhirdata.resource_change_log c, + fhirdata.logical_resources lr, + fhirdata.resource_types rt + WHERE lr.logical_resource_id = c.logical_resource_id + AND rt.resource_type_id = c.resource_type_id + AND c.change_tstamp >= '2021-01-01' + ORDER BY c.change_tstamp, c.resource_type_id, c.resource_id + LIMIT 100; + +``` + +The following example query will return the first 100 resource version meta-data values for resources ingested since the database was created: + +``` + SELECT c.resource_id, rt.resource_type, lr.logical_id, c.change_tstamp, c.version_id, c.change_type + FROM fhirdata.resource_change_log c, + fhirdata.logical_resources lr, + fhirdata.resource_types rt + WHERE lr.logical_resource_id = c.logical_resource_id + AND rt.resource_type_id = c.resource_type_id + AND c.resource_id > 0 + ORDER BY c.resource_id + LIMIT 100; + +``` + +This is an index-range-scan driven query which will be very fast. + +The resource object is stored in a resource-specific table. Joining each table to the above query would break the efficiency of the above query, so the resource payload data must be read in a separate query using the table name based on the `resource_type` column in the above query: + + +``` + SELECT data + FROM fhirdata.patient_resources + WHERE resource_id = 1657; +``` + + + +The data is always stored as a JSON string compressed using GZIP. For example: + +``` +echo -n "1f8b0800000000000000cd566d6fdb3610fe2b82beec8b298bd4bb9015589bbda209823a5d8badfd404b678b1b256a2495c408f2df7794eddaae9d365bd1615fe2883cdedd73f7dccbbdafc1a8415770bdeac12ffd2b6e0574d69ff8a2c64f9ac5398f324816094932c8216711a993889118a29cf0820161348cc39865112411be6bc172bfbcf76f401ba1ba9f4735782eb9b1affb9a5b70272c6494504ac2e89ac625cb4b1606699a4539fbcd7f98f816eeac53622cb78341f92574a0c7b713bf16377872863fde5d2b3bf3ed3bbfb1b62fa7d3dbdbdbe0360a945e4e695114d3bbc6b6f29dffecc7ed6b6fbef2ceb8d768586c5e197cb614b619e641a5daa95975b6012baa06b8b4cde69ba38ed9fabfb3297f16fcbac6e6891a6325160274e9ddb0200e4282a1204bc82a60ac48de755ee079de158aa3b401a84b8f24519ae7394b8a9885348d131a331451fd2031f61fc4c2b329027ce68281b180ced9f3cbdfef3ffa1ab4c458a876fe02e12d955e617c6eb81ce085aa45b71c63b832165a941a74572a51972ca06990c76140034aa33c8f823460518e2f2b553b12604653128d9136bde42b3c7ad3080bfec3c3646b72ccd0c6d6ccead1d656eafd6423b4494b23b331278b46e8e960a695d230c5474365070de7b0109d70d0f18eb83ba279856a26ff11d83c25c901d84b65bd9ff08b77a2f294f65e62623af559f48f3dfbe270806d50a5b02b7fe7c1295da774f4eb8226ad42f66a73c11d672f790b47eebf52063a9e24a1f7068c4d92e233d69ee2f95c68db18b8db4b948bf90f5f00645479251d43364abfab6bec62c6a57f0c52e9cfd4601bdef6168933199b88b37ac18de1553318b0d68c04183aab9dfcebd97e72371e7dd40a824d97106adb14b0400d9f0b893609afff1890793591620164055c9bad7be7508996a3e234284256147918e571e6a8f82f4cfe35f027db4b8a202cb230a4454a139652e698b86b5963497da897a7f7c2ad1d573b451ad5348b485ea5198929e7248ff39c2cd2398450c5199f4763aaed385dee5dcd8d64fbd8321ab6a05b2c17a996ab604b064797d92837bd61246461b4abdb8b5707357b01b5a8b8f45e0112aff62e87768e101de2f52c794c6072e448a34c2f2c978169b9b66becc23a7ffe27c867b303e4335509c435836ad0488d13d01f9338817dbf0a8da85d211bd3ed01c7b14a6848b26d7ff80af8ce5f1ee03bd70277896f8cf75254380c601fd9f1dde42923200ea280257ba866082bcb53967f2d4c57579707a0aeb017f54adb13d93aba3a4e1376b4aee6ba5e93538f8cfe5cf75c2bdde8dc217f8b0da208a3227e3bfad0b9c930ce5ae36ed562211c77f0c182b7428e7cb3389eff8494baad6e89f177c3d9ffc59dc95547231fd5f4b860893b777ea14de0bf1f3bdda8b11d07d0bebe375cfe099a32f60fd48dd19288bb3dcc4edfa80ef6d0254942e2acc0024d423c5ebbd0a8764d225c2a6bd707fd0560c374efc6e172be1e17b4a0296611f75b9739a8801ba8ddddb568d7f7794442ac86f89ab2328c4bb4857f4367886f47d2d1c2766aa3718b9f1d6a38eae0310db2240aa38cb12ca42c4ed2ddc890aa5b9e7e45d22228f2988e4ba6a361f6e915e4145f9680dcaec685740c95149de305f6bd1c3be80a995f1bef7ae83a902e419bb9fb5cc32d66427f62e8f60a6fe4660f08591ad1a3498cfab0f3ba163cdbacff5f548a11b938d0b69b208703e46058b8aa6b0769452fe1b923c573a52470ccda824b034e47db0eb895ad43e4fc92bc5b0e7cf968e770ed48805d94f3aa2fe36ce70774e4f5614bffbe5b4a619a7d8f3e1c3dbc7ff81bb7a7b144b60d0000" | xxd -r -p | gzip -d + +yields: + +{"resourceType":"Patient","id":"1748a37e5f5-57e8e823-d532-4e38-a92e-210404273e53","meta":{"versionId":"1","lastUpdated":"2021-11-03T14:28:20.667382Z"},"text":{"status":"generated","div":"
Generated by Synthea.Version identifier: v2.4.0-404-ge7ce2295\n . Person seed: -5368882594201645142 Population seed: 0
"},"extension":[{"extension":[{"url":"ombCategory","valueCoding":{"system":"urn:oid:2.16.840.1.113883.6.238","code":"2106-3","display":"White"}},{"url":"text","valueString":"White"}],"url":"http://hl7.org/fhir/us/core/StructureDefinition/us-core-race"},{"extension":[{"url":"ombCategory","valueCoding":{"system":"urn:oid:2.16.840.1.113883.6.238","code":"2186-5","display":"Not Hispanic or Latino"}},{"url":"text","valueString":"Not Hispanic or Latino"}],"url":"http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity"},{"url":"http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName","valueString":"Rosena550 West559"},{"url":"http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex","valueCode":"F"},{"url":"http://hl7.org/fhir/StructureDefinition/patient-birthPlace","valueAddress":{"city":"Southampton","state":"Massachusetts","country":"US"}},{"url":"http://synthetichealth.github.io/synthea/disability-adjusted-life-years","valueDecimal":6.902998038473883},{"url":"http://synthetichealth.github.io/synthea/quality-adjusted-life-years","valueDecimal":59.09700196152612}],"identifier":[{"system":"https://github.com/synthetichealth/synthea","value":"2963d173-8c67-41aa-8488-f6be0ec47ab3"},{"type":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0203","code":"MR","display":"Medical Record Number"}],"text":"Medical Record Number"},"system":"http://hospital.smarthealthit.org","value":"2963d173-8c67-41aa-8488-f6be0ec47ab3"},{"type":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0203","code":"SS","display":"Social Security Number"}],"text":"Social Security Number"},"system":"http://hl7.org/fhir/sid/us-ssn","value":"999-10-7559"},{"type":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0203","code":"DL","display":"Driver's License"}],"text":"Driver's License"},"system":"urn:oid:2.16.840.1.113883.4.3.25","value":"S99978628"},{"type":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v2-0203","code":"PPN","display":"Passport Number"}],"text":"Passport Number"},"system":"http://standardhealthrecord.org/fhir/StructureDefinition/passportNumber","value":"X70090394X"}],"name":[{"use":"official","family":"Stracke611","given":["Jackelyn13"],"prefix":["Mrs."]},{"use":"maiden","family":"Walker122","given":["Jackelyn13"],"prefix":["Mrs."]}],"telecom":[{"system":"phone","value":"555-479-4150","use":"home"}],"gender":"female","birthDate":"1916-02-21","deceasedDateTime":"1983-09-14T12:04:55-04:00","address":[{"extension":[{"extension":[{"url":"latitude","valueDecimal":41.753037227012456},{"url":"longitude","valueDecimal":-69.98418825902037}],"url":"http://hl7.org/fhir/StructureDefinition/geolocation"}],"line":["298 Reynolds Tunnel"],"city":"Brewster","state":"Massachusetts","postalCode":"02631","country":"US"}],"maritalStatus":{"coding":[{"system":"http://terminology.hl7.org/CodeSystem/v3-MaritalStatus","code":"M","display":"M"}],"text":"M"},"multipleBirthBoolean":false,"communication":[{"language":{"coding":[{"system":"urn:ietf:bcp:47","code":"en-US","display":"English"}],"text":"English"}}]} +``` + + +The resource object can be more easily retrieved from the IBM FHIR Server REST API using a versioned read (VREAD) interaction: + +``` + GET [server:port]/fhir-server/api/v4/Patient/1748a37e5f5-57e8e823-d532-4e38-a92e-210404273e53/_history/1 +``` + +Database implementations use different strategies to inline the resource payload data blob. When the size of this object exceeds the database-specific threshold, the database stores it outside of the patient_resources row which can impact how the data gets cached. To make the most of the IO capacity of the database and its underlying storage subsystem, clients should parallelize the read operation (using a thread-pool, for example). + +``` + | ---> [thread-2] read payload data + [thread-1] ----------- | ---> [thread-3] read payload data + scan-history -------> | queue | ---+ ---> [thread-4] read payload data + ^ | ----------- | ---> [thread-5] read payload data + | | resource ids | ---> [thread-6] read payload data + \-------------/ + repeat +``` + + +## Search Parameters + +``` +fhirdb=> \d fhirdata.parameter_names + Table "fhirdata.parameter_names" + Column | Type | Collation | Nullable | Default +-------------------+------------------------+-----------+----------+--------- + parameter_name_id | integer | | not null | + parameter_name | character varying(255) | | not null | +Indexes: + "parameter_names_pk" PRIMARY KEY, btree (parameter_name_id) + "idx_parameter_name_rtnm" UNIQUE, btree (parameter_name) +``` + +``` +fhirdb=> \d fhirdata.common_token_values + Table "fhirdata.common_token_values" + Column | Type | Collation | Nullable | Default +-----------------------+-------------------------+-----------+----------+------------------------------ + common_token_value_id | bigint | | not null | generated always as identity + code_system_id | integer | | not null | + token_value | character varying(1024) | | not null | +Indexes: + "common_token_values_pk" PRIMARY KEY, btree (common_token_value_id) + "idx_common_token_values_tvcp" UNIQUE, btree (token_value, code_system_id) +``` + +``` +fhirdb=> \d fhirdata.code_systems + Table "fhirdata.code_systems" + Column | Type | Collation | Nullable | Default +------------------+------------------------+-----------+----------+--------- + code_system_id | integer | | not null | + code_system_name | character varying(255) | | not null | +Indexes: + "code_systems_pk" PRIMARY KEY, btree (code_system_id) + "idx_code_system_cinm" UNIQUE, btree (code_system_name) + +``` + +``` +fhirdb=> select * from fhirdata.code_systems; + code_system_id | code_system_name +----------------+---------------------------------------------------------------------------------- + 21000 | http://unitsofmeasure.org + 21001 | Claim + 21002 | Encounter + 21003 | Observation + 21004 | Organization + 21005 | Patient + 21006 | Practitioner + 21007 | default-token-system + 21008 | http://hl7.org/fhir/claim-use + 21009 | http://hl7.org/fhir/diagnostic-report-status +``` + + +``` + Table Name | Description +------------------------------------+--------------------------------------------------------------- +[resourceType]_current_refs | Problems, Medications, Allergies, Drug allergies current lists +[resourceType]_date_values | Search date parameters +[resourceType]_latlng_values | Search location parameters +[resourceType]_number_values | Search number parameters +[resourceType]_str_values | Search string parameters +[resourceType]_quantity_values | Search quantity parameters +[resourceType]_resource_token_refs | Search token parameters +[resourceType]_security | Search security parameters +[resourceType]_profiles | Search profile parameters +[resourceType]_tags | Search tag parameters +logical_resource_profiles | Profiles claimed by the resource +logical_resource_tags | Tags associated with the resource +logical_resource_security | Security labels associated with the resource +``` + +